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::ClearLastCreated()
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
4221 //=======================================================================
4222 //function : isReverse
4223 //purpose : Return true if normal of prevNodes is not co-directied with
4224 // gp_Vec(prevNodes[iNotSame],nextNodes[iNotSame]).
4225 // iNotSame is where prevNodes and nextNodes are different.
4226 // If result is true then future volume orientation is OK
4227 //=======================================================================
4229 bool isReverse(const SMDS_MeshElement* face,
4230 const vector<const SMDS_MeshNode*>& prevNodes,
4231 const vector<const SMDS_MeshNode*>& nextNodes,
4235 SMESH_TNodeXYZ pP = prevNodes[ iNotSame ];
4236 SMESH_TNodeXYZ pN = nextNodes[ iNotSame ];
4237 gp_XYZ extrDir( pN - pP ), faceNorm;
4238 SMESH_MeshAlgos::FaceNormal( face, faceNorm, /*normalized=*/false );
4240 return faceNorm * extrDir < 0.0;
4243 //================================================================================
4245 * \brief Assure that theElemSets[0] holds elements, not nodes
4247 //================================================================================
4249 void setElemsFirst( TIDSortedElemSet theElemSets[2] )
4251 if ( !theElemSets[0].empty() &&
4252 (*theElemSets[0].begin())->GetType() == SMDSAbs_Node )
4254 std::swap( theElemSets[0], theElemSets[1] );
4256 else if ( !theElemSets[1].empty() &&
4257 (*theElemSets[1].begin())->GetType() != SMDSAbs_Node )
4259 std::swap( theElemSets[0], theElemSets[1] );
4264 //=======================================================================
4266 * \brief Create elements by sweeping an element
4267 * \param elem - element to sweep
4268 * \param newNodesItVec - nodes generated from each node of the element
4269 * \param newElems - generated elements
4270 * \param nbSteps - number of sweeping steps
4271 * \param srcElements - to append elem for each generated element
4273 //=======================================================================
4275 void SMESH_MeshEditor::sweepElement(const SMDS_MeshElement* elem,
4276 const vector<TNodeOfNodeListMapItr> & newNodesItVec,
4277 list<const SMDS_MeshElement*>& newElems,
4279 SMESH_SequenceOfElemPtr& srcElements)
4281 //MESSAGE("sweepElement " << nbSteps);
4282 SMESHDS_Mesh* aMesh = GetMeshDS();
4284 const int nbNodes = elem->NbNodes();
4285 const int nbCorners = elem->NbCornerNodes();
4286 SMDSAbs_EntityType baseType = elem->GetEntityType(); /* it can change in case of
4287 polyhedron creation !!! */
4288 // Loop on elem nodes:
4289 // find new nodes and detect same nodes indices
4290 vector < list< const SMDS_MeshNode* >::const_iterator > itNN( nbNodes );
4291 vector<const SMDS_MeshNode*> prevNod( nbNodes );
4292 vector<const SMDS_MeshNode*> nextNod( nbNodes );
4293 vector<const SMDS_MeshNode*> midlNod( nbNodes );
4295 int iNode, nbSame = 0, nbDouble = 0, iNotSameNode = 0;
4296 vector<int> sames(nbNodes);
4297 vector<bool> isSingleNode(nbNodes);
4299 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4300 TNodeOfNodeListMapItr nnIt = newNodesItVec[ iNode ];
4301 const SMDS_MeshNode* node = nnIt->first;
4302 const list< const SMDS_MeshNode* > & listNewNodes = nnIt->second;
4303 if ( listNewNodes.empty() )
4306 itNN [ iNode ] = listNewNodes.begin();
4307 prevNod[ iNode ] = node;
4308 nextNod[ iNode ] = listNewNodes.front();
4310 isSingleNode[iNode] = (listNewNodes.size()==nbSteps); /* medium node of quadratic or
4311 corner node of linear */
4312 if ( prevNod[ iNode ] != nextNod [ iNode ])
4313 nbDouble += !isSingleNode[iNode];
4315 if( iNode < nbCorners ) { // check corners only
4316 if ( prevNod[ iNode ] == nextNod [ iNode ])
4317 sames[nbSame++] = iNode;
4319 iNotSameNode = iNode;
4323 if ( nbSame == nbNodes || nbSame > 2) {
4324 MESSAGE( " Too many same nodes of element " << elem->GetID() );
4328 if ( elem->GetType() == SMDSAbs_Face && !isReverse( elem, prevNod, nextNod, iNotSameNode ))
4330 // fix nodes order to have bottom normal external
4331 if ( baseType == SMDSEntity_Polygon )
4333 std::reverse( itNN.begin(), itNN.end() );
4334 std::reverse( prevNod.begin(), prevNod.end() );
4335 std::reverse( midlNod.begin(), midlNod.end() );
4336 std::reverse( nextNod.begin(), nextNod.end() );
4337 std::reverse( isSingleNode.begin(), isSingleNode.end() );
4341 const vector<int>& ind = SMDS_MeshCell::reverseSmdsOrder( baseType );
4342 SMDS_MeshCell::applyInterlace( ind, itNN );
4343 SMDS_MeshCell::applyInterlace( ind, prevNod );
4344 SMDS_MeshCell::applyInterlace( ind, nextNod );
4345 SMDS_MeshCell::applyInterlace( ind, midlNod );
4346 SMDS_MeshCell::applyInterlace( ind, isSingleNode );
4349 sames[nbSame] = iNotSameNode;
4350 for ( int j = 0; j <= nbSame; ++j )
4351 for ( size_t i = 0; i < ind.size(); ++i )
4352 if ( ind[i] == sames[j] )
4357 iNotSameNode = sames[nbSame];
4361 else if ( elem->GetType() == SMDSAbs_Edge )
4363 // orient a new face same as adjacent one
4365 const SMDS_MeshElement* e;
4366 TIDSortedElemSet dummy;
4367 if (( e = SMESH_MeshAlgos::FindFaceInSet( nextNod[0], prevNod[0], dummy,dummy, &i1, &i2 )) ||
4368 ( e = SMESH_MeshAlgos::FindFaceInSet( prevNod[1], nextNod[1], dummy,dummy, &i1, &i2 )) ||
4369 ( e = SMESH_MeshAlgos::FindFaceInSet( prevNod[0], prevNod[1], dummy,dummy, &i1, &i2 )))
4371 // there is an adjacent face, check order of nodes in it
4372 bool sameOrder = ( Abs( i2 - i1 ) == 1 ) ? ( i2 > i1 ) : ( i2 < i1 );
4375 std::swap( itNN[0], itNN[1] );
4376 std::swap( prevNod[0], prevNod[1] );
4377 std::swap( nextNod[0], nextNod[1] );
4378 isSingleNode.swap( isSingleNode[0], isSingleNode[1] );
4380 sames[0] = 1 - sames[0];
4381 iNotSameNode = 1 - iNotSameNode;
4386 int iSameNode = 0, iBeforeSame = 0, iAfterSame = 0, iOpposSame = 0;
4388 iSameNode = sames[ nbSame-1 ];
4389 iBeforeSame = ( iSameNode + nbCorners - 1 ) % nbCorners;
4390 iAfterSame = ( iSameNode + 1 ) % nbCorners;
4391 iOpposSame = ( iSameNode - 2 < 0 ? iSameNode + 2 : iSameNode - 2 );
4394 // make new elements
4395 for (int iStep = 0; iStep < nbSteps; iStep++ )
4398 for ( iNode = 0; iNode < nbNodes; iNode++ )
4400 midlNod[ iNode ] = isSingleNode[iNode] ? 0 : *itNN[ iNode ]++;
4401 nextNod[ iNode ] = *itNN[ iNode ]++;
4404 SMDS_MeshElement* aNewElem = 0;
4405 /*if(!elem->IsPoly())*/ {
4406 switch ( baseType ) {
4408 case SMDSEntity_Node: { // sweep NODE
4409 if ( nbSame == 0 ) {
4410 if ( isSingleNode[0] )
4411 aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ] );
4413 aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ], midlNod[ 0 ] );
4419 case SMDSEntity_Edge: { // sweep EDGE
4420 if ( nbDouble == 0 )
4422 if ( nbSame == 0 ) // ---> quadrangle
4423 aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
4424 nextNod[ 1 ], nextNod[ 0 ] );
4425 else // ---> triangle
4426 aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
4427 nextNod[ iNotSameNode ] );
4429 else // ---> polygon
4431 vector<const SMDS_MeshNode*> poly_nodes;
4432 poly_nodes.push_back( prevNod[0] );
4433 poly_nodes.push_back( prevNod[1] );
4434 if ( prevNod[1] != nextNod[1] )
4436 if ( midlNod[1]) poly_nodes.push_back( midlNod[1]);
4437 poly_nodes.push_back( nextNod[1] );
4439 if ( prevNod[0] != nextNod[0] )
4441 poly_nodes.push_back( nextNod[0] );
4442 if ( midlNod[0]) poly_nodes.push_back( midlNod[0]);
4444 switch ( poly_nodes.size() ) {
4446 aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ], poly_nodes[ 2 ]);
4449 aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ],
4450 poly_nodes[ 2 ], poly_nodes[ 3 ]);
4453 aNewElem = aMesh->AddPolygonalFace (poly_nodes);
4458 case SMDSEntity_Triangle: // TRIANGLE --->
4460 if ( nbDouble > 0 ) break;
4461 if ( nbSame == 0 ) // ---> pentahedron
4462 aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
4463 nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ] );
4465 else if ( nbSame == 1 ) // ---> pyramid
4466 aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
4467 nextNod[ iAfterSame ], nextNod[ iBeforeSame ],
4468 nextNod[ iSameNode ]);
4470 else // 2 same nodes: ---> tetrahedron
4471 aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
4472 nextNod[ iNotSameNode ]);
4475 case SMDSEntity_Quad_Edge: // sweep quadratic EDGE --->
4479 if ( nbDouble+nbSame == 2 )
4481 if(nbSame==0) { // ---> quadratic quadrangle
4482 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
4483 prevNod[2], midlNod[1], nextNod[2], midlNod[0]);
4485 else { //(nbSame==1) // ---> quadratic triangle
4487 return; // medium node on axis
4489 else if(sames[0]==0)
4490 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1],
4491 prevNod[2], midlNod[1], nextNod[2] );
4493 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[0],
4494 prevNod[2], nextNod[2], midlNod[0]);
4497 else if ( nbDouble == 3 )
4499 if ( nbSame == 0 ) { // ---> bi-quadratic quadrangle
4500 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
4501 prevNod[2], midlNod[1], nextNod[2], midlNod[0], midlNod[2]);
4508 case SMDSEntity_Quadrangle: { // sweep QUADRANGLE --->
4509 if ( nbDouble > 0 ) break;
4511 if ( nbSame == 0 ) // ---> hexahedron
4512 aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ], prevNod[ 3 ],
4513 nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ], nextNod[ 3 ]);
4515 else if ( nbSame == 1 ) { // ---> pyramid + pentahedron
4516 aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
4517 nextNod[ iAfterSame ], nextNod[ iBeforeSame ],
4518 nextNod[ iSameNode ]);
4519 newElems.push_back( aNewElem );
4520 aNewElem = aMesh->AddVolume (prevNod[ iAfterSame ], prevNod[ iOpposSame ],
4521 prevNod[ iBeforeSame ], nextNod[ iAfterSame ],
4522 nextNod[ iOpposSame ], nextNod[ iBeforeSame ] );
4524 else if ( nbSame == 2 ) { // ---> pentahedron
4525 if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] )
4526 // iBeforeSame is same too
4527 aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iOpposSame ],
4528 nextNod[ iOpposSame ], prevNod[ iSameNode ],
4529 prevNod[ iAfterSame ], nextNod[ iAfterSame ]);
4531 // iAfterSame is same too
4532 aNewElem = aMesh->AddVolume (prevNod[ iSameNode ], prevNod[ iBeforeSame ],
4533 nextNod[ iBeforeSame ], prevNod[ iAfterSame ],
4534 prevNod[ iOpposSame ], nextNod[ iOpposSame ]);
4538 case SMDSEntity_Quad_Triangle: // sweep (Bi)Quadratic TRIANGLE --->
4539 case SMDSEntity_BiQuad_Triangle: /* ??? */ {
4540 if ( nbDouble+nbSame != 3 ) break;
4542 // ---> pentahedron with 15 nodes
4543 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
4544 nextNod[0], nextNod[1], nextNod[2],
4545 prevNod[3], prevNod[4], prevNod[5],
4546 nextNod[3], nextNod[4], nextNod[5],
4547 midlNod[0], midlNod[1], midlNod[2]);
4549 else if(nbSame==1) {
4550 // ---> 2d order pyramid of 13 nodes
4551 int apex = iSameNode;
4552 int i0 = ( apex + 1 ) % nbCorners;
4553 int i1 = ( apex - 1 + nbCorners ) % nbCorners;
4557 aNewElem = aMesh->AddVolume(prevNod[i1], prevNod[i0],
4558 nextNod[i0], nextNod[i1], prevNod[apex],
4559 prevNod[i01], midlNod[i0],
4560 nextNod[i01], midlNod[i1],
4561 prevNod[i1a], prevNod[i0a],
4562 nextNod[i0a], nextNod[i1a]);
4564 else if(nbSame==2) {
4565 // ---> 2d order tetrahedron of 10 nodes
4566 int n1 = iNotSameNode;
4567 int n2 = ( n1 + 1 ) % nbCorners;
4568 int n3 = ( n1 + nbCorners - 1 ) % nbCorners;
4572 aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], prevNod[n3], nextNod[n1],
4573 prevNod[n12], prevNod[n23], prevNod[n31],
4574 midlNod[n1], nextNod[n12], nextNod[n31]);
4578 case SMDSEntity_Quad_Quadrangle: { // sweep Quadratic QUADRANGLE --->
4580 if ( nbDouble != 4 ) break;
4581 // ---> hexahedron with 20 nodes
4582 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
4583 nextNod[0], nextNod[1], nextNod[2], nextNod[3],
4584 prevNod[4], prevNod[5], prevNod[6], prevNod[7],
4585 nextNod[4], nextNod[5], nextNod[6], nextNod[7],
4586 midlNod[0], midlNod[1], midlNod[2], midlNod[3]);
4588 else if(nbSame==1) {
4589 // ---> pyramid + pentahedron - can not be created since it is needed
4590 // additional middle node at the center of face
4591 INFOS( " Sweep for face " << elem->GetID() << " can not be created" );
4594 else if( nbSame == 2 ) {
4595 if ( nbDouble != 2 ) break;
4596 // ---> 2d order Pentahedron with 15 nodes
4598 if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] ) {
4599 // iBeforeSame is same too
4606 // iAfterSame is same too
4616 aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], nextNod[n2],
4617 prevNod[n4], prevNod[n5], nextNod[n5],
4618 prevNod[n12], midlNod[n2], nextNod[n12],
4619 prevNod[n45], midlNod[n5], nextNod[n45],
4620 prevNod[n14], prevNod[n25], nextNod[n25]);
4624 case SMDSEntity_BiQuad_Quadrangle: { // sweep BiQuadratic QUADRANGLE --->
4626 if( nbSame == 0 && nbDouble == 9 ) {
4627 // ---> tri-quadratic hexahedron with 27 nodes
4628 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
4629 nextNod[0], nextNod[1], nextNod[2], nextNod[3],
4630 prevNod[4], prevNod[5], prevNod[6], prevNod[7],
4631 nextNod[4], nextNod[5], nextNod[6], nextNod[7],
4632 midlNod[0], midlNod[1], midlNod[2], midlNod[3],
4633 prevNod[8], // bottom center
4634 midlNod[4], midlNod[5], midlNod[6], midlNod[7],
4635 nextNod[8], // top center
4636 midlNod[8]);// elem center
4644 case SMDSEntity_Polygon: { // sweep POLYGON
4646 if ( nbNodes == 6 && nbSame == 0 && nbDouble == 0 ) {
4647 // ---> hexagonal prism
4648 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
4649 prevNod[3], prevNod[4], prevNod[5],
4650 nextNod[0], nextNod[1], nextNod[2],
4651 nextNod[3], nextNod[4], nextNod[5]);
4655 case SMDSEntity_Ball:
4660 } // switch ( baseType )
4663 if ( !aNewElem && elem->GetType() == SMDSAbs_Face ) // try to create a polyherdal prism
4665 if ( baseType != SMDSEntity_Polygon )
4667 const std::vector<int>& ind = SMDS_MeshCell::interlacedSmdsOrder(baseType);
4668 SMDS_MeshCell::applyInterlace( ind, prevNod );
4669 SMDS_MeshCell::applyInterlace( ind, nextNod );
4670 SMDS_MeshCell::applyInterlace( ind, midlNod );
4671 SMDS_MeshCell::applyInterlace( ind, itNN );
4672 SMDS_MeshCell::applyInterlace( ind, isSingleNode );
4673 baseType = SMDSEntity_Polygon; // WARNING: change baseType !!!!
4675 vector<const SMDS_MeshNode*> polyedre_nodes (nbNodes*2 + 4*nbNodes);
4676 vector<int> quantities (nbNodes + 2);
4677 polyedre_nodes.clear();
4681 for (int inode = 0; inode < nbNodes; inode++)
4682 polyedre_nodes.push_back( prevNod[inode] );
4683 quantities.push_back( nbNodes );
4686 polyedre_nodes.push_back( nextNod[0] );
4687 for (int inode = nbNodes; inode-1; --inode )
4688 polyedre_nodes.push_back( nextNod[inode-1] );
4689 quantities.push_back( nbNodes );
4692 for (int iface = 0; iface < nbNodes; iface++)
4694 const int prevNbNodes = polyedre_nodes.size();
4695 int inextface = (iface+1) % nbNodes;
4696 polyedre_nodes.push_back( prevNod[inextface] );
4697 polyedre_nodes.push_back( prevNod[iface] );
4698 if ( prevNod[iface] != nextNod[iface] )
4700 if ( midlNod[ iface ]) polyedre_nodes.push_back( midlNod[ iface ]);
4701 polyedre_nodes.push_back( nextNod[iface] );
4703 if ( prevNod[inextface] != nextNod[inextface] )
4705 polyedre_nodes.push_back( nextNod[inextface] );
4706 if ( midlNod[ inextface ]) polyedre_nodes.push_back( midlNod[ inextface ]);
4708 const int nbFaceNodes = polyedre_nodes.size() - prevNbNodes;
4709 if ( nbFaceNodes > 2 )
4710 quantities.push_back( nbFaceNodes );
4711 else // degenerated face
4712 polyedre_nodes.resize( prevNbNodes );
4714 aNewElem = aMesh->AddPolyhedralVolume (polyedre_nodes, quantities);
4716 } // // try to create a polyherdal prism
4719 newElems.push_back( aNewElem );
4720 myLastCreatedElems.Append(aNewElem);
4721 srcElements.Append( elem );
4724 // set new prev nodes
4725 for ( iNode = 0; iNode < nbNodes; iNode++ )
4726 prevNod[ iNode ] = nextNod[ iNode ];
4731 //=======================================================================
4733 * \brief Create 1D and 2D elements around swept elements
4734 * \param mapNewNodes - source nodes and ones generated from them
4735 * \param newElemsMap - source elements and ones generated from them
4736 * \param elemNewNodesMap - nodes generated from each node of each element
4737 * \param elemSet - all swept elements
4738 * \param nbSteps - number of sweeping steps
4739 * \param srcElements - to append elem for each generated element
4741 //=======================================================================
4743 void SMESH_MeshEditor::makeWalls (TNodeOfNodeListMap & mapNewNodes,
4744 TTElemOfElemListMap & newElemsMap,
4745 TElemOfVecOfNnlmiMap & elemNewNodesMap,
4746 TIDSortedElemSet& elemSet,
4748 SMESH_SequenceOfElemPtr& srcElements)
4750 ASSERT( newElemsMap.size() == elemNewNodesMap.size() );
4751 SMESHDS_Mesh* aMesh = GetMeshDS();
4753 // Find nodes belonging to only one initial element - sweep them into edges.
4755 TNodeOfNodeListMapItr nList = mapNewNodes.begin();
4756 for ( ; nList != mapNewNodes.end(); nList++ )
4758 const SMDS_MeshNode* node =
4759 static_cast<const SMDS_MeshNode*>( nList->first );
4760 if ( newElemsMap.count( node ))
4761 continue; // node was extruded into edge
4762 SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator();
4763 int nbInitElems = 0;
4764 const SMDS_MeshElement* el = 0;
4765 SMDSAbs_ElementType highType = SMDSAbs_Edge; // count most complex elements only
4766 while ( eIt->more() && nbInitElems < 2 ) {
4768 SMDSAbs_ElementType type = el->GetType();
4769 if ( type == SMDSAbs_Volume || type < highType ) continue;
4770 if ( type > highType ) {
4774 nbInitElems += elemSet.count(el);
4776 if ( nbInitElems < 2 ) {
4777 bool NotCreateEdge = el && el->IsMediumNode(node);
4778 if(!NotCreateEdge) {
4779 vector<TNodeOfNodeListMapItr> newNodesItVec( 1, nList );
4780 list<const SMDS_MeshElement*> newEdges;
4781 sweepElement( node, newNodesItVec, newEdges, nbSteps, srcElements );
4786 // Make a ceiling for each element ie an equal element of last new nodes.
4787 // Find free links of faces - make edges and sweep them into faces.
4789 TTElemOfElemListMap::iterator itElem = newElemsMap.begin();
4790 TElemOfVecOfNnlmiMap::iterator itElemNodes = elemNewNodesMap.begin();
4791 for ( ; itElem != newElemsMap.end(); itElem++, itElemNodes++ )
4793 const SMDS_MeshElement* elem = itElem->first;
4794 vector<TNodeOfNodeListMapItr>& vecNewNodes = itElemNodes->second;
4796 if(itElem->second.size()==0) continue;
4798 const bool isQuadratic = elem->IsQuadratic();
4800 if ( elem->GetType() == SMDSAbs_Edge ) {
4801 // create a ceiling edge
4802 if ( !isQuadratic ) {
4803 if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
4804 vecNewNodes[ 1 ]->second.back())) {
4805 myLastCreatedElems.Append(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
4806 vecNewNodes[ 1 ]->second.back()));
4807 srcElements.Append( elem );
4811 if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
4812 vecNewNodes[ 1 ]->second.back(),
4813 vecNewNodes[ 2 ]->second.back())) {
4814 myLastCreatedElems.Append(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
4815 vecNewNodes[ 1 ]->second.back(),
4816 vecNewNodes[ 2 ]->second.back()));
4817 srcElements.Append( elem );
4821 if ( elem->GetType() != SMDSAbs_Face )
4824 bool hasFreeLinks = false;
4826 TIDSortedElemSet avoidSet;
4827 avoidSet.insert( elem );
4829 set<const SMDS_MeshNode*> aFaceLastNodes;
4830 int iNode, nbNodes = vecNewNodes.size();
4831 if ( !isQuadratic ) {
4832 // loop on the face nodes
4833 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4834 aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4835 // look for free links of the face
4836 int iNext = ( iNode + 1 == nbNodes ) ? 0 : iNode + 1;
4837 const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
4838 const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
4839 // check if a link n1-n2 is free
4840 if ( ! SMESH_MeshAlgos::FindFaceInSet ( n1, n2, elemSet, avoidSet )) {
4841 hasFreeLinks = true;
4842 // make a new edge and a ceiling for a new edge
4843 const SMDS_MeshElement* edge;
4844 if ( ! ( edge = aMesh->FindEdge( n1, n2 ))) {
4845 myLastCreatedElems.Append( edge = aMesh->AddEdge( n1, n2 )); // free link edge
4846 srcElements.Append( myLastCreatedElems.Last() );
4848 n1 = vecNewNodes[ iNode ]->second.back();
4849 n2 = vecNewNodes[ iNext ]->second.back();
4850 if ( !aMesh->FindEdge( n1, n2 )) {
4851 myLastCreatedElems.Append(aMesh->AddEdge( n1, n2 )); // new edge ceiling
4852 srcElements.Append( edge );
4857 else { // elem is quadratic face
4858 int nbn = nbNodes/2;
4859 for ( iNode = 0; iNode < nbn; iNode++ ) {
4860 aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4861 int iNext = ( iNode + 1 == nbn ) ? 0 : iNode + 1;
4862 const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
4863 const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
4864 const SMDS_MeshNode* n3 = vecNewNodes[ iNode+nbn ]->first;
4865 // check if a link is free
4866 if ( ! SMESH_MeshAlgos::FindFaceInSet ( n1, n2, elemSet, avoidSet ) &&
4867 ! SMESH_MeshAlgos::FindFaceInSet ( n1, n3, elemSet, avoidSet ) &&
4868 ! SMESH_MeshAlgos::FindFaceInSet ( n3, n2, elemSet, avoidSet ) ) {
4869 hasFreeLinks = true;
4870 // make an edge and a ceiling for a new edge
4872 if ( !aMesh->FindEdge( n1, n2, n3 )) {
4873 myLastCreatedElems.Append(aMesh->AddEdge( n1, n2, n3 )); // free link edge
4874 srcElements.Append( elem );
4876 n1 = vecNewNodes[ iNode ]->second.back();
4877 n2 = vecNewNodes[ iNext ]->second.back();
4878 n3 = vecNewNodes[ iNode+nbn ]->second.back();
4879 if ( !aMesh->FindEdge( n1, n2, n3 )) {
4880 myLastCreatedElems.Append(aMesh->AddEdge( n1, n2, n3 )); // ceiling edge
4881 srcElements.Append( elem );
4885 for ( iNode = nbn; iNode < nbNodes; iNode++ ) {
4886 aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4890 // sweep free links into faces
4892 if ( hasFreeLinks ) {
4893 list<const SMDS_MeshElement*> & newVolumes = itElem->second;
4894 int iVol, volNb, nbVolumesByStep = newVolumes.size() / nbSteps;
4896 set<const SMDS_MeshNode*> initNodeSet, topNodeSet, faceNodeSet;
4897 set<const SMDS_MeshNode*> initNodeSetNoCenter/*, topNodeSetNoCenter*/;
4898 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4899 initNodeSet.insert( vecNewNodes[ iNode ]->first );
4900 topNodeSet .insert( vecNewNodes[ iNode ]->second.back() );
4902 if ( isQuadratic && nbNodes % 2 ) { // node set for the case of a biquadratic
4903 initNodeSetNoCenter = initNodeSet; // swept face and a not biquadratic volume
4904 initNodeSetNoCenter.erase( vecNewNodes.back()->first );
4906 for ( volNb = 0; volNb < nbVolumesByStep; volNb++ ) {
4907 list<const SMDS_MeshElement*>::iterator v = newVolumes.begin();
4908 std::advance( v, volNb );
4909 // find indices of free faces of a volume and their source edges
4910 list< int > freeInd;
4911 list< const SMDS_MeshElement* > srcEdges; // source edges of free faces
4912 SMDS_VolumeTool vTool( *v, /*ignoreCentralNodes=*/false );
4913 int iF, nbF = vTool.NbFaces();
4914 for ( iF = 0; iF < nbF; iF ++ ) {
4915 if (vTool.IsFreeFace( iF ) &&
4916 vTool.GetFaceNodes( iF, faceNodeSet ) &&
4917 initNodeSet != faceNodeSet) // except an initial face
4919 if ( nbSteps == 1 && faceNodeSet == topNodeSet )
4921 if ( faceNodeSet == initNodeSetNoCenter )
4923 freeInd.push_back( iF );
4924 // find source edge of a free face iF
4925 vector<const SMDS_MeshNode*> commonNodes; // shared by the initial and free faces
4926 commonNodes.resize( initNodeSet.size(), NULL ); // avoid spoiling memory
4927 std::set_intersection( faceNodeSet.begin(), faceNodeSet.end(),
4928 initNodeSet.begin(), initNodeSet.end(),
4929 commonNodes.begin());
4930 if ( (*v)->IsQuadratic() )
4931 srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1],commonNodes[2]));
4933 srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1]));
4935 if ( !srcEdges.back() )
4937 cout << "SMESH_MeshEditor::makeWalls(), no source edge found for a free face #"
4938 << iF << " of volume #" << vTool.ID() << endl;
4943 if ( freeInd.empty() )
4946 // create faces for all steps;
4947 // if such a face has been already created by sweep of edge,
4948 // assure that its orientation is OK
4949 for ( int iStep = 0; iStep < nbSteps; iStep++ ) {
4950 vTool.Set( *v, /*ignoreCentralNodes=*/false );
4951 vTool.SetExternalNormal();
4952 const int nextShift = vTool.IsForward() ? +1 : -1;
4953 list< int >::iterator ind = freeInd.begin();
4954 list< const SMDS_MeshElement* >::iterator srcEdge = srcEdges.begin();
4955 for ( ; ind != freeInd.end(); ++ind, ++srcEdge ) // loop on free faces
4957 const SMDS_MeshNode** nodes = vTool.GetFaceNodes( *ind );
4958 int nbn = vTool.NbFaceNodes( *ind );
4959 const SMDS_MeshElement * f = 0;
4960 if ( nbn == 3 ) ///// triangle
4962 f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ]);
4964 nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
4966 const SMDS_MeshNode* newOrder[3] = { nodes[ 1 - nextShift ],
4968 nodes[ 1 + nextShift ] };
4970 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4972 myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
4976 else if ( nbn == 4 ) ///// quadrangle
4978 f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ], nodes[ 3 ]);
4980 nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
4982 const SMDS_MeshNode* newOrder[4] = { nodes[ 0 ], nodes[ 2-nextShift ],
4983 nodes[ 2 ], nodes[ 2+nextShift ] };
4985 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4987 myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
4988 newOrder[ 2 ], newOrder[ 3 ]));
4991 else if ( nbn == 6 && isQuadratic ) /////// quadratic triangle
4993 f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[1], nodes[3], nodes[5] );
4995 nodes[2] != f->GetNodeWrap( f->GetNodeIndex( nodes[0] ) + 2*nextShift ))
4997 const SMDS_MeshNode* newOrder[6] = { nodes[2 - 2*nextShift],
4999 nodes[2 + 2*nextShift],
5000 nodes[3 - 2*nextShift],
5002 nodes[3 + 2*nextShift]};
5004 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5006 myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ],
5014 else if ( nbn == 8 && isQuadratic ) /////// quadratic quadrangle
5016 f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[6],
5017 nodes[1], nodes[3], nodes[5], nodes[7] );
5019 nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
5021 const SMDS_MeshNode* newOrder[8] = { nodes[0],
5022 nodes[4 - 2*nextShift],
5024 nodes[4 + 2*nextShift],
5026 nodes[5 - 2*nextShift],
5028 nodes[5 + 2*nextShift] };
5030 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5032 myLastCreatedElems.Append(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
5033 newOrder[ 2 ], newOrder[ 3 ],
5034 newOrder[ 4 ], newOrder[ 5 ],
5035 newOrder[ 6 ], newOrder[ 7 ]));
5038 else if ( nbn == 9 && isQuadratic ) /////// bi-quadratic quadrangle
5040 f = aMesh->FindElement( vector<const SMDS_MeshNode*>( nodes, nodes+nbn ),
5041 SMDSAbs_Face, /*noMedium=*/false);
5043 nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
5045 const SMDS_MeshNode* newOrder[9] = { nodes[0],
5046 nodes[4 - 2*nextShift],
5048 nodes[4 + 2*nextShift],
5050 nodes[5 - 2*nextShift],
5052 nodes[5 + 2*nextShift],
5055 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5057 myLastCreatedElems.Append(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
5058 newOrder[ 2 ], newOrder[ 3 ],
5059 newOrder[ 4 ], newOrder[ 5 ],
5060 newOrder[ 6 ], newOrder[ 7 ],
5064 else //////// polygon
5066 vector<const SMDS_MeshNode*> polygon_nodes ( nodes, nodes+nbn );
5067 const SMDS_MeshFace * f = aMesh->FindFace( polygon_nodes );
5069 nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + nextShift ))
5071 if ( !vTool.IsForward() )
5072 std::reverse( polygon_nodes.begin(), polygon_nodes.end());
5074 aMesh->ChangeElementNodes( f, &polygon_nodes[0], nbn );
5076 AddElement(polygon_nodes, SMDSAbs_Face, polygon_nodes.size()>4);
5080 while ( srcElements.Length() < myLastCreatedElems.Length() )
5081 srcElements.Append( *srcEdge );
5083 } // loop on free faces
5085 // go to the next volume
5087 while ( iVol++ < nbVolumesByStep ) v++;
5090 } // loop on volumes of one step
5091 } // sweep free links into faces
5093 // Make a ceiling face with a normal external to a volume
5095 // use SMDS_VolumeTool to get a correctly ordered nodes of a ceiling face
5096 SMDS_VolumeTool lastVol( itElem->second.back(), /*ignoreCentralNodes=*/false );
5097 int iF = lastVol.GetFaceIndex( aFaceLastNodes );
5099 if ( iF < 0 && isQuadratic && nbNodes % 2 ) { // remove a central node of biquadratic
5100 aFaceLastNodes.erase( vecNewNodes.back()->second.back() );
5101 iF = lastVol.GetFaceIndex( aFaceLastNodes );
5104 lastVol.SetExternalNormal();
5105 const SMDS_MeshNode** nodes = lastVol.GetFaceNodes( iF );
5106 int nbn = lastVol.NbFaceNodes( iF );
5107 // we do not use this->AddElement() because nodes are interlaced
5108 vector<const SMDS_MeshNode*> nodeVec( nodes, nodes+nbn );
5109 if ( !hasFreeLinks ||
5110 !aMesh->FindElement( nodeVec, SMDSAbs_Face, /*noMedium=*/false) )
5113 myLastCreatedElems.Append(aMesh->AddFace( nodes[0], nodes[1], nodes[2] ));
5115 else if ( nbn == 4 )
5116 myLastCreatedElems.Append(aMesh->AddFace( nodes[0], nodes[1], nodes[2], nodes[3]));
5118 else if ( nbn == 6 && isQuadratic )
5119 myLastCreatedElems.Append(aMesh->AddFace( nodes[0], nodes[2], nodes[4],
5120 nodes[1], nodes[3], nodes[5]));
5121 else if ( nbn == 7 && isQuadratic )
5122 myLastCreatedElems.Append(aMesh->AddFace( nodes[0], nodes[2], nodes[4],
5123 nodes[1], nodes[3], nodes[5], nodes[6]));
5124 else if ( nbn == 8 && isQuadratic )
5125 myLastCreatedElems.Append(aMesh->AddFace( nodes[0], nodes[2], nodes[4], nodes[6],
5126 nodes[1], nodes[3], nodes[5], nodes[7]));
5127 else if ( nbn == 9 && isQuadratic )
5128 myLastCreatedElems.Append(aMesh->AddFace( nodes[0], nodes[2], nodes[4], nodes[6],
5129 nodes[1], nodes[3], nodes[5], nodes[7],
5132 myLastCreatedElems.Append(aMesh->AddPolygonalFace( nodeVec ));
5134 while ( srcElements.Length() < myLastCreatedElems.Length() )
5135 srcElements.Append( elem );
5138 } // loop on swept elements
5141 //=======================================================================
5142 //function : RotationSweep
5144 //=======================================================================
5146 SMESH_MeshEditor::PGroupIDs
5147 SMESH_MeshEditor::RotationSweep(TIDSortedElemSet theElemSets[2],
5148 const gp_Ax1& theAxis,
5149 const double theAngle,
5150 const int theNbSteps,
5151 const double theTol,
5152 const bool theMakeGroups,
5153 const bool theMakeWalls)
5155 myLastCreatedElems.Clear();
5156 myLastCreatedNodes.Clear();
5158 // source elements for each generated one
5159 SMESH_SequenceOfElemPtr srcElems, srcNodes;
5161 MESSAGE( "RotationSweep()");
5163 aTrsf.SetRotation( theAxis, theAngle );
5165 aTrsf2.SetRotation( theAxis, theAngle/2. );
5167 gp_Lin aLine( theAxis );
5168 double aSqTol = theTol * theTol;
5170 SMESHDS_Mesh* aMesh = GetMeshDS();
5172 TNodeOfNodeListMap mapNewNodes;
5173 TElemOfVecOfNnlmiMap mapElemNewNodes;
5174 TTElemOfElemListMap newElemsMap;
5176 const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
5177 myMesh->NbFaces(ORDER_QUADRATIC) +
5178 myMesh->NbVolumes(ORDER_QUADRATIC) );
5179 // loop on theElemSets
5180 setElemsFirst( theElemSets );
5181 TIDSortedElemSet::iterator itElem;
5182 for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
5184 TIDSortedElemSet& theElems = theElemSets[ is2ndSet ];
5185 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
5186 const SMDS_MeshElement* elem = *itElem;
5187 if ( !elem || elem->GetType() == SMDSAbs_Volume )
5189 vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5190 newNodesItVec.reserve( elem->NbNodes() );
5192 // loop on elem nodes
5193 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5194 while ( itN->more() )
5196 // check if a node has been already sweeped
5197 const SMDS_MeshNode* node = cast2Node( itN->next() );
5199 gp_XYZ aXYZ( node->X(), node->Y(), node->Z() );
5201 aXYZ.Coord( coord[0], coord[1], coord[2] );
5202 bool isOnAxis = ( aLine.SquareDistance( aXYZ ) <= aSqTol );
5204 TNodeOfNodeListMapItr nIt =
5205 mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5206 list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5207 if ( listNewNodes.empty() )
5209 // check if we are to create medium nodes between corner ones
5210 bool needMediumNodes = false;
5211 if ( isQuadraticMesh )
5213 SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
5214 while (it->more() && !needMediumNodes )
5216 const SMDS_MeshElement* invElem = it->next();
5217 if ( invElem != elem && !theElems.count( invElem )) continue;
5218 needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
5219 if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
5220 needMediumNodes = true;
5225 const SMDS_MeshNode * newNode = node;
5226 for ( int i = 0; i < theNbSteps; i++ ) {
5228 if ( needMediumNodes ) // create a medium node
5230 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
5231 newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5232 myLastCreatedNodes.Append(newNode);
5233 srcNodes.Append( node );
5234 listNewNodes.push_back( newNode );
5235 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
5238 aTrsf.Transforms( coord[0], coord[1], coord[2] );
5240 // create a corner node
5241 newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5242 myLastCreatedNodes.Append(newNode);
5243 srcNodes.Append( node );
5244 listNewNodes.push_back( newNode );
5247 listNewNodes.push_back( newNode );
5248 // if ( needMediumNodes )
5249 // listNewNodes.push_back( newNode );
5253 newNodesItVec.push_back( nIt );
5255 // make new elements
5256 sweepElement( elem, newNodesItVec, newElemsMap[elem], theNbSteps, srcElems );
5261 makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElemSets[0], theNbSteps, srcElems );
5263 PGroupIDs newGroupIDs;
5264 if ( theMakeGroups )
5265 newGroupIDs = generateGroups( srcNodes, srcElems, "rotated");
5270 //=======================================================================
5271 //function : ExtrusParam
5272 //purpose : standard construction
5273 //=======================================================================
5275 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const gp_Vec& theStep,
5276 const int theNbSteps,
5278 const double theTolerance):
5280 myFlags( theFlags ),
5281 myTolerance( theTolerance ),
5282 myElemsToUse( NULL )
5284 mySteps = new TColStd_HSequenceOfReal;
5285 const double stepSize = theStep.Magnitude();
5286 for (int i=1; i<=theNbSteps; i++ )
5287 mySteps->Append( stepSize );
5289 if (( theFlags & EXTRUSION_FLAG_SEW ) &&
5290 ( theTolerance > 0 ))
5292 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDirAndSew;
5296 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDir;
5300 //=======================================================================
5301 //function : ExtrusParam
5302 //purpose : steps are given explicitly
5303 //=======================================================================
5305 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const gp_Dir& theDir,
5306 Handle(TColStd_HSequenceOfReal) theSteps,
5308 const double theTolerance):
5310 mySteps( theSteps ),
5311 myFlags( theFlags ),
5312 myTolerance( theTolerance ),
5313 myElemsToUse( NULL )
5315 if (( theFlags & EXTRUSION_FLAG_SEW ) &&
5316 ( theTolerance > 0 ))
5318 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDirAndSew;
5322 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDir;
5326 //=======================================================================
5327 //function : ExtrusParam
5328 //purpose : for extrusion by normal
5329 //=======================================================================
5331 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const double theStepSize,
5332 const int theNbSteps,
5336 mySteps( new TColStd_HSequenceOfReal ),
5337 myFlags( theFlags ),
5339 myElemsToUse( NULL )
5341 for (int i = 0; i < theNbSteps; i++ )
5342 mySteps->Append( theStepSize );
5346 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByNormal1D;
5350 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByNormal2D;
5354 //=======================================================================
5355 //function : ExtrusParam::SetElementsToUse
5356 //purpose : stores elements to use for extrusion by normal, depending on
5357 // state of EXTRUSION_FLAG_USE_INPUT_ELEMS_ONLY flag
5358 //=======================================================================
5360 void SMESH_MeshEditor::ExtrusParam::SetElementsToUse( const TIDSortedElemSet& elems )
5362 myElemsToUse = ToUseInpElemsOnly() ? & elems : 0;
5365 //=======================================================================
5366 //function : ExtrusParam::beginStepIter
5367 //purpose : prepare iteration on steps
5368 //=======================================================================
5370 void SMESH_MeshEditor::ExtrusParam::beginStepIter( bool withMediumNodes )
5372 myWithMediumNodes = withMediumNodes;
5376 //=======================================================================
5377 //function : ExtrusParam::moreSteps
5378 //purpose : are there more steps?
5379 //=======================================================================
5381 bool SMESH_MeshEditor::ExtrusParam::moreSteps()
5383 return myNextStep <= mySteps->Length() || !myCurSteps.empty();
5385 //=======================================================================
5386 //function : ExtrusParam::nextStep
5387 //purpose : returns the next step
5388 //=======================================================================
5390 double SMESH_MeshEditor::ExtrusParam::nextStep()
5393 if ( !myCurSteps.empty() )
5395 res = myCurSteps.back();
5396 myCurSteps.pop_back();
5398 else if ( myNextStep <= mySteps->Length() )
5400 myCurSteps.push_back( mySteps->Value( myNextStep ));
5402 if ( myWithMediumNodes )
5404 myCurSteps.back() /= 2.;
5405 myCurSteps.push_back( myCurSteps.back() );
5412 //=======================================================================
5413 //function : ExtrusParam::makeNodesByDir
5414 //purpose : create nodes for standard extrusion
5415 //=======================================================================
5417 int SMESH_MeshEditor::ExtrusParam::
5418 makeNodesByDir( SMESHDS_Mesh* mesh,
5419 const SMDS_MeshNode* srcNode,
5420 std::list<const SMDS_MeshNode*> & newNodes,
5421 const bool makeMediumNodes)
5423 gp_XYZ p = SMESH_TNodeXYZ( srcNode );
5426 for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5428 p += myDir.XYZ() * nextStep();
5429 const SMDS_MeshNode * newNode = mesh->AddNode( p.X(), p.Y(), p.Z() );
5430 newNodes.push_back( newNode );
5435 //=======================================================================
5436 //function : ExtrusParam::makeNodesByDirAndSew
5437 //purpose : create nodes for standard extrusion with sewing
5438 //=======================================================================
5440 int SMESH_MeshEditor::ExtrusParam::
5441 makeNodesByDirAndSew( SMESHDS_Mesh* mesh,
5442 const SMDS_MeshNode* srcNode,
5443 std::list<const SMDS_MeshNode*> & newNodes,
5444 const bool makeMediumNodes)
5446 gp_XYZ P1 = SMESH_TNodeXYZ( srcNode );
5449 for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5451 P1 += myDir.XYZ() * nextStep();
5453 // try to search in sequence of existing nodes
5454 // if myNodes.Length()>0 we 'nave to use given sequence
5455 // else - use all nodes of mesh
5456 const SMDS_MeshNode * node = 0;
5457 if ( myNodes.Length() > 0 ) {
5459 for(i=1; i<=myNodes.Length(); i++) {
5460 gp_XYZ P2 = SMESH_TNodeXYZ( myNodes.Value(i) );
5461 if (( P1 - P2 ).SquareModulus() < myTolerance * myTolerance )
5463 node = myNodes.Value(i);
5469 SMDS_NodeIteratorPtr itn = mesh->nodesIterator();
5470 while(itn->more()) {
5471 SMESH_TNodeXYZ P2( itn->next() );
5472 if (( P1 - P2 ).SquareModulus() < myTolerance * myTolerance )
5481 node = mesh->AddNode( P1.X(), P1.Y(), P1.Z() );
5483 newNodes.push_back( node );
5490 //=======================================================================
5491 //function : ExtrusParam::makeNodesByNormal2D
5492 //purpose : create nodes for extrusion using normals of faces
5493 //=======================================================================
5495 int SMESH_MeshEditor::ExtrusParam::
5496 makeNodesByNormal2D( SMESHDS_Mesh* mesh,
5497 const SMDS_MeshNode* srcNode,
5498 std::list<const SMDS_MeshNode*> & newNodes,
5499 const bool makeMediumNodes)
5501 const bool alongAvgNorm = ( myFlags & EXTRUSION_FLAG_BY_AVG_NORMAL );
5503 gp_XYZ p = SMESH_TNodeXYZ( srcNode );
5505 // get normals to faces sharing srcNode
5506 vector< gp_XYZ > norms, baryCenters;
5507 gp_XYZ norm, avgNorm( 0,0,0 );
5508 SMDS_ElemIteratorPtr faceIt = srcNode->GetInverseElementIterator( SMDSAbs_Face );
5509 while ( faceIt->more() )
5511 const SMDS_MeshElement* face = faceIt->next();
5512 if ( myElemsToUse && !myElemsToUse->count( face ))
5514 if ( SMESH_MeshAlgos::FaceNormal( face, norm, /*normalized=*/true ))
5516 norms.push_back( norm );
5518 if ( !alongAvgNorm )
5522 for ( SMDS_ElemIteratorPtr nIt = face->nodesIterator(); nIt->more(); ++nbN )
5523 bc += SMESH_TNodeXYZ( nIt->next() );
5524 baryCenters.push_back( bc / nbN );
5529 if ( norms.empty() ) return 0;
5531 double normSize = avgNorm.Modulus();
5532 if ( normSize < std::numeric_limits<double>::min() )
5535 if ( myFlags & EXTRUSION_FLAG_BY_AVG_NORMAL ) // extrude along avgNorm
5538 return makeNodesByDir( mesh, srcNode, newNodes, makeMediumNodes );
5541 avgNorm /= normSize;
5544 for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5547 double stepSize = nextStep();
5549 if ( norms.size() > 1 )
5551 for ( size_t iF = 0; iF < norms.size(); ++iF ) // loop on faces
5553 // translate plane of a face
5554 baryCenters[ iF ] += norms[ iF ] * stepSize;
5556 // find point of intersection of the face plane located at baryCenters[ iF ]
5557 // and avgNorm located at pNew
5558 double d = -( norms[ iF ] * baryCenters[ iF ]); // d of plane equation ax+by+cz+d=0
5559 double dot = ( norms[ iF ] * avgNorm );
5560 if ( dot < std::numeric_limits<double>::min() )
5561 dot = stepSize * 1e-3;
5562 double step = -( norms[ iF ] * pNew + d ) / dot;
5563 pNew += step * avgNorm;
5568 pNew += stepSize * avgNorm;
5572 const SMDS_MeshNode * newNode = mesh->AddNode( p.X(), p.Y(), p.Z() );
5573 newNodes.push_back( newNode );
5578 //=======================================================================
5579 //function : ExtrusParam::makeNodesByNormal1D
5580 //purpose : create nodes for extrusion using normals of edges
5581 //=======================================================================
5583 int SMESH_MeshEditor::ExtrusParam::
5584 makeNodesByNormal1D( SMESHDS_Mesh* mesh,
5585 const SMDS_MeshNode* srcNode,
5586 std::list<const SMDS_MeshNode*> & newNodes,
5587 const bool makeMediumNodes)
5589 throw SALOME_Exception("Extrusion 1D by Normal not implemented");
5593 //=======================================================================
5594 //function : ExtrusionSweep
5596 //=======================================================================
5598 SMESH_MeshEditor::PGroupIDs
5599 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet theElems[2],
5600 const gp_Vec& theStep,
5601 const int theNbSteps,
5602 TTElemOfElemListMap& newElemsMap,
5604 const double theTolerance)
5606 ExtrusParam aParams( theStep, theNbSteps, theFlags, theTolerance );
5607 return ExtrusionSweep( theElems, aParams, newElemsMap );
5611 //=======================================================================
5612 //function : ExtrusionSweep
5614 //=======================================================================
5616 SMESH_MeshEditor::PGroupIDs
5617 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet theElemSets[2],
5618 ExtrusParam& theParams,
5619 TTElemOfElemListMap& newElemsMap)
5621 myLastCreatedElems.Clear();
5622 myLastCreatedNodes.Clear();
5624 // source elements for each generated one
5625 SMESH_SequenceOfElemPtr srcElems, srcNodes;
5627 SMESHDS_Mesh* aMesh = GetMeshDS();
5629 setElemsFirst( theElemSets );
5630 const int nbSteps = theParams.NbSteps();
5631 theParams.SetElementsToUse( theElemSets[0] );
5633 TNodeOfNodeListMap mapNewNodes;
5634 //TNodeOfNodeVecMap mapNewNodes;
5635 TElemOfVecOfNnlmiMap mapElemNewNodes;
5636 //TElemOfVecOfMapNodesMap mapElemNewNodes;
5638 const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
5639 myMesh->NbFaces(ORDER_QUADRATIC) +
5640 myMesh->NbVolumes(ORDER_QUADRATIC) );
5642 TIDSortedElemSet::iterator itElem;
5643 for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
5645 TIDSortedElemSet& theElems = theElemSets[ is2ndSet ];
5646 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
5648 // check element type
5649 const SMDS_MeshElement* elem = *itElem;
5650 if ( !elem || elem->GetType() == SMDSAbs_Volume )
5653 const size_t nbNodes = elem->NbNodes();
5654 vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5655 newNodesItVec.reserve( nbNodes );
5657 // loop on elem nodes
5658 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5659 while ( itN->more() )
5661 // check if a node has been already sweeped
5662 const SMDS_MeshNode* node = cast2Node( itN->next() );
5663 TNodeOfNodeListMap::iterator nIt =
5664 mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5665 list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5666 if ( listNewNodes.empty() )
5670 // check if we are to create medium nodes between corner ones
5671 bool needMediumNodes = false;
5672 if ( isQuadraticMesh )
5674 SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
5675 while (it->more() && !needMediumNodes )
5677 const SMDS_MeshElement* invElem = it->next();
5678 if ( invElem != elem && !theElems.count( invElem )) continue;
5679 needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
5680 if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
5681 needMediumNodes = true;
5684 // create nodes for all steps
5685 if ( theParams.MakeNodes( GetMeshDS(), node, listNewNodes, needMediumNodes ))
5687 list<const SMDS_MeshNode*>::iterator newNodesIt = listNewNodes.begin();
5688 for ( ; newNodesIt != listNewNodes.end(); ++newNodesIt )
5690 myLastCreatedNodes.Append( *newNodesIt );
5691 srcNodes.Append( node );
5696 break; // newNodesItVec will be shorter than nbNodes
5699 newNodesItVec.push_back( nIt );
5701 // make new elements
5702 if ( newNodesItVec.size() == nbNodes )
5703 sweepElement( elem, newNodesItVec, newElemsMap[elem], nbSteps, srcElems );
5707 if ( theParams.ToMakeBoundary() ) {
5708 makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElemSets[0], nbSteps, srcElems );
5710 PGroupIDs newGroupIDs;
5711 if ( theParams.ToMakeGroups() )
5712 newGroupIDs = generateGroups( srcNodes, srcElems, "extruded");
5717 //=======================================================================
5718 //function : ExtrusionAlongTrack
5720 //=======================================================================
5721 SMESH_MeshEditor::Extrusion_Error
5722 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet theElements[2],
5723 SMESH_subMesh* theTrack,
5724 const SMDS_MeshNode* theN1,
5725 const bool theHasAngles,
5726 list<double>& theAngles,
5727 const bool theLinearVariation,
5728 const bool theHasRefPoint,
5729 const gp_Pnt& theRefPoint,
5730 const bool theMakeGroups)
5732 MESSAGE("ExtrusionAlongTrack");
5733 myLastCreatedElems.Clear();
5734 myLastCreatedNodes.Clear();
5737 std::list<double> aPrms;
5738 TIDSortedElemSet::iterator itElem;
5741 TopoDS_Edge aTrackEdge;
5742 TopoDS_Vertex aV1, aV2;
5744 SMDS_ElemIteratorPtr aItE;
5745 SMDS_NodeIteratorPtr aItN;
5746 SMDSAbs_ElementType aTypeE;
5748 TNodeOfNodeListMap mapNewNodes;
5751 aNbE = theElements[0].size() + theElements[1].size();
5754 return EXTR_NO_ELEMENTS;
5756 // 1.1 Track Pattern
5759 SMESHDS_SubMesh* pSubMeshDS = theTrack->GetSubMeshDS();
5761 aItE = pSubMeshDS->GetElements();
5762 while ( aItE->more() ) {
5763 const SMDS_MeshElement* pE = aItE->next();
5764 aTypeE = pE->GetType();
5765 // Pattern must contain links only
5766 if ( aTypeE != SMDSAbs_Edge )
5767 return EXTR_PATH_NOT_EDGE;
5770 list<SMESH_MeshEditor_PathPoint> fullList;
5772 const TopoDS_Shape& aS = theTrack->GetSubShape();
5773 // Sub-shape for the Pattern must be an Edge or Wire
5774 if( aS.ShapeType() == TopAbs_EDGE ) {
5775 aTrackEdge = TopoDS::Edge( aS );
5776 // the Edge must not be degenerated
5777 if ( SMESH_Algo::isDegenerated( aTrackEdge ) )
5778 return EXTR_BAD_PATH_SHAPE;
5779 TopExp::Vertices( aTrackEdge, aV1, aV2 );
5780 aItN = theTrack->GetFather()->GetSubMesh( aV1 )->GetSubMeshDS()->GetNodes();
5781 const SMDS_MeshNode* aN1 = aItN->next();
5782 aItN = theTrack->GetFather()->GetSubMesh( aV2 )->GetSubMeshDS()->GetNodes();
5783 const SMDS_MeshNode* aN2 = aItN->next();
5784 // starting node must be aN1 or aN2
5785 if ( !( aN1 == theN1 || aN2 == theN1 ) )
5786 return EXTR_BAD_STARTING_NODE;
5787 aItN = pSubMeshDS->GetNodes();
5788 while ( aItN->more() ) {
5789 const SMDS_MeshNode* pNode = aItN->next();
5790 const SMDS_EdgePosition* pEPos =
5791 static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
5792 double aT = pEPos->GetUParameter();
5793 aPrms.push_back( aT );
5795 //Extrusion_Error err =
5796 MakeEdgePathPoints(aPrms, aTrackEdge, (aN1==theN1), fullList);
5797 } else if( aS.ShapeType() == TopAbs_WIRE ) {
5798 list< SMESH_subMesh* > LSM;
5799 TopTools_SequenceOfShape Edges;
5800 SMESH_subMeshIteratorPtr itSM = theTrack->getDependsOnIterator(false,true);
5801 while(itSM->more()) {
5802 SMESH_subMesh* SM = itSM->next();
5804 const TopoDS_Shape& aS = SM->GetSubShape();
5807 list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
5808 int startNid = theN1->GetID();
5809 TColStd_MapOfInteger UsedNums;
5811 int NbEdges = Edges.Length();
5813 for(; i<=NbEdges; i++) {
5815 list< SMESH_subMesh* >::iterator itLSM = LSM.begin();
5816 for(; itLSM!=LSM.end(); itLSM++) {
5818 if(UsedNums.Contains(k)) continue;
5819 aTrackEdge = TopoDS::Edge( Edges.Value(k) );
5820 SMESH_subMesh* locTrack = *itLSM;
5821 SMESHDS_SubMesh* locMeshDS = locTrack->GetSubMeshDS();
5822 TopExp::Vertices( aTrackEdge, aV1, aV2 );
5823 aItN = locTrack->GetFather()->GetSubMesh(aV1)->GetSubMeshDS()->GetNodes();
5824 const SMDS_MeshNode* aN1 = aItN->next();
5825 aItN = locTrack->GetFather()->GetSubMesh(aV2)->GetSubMeshDS()->GetNodes();
5826 const SMDS_MeshNode* aN2 = aItN->next();
5827 // starting node must be aN1 or aN2
5828 if ( !( aN1->GetID() == startNid || aN2->GetID() == startNid ) ) continue;
5829 // 2. Collect parameters on the track edge
5831 aItN = locMeshDS->GetNodes();
5832 while ( aItN->more() ) {
5833 const SMDS_MeshNode* pNode = aItN->next();
5834 const SMDS_EdgePosition* pEPos =
5835 static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
5836 double aT = pEPos->GetUParameter();
5837 aPrms.push_back( aT );
5839 list<SMESH_MeshEditor_PathPoint> LPP;
5840 //Extrusion_Error err =
5841 MakeEdgePathPoints(aPrms, aTrackEdge,(aN1->GetID()==startNid), LPP);
5842 LLPPs.push_back(LPP);
5844 // update startN for search following egde
5845 if( aN1->GetID() == startNid ) startNid = aN2->GetID();
5846 else startNid = aN1->GetID();
5850 list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
5851 list<SMESH_MeshEditor_PathPoint> firstList = *itLLPP;
5852 list<SMESH_MeshEditor_PathPoint>::iterator itPP = firstList.begin();
5853 for(; itPP!=firstList.end(); itPP++) {
5854 fullList.push_back( *itPP );
5856 SMESH_MeshEditor_PathPoint PP1 = fullList.back();
5857 fullList.pop_back();
5859 for(; itLLPP!=LLPPs.end(); itLLPP++) {
5860 list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
5861 itPP = currList.begin();
5862 SMESH_MeshEditor_PathPoint PP2 = currList.front();
5863 gp_Dir D1 = PP1.Tangent();
5864 gp_Dir D2 = PP2.Tangent();
5865 gp_Dir Dnew( gp_Vec( (D1.X()+D2.X())/2, (D1.Y()+D2.Y())/2,
5866 (D1.Z()+D2.Z())/2 ) );
5867 PP1.SetTangent(Dnew);
5868 fullList.push_back(PP1);
5870 for(; itPP!=firstList.end(); itPP++) {
5871 fullList.push_back( *itPP );
5873 PP1 = fullList.back();
5874 fullList.pop_back();
5876 // if wire not closed
5877 fullList.push_back(PP1);
5881 return EXTR_BAD_PATH_SHAPE;
5884 return MakeExtrElements(theElements, fullList, theHasAngles, theAngles, theLinearVariation,
5885 theHasRefPoint, theRefPoint, theMakeGroups);
5889 //=======================================================================
5890 //function : ExtrusionAlongTrack
5892 //=======================================================================
5893 SMESH_MeshEditor::Extrusion_Error
5894 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet theElements[2],
5895 SMESH_Mesh* theTrack,
5896 const SMDS_MeshNode* theN1,
5897 const bool theHasAngles,
5898 list<double>& theAngles,
5899 const bool theLinearVariation,
5900 const bool theHasRefPoint,
5901 const gp_Pnt& theRefPoint,
5902 const bool theMakeGroups)
5904 myLastCreatedElems.Clear();
5905 myLastCreatedNodes.Clear();
5908 std::list<double> aPrms;
5909 TIDSortedElemSet::iterator itElem;
5912 TopoDS_Edge aTrackEdge;
5913 TopoDS_Vertex aV1, aV2;
5915 SMDS_ElemIteratorPtr aItE;
5916 SMDS_NodeIteratorPtr aItN;
5917 SMDSAbs_ElementType aTypeE;
5919 TNodeOfNodeListMap mapNewNodes;
5922 aNbE = theElements[0].size() + theElements[1].size();
5925 return EXTR_NO_ELEMENTS;
5927 // 1.1 Track Pattern
5930 SMESHDS_Mesh* pMeshDS = theTrack->GetMeshDS();
5932 aItE = pMeshDS->elementsIterator();
5933 while ( aItE->more() ) {
5934 const SMDS_MeshElement* pE = aItE->next();
5935 aTypeE = pE->GetType();
5936 // Pattern must contain links only
5937 if ( aTypeE != SMDSAbs_Edge )
5938 return EXTR_PATH_NOT_EDGE;
5941 list<SMESH_MeshEditor_PathPoint> fullList;
5943 const TopoDS_Shape& aS = theTrack->GetShapeToMesh();
5945 if ( !theTrack->HasShapeToMesh() ) {
5946 //Mesh without shape
5947 const SMDS_MeshNode* currentNode = NULL;
5948 const SMDS_MeshNode* prevNode = theN1;
5949 std::vector<const SMDS_MeshNode*> aNodesList;
5950 aNodesList.push_back(theN1);
5951 int nbEdges = 0, conn=0;
5952 const SMDS_MeshElement* prevElem = NULL;
5953 const SMDS_MeshElement* currentElem = NULL;
5954 int totalNbEdges = theTrack->NbEdges();
5955 SMDS_ElemIteratorPtr nIt;
5958 if( !theTrack->GetMeshDS()->Contains(theN1) ) {
5959 return EXTR_BAD_STARTING_NODE;
5962 conn = nbEdgeConnectivity(theN1);
5964 return EXTR_PATH_NOT_EDGE;
5966 aItE = theN1->GetInverseElementIterator();
5967 prevElem = aItE->next();
5968 currentElem = prevElem;
5970 if(totalNbEdges == 1 ) {
5971 nIt = currentElem->nodesIterator();
5972 currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
5973 if(currentNode == prevNode)
5974 currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
5975 aNodesList.push_back(currentNode);
5977 nIt = currentElem->nodesIterator();
5978 while( nIt->more() ) {
5979 currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
5980 if(currentNode == prevNode)
5981 currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
5982 aNodesList.push_back(currentNode);
5984 //case of the closed mesh
5985 if(currentNode == theN1) {
5990 conn = nbEdgeConnectivity(currentNode);
5992 return EXTR_PATH_NOT_EDGE;
5993 }else if( conn == 1 && nbEdges > 0 ) {
5998 prevNode = currentNode;
5999 aItE = currentNode->GetInverseElementIterator();
6000 currentElem = aItE->next();
6001 if( currentElem == prevElem)
6002 currentElem = aItE->next();
6003 nIt = currentElem->nodesIterator();
6004 prevElem = currentElem;
6010 if(nbEdges != totalNbEdges)
6011 return EXTR_PATH_NOT_EDGE;
6013 TopTools_SequenceOfShape Edges;
6014 double x1,x2,y1,y2,z1,z2;
6015 list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
6016 int startNid = theN1->GetID();
6017 for(int i = 1; i < aNodesList.size(); i++) {
6018 x1 = aNodesList[i-1]->X();x2 = aNodesList[i]->X();
6019 y1 = aNodesList[i-1]->Y();y2 = aNodesList[i]->Y();
6020 z1 = aNodesList[i-1]->Z();z2 = aNodesList[i]->Z();
6021 TopoDS_Edge e = BRepBuilderAPI_MakeEdge(gp_Pnt(x1,y1,z1),gp_Pnt(x2,y2,z2));
6022 list<SMESH_MeshEditor_PathPoint> LPP;
6024 MakeEdgePathPoints(aPrms, e, (aNodesList[i-1]->GetID()==startNid), LPP);
6025 LLPPs.push_back(LPP);
6026 if( aNodesList[i-1]->GetID() == startNid ) startNid = aNodesList[i]->GetID();
6027 else startNid = aNodesList[i-1]->GetID();
6031 list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
6032 list<SMESH_MeshEditor_PathPoint> firstList = *itLLPP;
6033 list<SMESH_MeshEditor_PathPoint>::iterator itPP = firstList.begin();
6034 for(; itPP!=firstList.end(); itPP++) {
6035 fullList.push_back( *itPP );
6038 SMESH_MeshEditor_PathPoint PP1 = fullList.back();
6039 SMESH_MeshEditor_PathPoint PP2;
6040 fullList.pop_back();
6042 for(; itLLPP!=LLPPs.end(); itLLPP++) {
6043 list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
6044 itPP = currList.begin();
6045 PP2 = currList.front();
6046 gp_Dir D1 = PP1.Tangent();
6047 gp_Dir D2 = PP2.Tangent();
6048 gp_Dir Dnew( gp_Vec( (D1.X()+D2.X())/2, (D1.Y()+D2.Y())/2,
6049 (D1.Z()+D2.Z())/2 ) );
6050 PP1.SetTangent(Dnew);
6051 fullList.push_back(PP1);
6053 for(; itPP!=currList.end(); itPP++) {
6054 fullList.push_back( *itPP );
6056 PP1 = fullList.back();
6057 fullList.pop_back();
6059 fullList.push_back(PP1);
6061 } // Sub-shape for the Pattern must be an Edge or Wire
6062 else if( aS.ShapeType() == TopAbs_EDGE ) {
6063 aTrackEdge = TopoDS::Edge( aS );
6064 // the Edge must not be degenerated
6065 if ( SMESH_Algo::isDegenerated( aTrackEdge ) )
6066 return EXTR_BAD_PATH_SHAPE;
6067 TopExp::Vertices( aTrackEdge, aV1, aV2 );
6068 const SMDS_MeshNode* aN1 = SMESH_Algo::VertexNode( aV1, pMeshDS );
6069 const SMDS_MeshNode* aN2 = SMESH_Algo::VertexNode( aV2, pMeshDS );
6070 // starting node must be aN1 or aN2
6071 if ( !( aN1 == theN1 || aN2 == theN1 ) )
6072 return EXTR_BAD_STARTING_NODE;
6073 aItN = pMeshDS->nodesIterator();
6074 while ( aItN->more() ) {
6075 const SMDS_MeshNode* pNode = aItN->next();
6076 if( pNode==aN1 || pNode==aN2 ) continue;
6077 const SMDS_EdgePosition* pEPos =
6078 static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
6079 double aT = pEPos->GetUParameter();
6080 aPrms.push_back( aT );
6082 //Extrusion_Error err =
6083 MakeEdgePathPoints(aPrms, aTrackEdge, (aN1==theN1), fullList);
6085 else if( aS.ShapeType() == TopAbs_WIRE ) {
6086 list< SMESH_subMesh* > LSM;
6087 TopTools_SequenceOfShape Edges;
6088 TopExp_Explorer eExp(aS, TopAbs_EDGE);
6089 for(; eExp.More(); eExp.Next()) {
6090 TopoDS_Edge E = TopoDS::Edge( eExp.Current() );
6091 if( SMESH_Algo::isDegenerated(E) ) continue;
6092 SMESH_subMesh* SM = theTrack->GetSubMesh(E);
6098 list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
6099 TopoDS_Vertex aVprev;
6100 TColStd_MapOfInteger UsedNums;
6101 int NbEdges = Edges.Length();
6103 for(; i<=NbEdges; i++) {
6105 list< SMESH_subMesh* >::iterator itLSM = LSM.begin();
6106 for(; itLSM!=LSM.end(); itLSM++) {
6108 if(UsedNums.Contains(k)) continue;
6109 aTrackEdge = TopoDS::Edge( Edges.Value(k) );
6110 SMESH_subMesh* locTrack = *itLSM;
6111 SMESHDS_SubMesh* locMeshDS = locTrack->GetSubMeshDS();
6112 TopExp::Vertices( aTrackEdge, aV1, aV2 );
6113 bool aN1isOK = false, aN2isOK = false;
6114 if ( aVprev.IsNull() ) {
6115 // if previous vertex is not yet defined, it means that we in the beginning of wire
6116 // and we have to find initial vertex corresponding to starting node theN1
6117 const SMDS_MeshNode* aN1 = SMESH_Algo::VertexNode( aV1, pMeshDS );
6118 const SMDS_MeshNode* aN2 = SMESH_Algo::VertexNode( aV2, pMeshDS );
6119 // starting node must be aN1 or aN2
6120 aN1isOK = ( aN1 && aN1 == theN1 );
6121 aN2isOK = ( aN2 && aN2 == theN1 );
6124 // we have specified ending vertex of the previous edge on the previous iteration
6125 // and we have just to check that it corresponds to any vertex in current segment
6126 aN1isOK = aVprev.IsSame( aV1 );
6127 aN2isOK = aVprev.IsSame( aV2 );
6129 if ( !aN1isOK && !aN2isOK ) continue;
6130 // 2. Collect parameters on the track edge
6132 aItN = locMeshDS->GetNodes();
6133 while ( aItN->more() ) {
6134 const SMDS_MeshNode* pNode = aItN->next();
6135 const SMDS_EdgePosition* pEPos =
6136 static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
6137 double aT = pEPos->GetUParameter();
6138 aPrms.push_back( aT );
6140 list<SMESH_MeshEditor_PathPoint> LPP;
6141 //Extrusion_Error err =
6142 MakeEdgePathPoints(aPrms, aTrackEdge, aN1isOK, LPP);
6143 LLPPs.push_back(LPP);
6145 // update startN for search following egde
6146 if ( aN1isOK ) aVprev = aV2;
6151 list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
6152 list<SMESH_MeshEditor_PathPoint>& firstList = *itLLPP;
6153 fullList.splice( fullList.end(), firstList );
6155 SMESH_MeshEditor_PathPoint PP1 = fullList.back();
6156 fullList.pop_back();
6158 for(; itLLPP!=LLPPs.end(); itLLPP++) {
6159 list<SMESH_MeshEditor_PathPoint>& currList = *itLLPP;
6160 SMESH_MeshEditor_PathPoint PP2 = currList.front();
6161 gp_Dir D1 = PP1.Tangent();
6162 gp_Dir D2 = PP2.Tangent();
6163 gp_Dir Dnew( ( D1.XYZ() + D2.XYZ() ) / 2 );
6164 PP1.SetTangent(Dnew);
6165 fullList.push_back(PP1);
6166 fullList.splice( fullList.end(), currList, ++currList.begin(), currList.end() );
6167 PP1 = fullList.back();
6168 fullList.pop_back();
6170 // if wire not closed
6171 fullList.push_back(PP1);
6175 return EXTR_BAD_PATH_SHAPE;
6178 return MakeExtrElements(theElements, fullList, theHasAngles, theAngles, theLinearVariation,
6179 theHasRefPoint, theRefPoint, theMakeGroups);
6183 //=======================================================================
6184 //function : MakeEdgePathPoints
6185 //purpose : auxilary for ExtrusionAlongTrack
6186 //=======================================================================
6187 SMESH_MeshEditor::Extrusion_Error
6188 SMESH_MeshEditor::MakeEdgePathPoints(std::list<double>& aPrms,
6189 const TopoDS_Edge& aTrackEdge,
6191 list<SMESH_MeshEditor_PathPoint>& LPP)
6193 Standard_Real aTx1, aTx2, aL2, aTolVec, aTolVec2;
6195 aTolVec2=aTolVec*aTolVec;
6197 TopoDS_Vertex aV1, aV2;
6198 TopExp::Vertices( aTrackEdge, aV1, aV2 );
6199 aT1=BRep_Tool::Parameter( aV1, aTrackEdge );
6200 aT2=BRep_Tool::Parameter( aV2, aTrackEdge );
6201 // 2. Collect parameters on the track edge
6202 aPrms.push_front( aT1 );
6203 aPrms.push_back( aT2 );
6206 if( FirstIsStart ) {
6217 SMESH_MeshEditor_PathPoint aPP;
6218 Handle(Geom_Curve) aC3D = BRep_Tool::Curve( aTrackEdge, aTx1, aTx2 );
6219 std::list<double>::iterator aItD = aPrms.begin();
6220 for(; aItD != aPrms.end(); ++aItD) {
6224 aC3D->D1( aT, aP3D, aVec );
6225 aL2 = aVec.SquareMagnitude();
6226 if ( aL2 < aTolVec2 )
6227 return EXTR_CANT_GET_TANGENT;
6228 gp_Dir aTgt( aVec );
6230 aPP.SetTangent( aTgt );
6231 aPP.SetParameter( aT );
6238 //=======================================================================
6239 //function : MakeExtrElements
6240 //purpose : auxilary for ExtrusionAlongTrack
6241 //=======================================================================
6242 SMESH_MeshEditor::Extrusion_Error
6243 SMESH_MeshEditor::MakeExtrElements(TIDSortedElemSet theElemSets[2],
6244 list<SMESH_MeshEditor_PathPoint>& fullList,
6245 const bool theHasAngles,
6246 list<double>& theAngles,
6247 const bool theLinearVariation,
6248 const bool theHasRefPoint,
6249 const gp_Pnt& theRefPoint,
6250 const bool theMakeGroups)
6252 const int aNbTP = fullList.size();
6254 if( theHasAngles && !theAngles.empty() && theLinearVariation )
6255 LinearAngleVariation(aNbTP-1, theAngles);
6256 // fill vector of path points with angles
6257 vector<SMESH_MeshEditor_PathPoint> aPPs;
6258 list<SMESH_MeshEditor_PathPoint>::iterator itPP = fullList.begin();
6259 list<double>::iterator itAngles = theAngles.begin();
6260 aPPs.push_back( *itPP++ );
6261 for( ; itPP != fullList.end(); itPP++) {
6262 aPPs.push_back( *itPP );
6263 if ( theHasAngles && itAngles != theAngles.end() )
6264 aPPs.back().SetAngle( *itAngles++ );
6267 TNodeOfNodeListMap mapNewNodes;
6268 TElemOfVecOfNnlmiMap mapElemNewNodes;
6269 TTElemOfElemListMap newElemsMap;
6270 TIDSortedElemSet::iterator itElem;
6271 // source elements for each generated one
6272 SMESH_SequenceOfElemPtr srcElems, srcNodes;
6274 // 3. Center of rotation aV0
6275 gp_Pnt aV0 = theRefPoint;
6276 if ( !theHasRefPoint )
6278 gp_XYZ aGC( 0.,0.,0. );
6279 TIDSortedElemSet newNodes;
6281 for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
6283 TIDSortedElemSet& theElements = theElemSets[ is2ndSet ];
6284 itElem = theElements.begin();
6285 for ( ; itElem != theElements.end(); itElem++ ) {
6286 const SMDS_MeshElement* elem = *itElem;
6288 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6289 while ( itN->more() ) {
6290 const SMDS_MeshElement* node = itN->next();
6291 if ( newNodes.insert( node ).second )
6292 aGC += SMESH_TNodeXYZ( node );
6296 aGC /= newNodes.size();
6298 } // if (!theHasRefPoint) {
6300 // 4. Processing the elements
6301 SMESHDS_Mesh* aMesh = GetMeshDS();
6303 setElemsFirst( theElemSets );
6304 for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
6306 TIDSortedElemSet& theElements = theElemSets[ is2ndSet ];
6307 for ( itElem = theElements.begin(); itElem != theElements.end(); itElem++ ) {
6308 // check element type
6309 const SMDS_MeshElement* elem = *itElem;
6312 // SMDSAbs_ElementType aTypeE = elem->GetType();
6313 // if ( aTypeE != SMDSAbs_Face && aTypeE != SMDSAbs_Edge )
6316 vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
6317 newNodesItVec.reserve( elem->NbNodes() );
6319 // loop on elem nodes
6321 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6322 while ( itN->more() )
6325 // check if a node has been already processed
6326 const SMDS_MeshNode* node =
6327 static_cast<const SMDS_MeshNode*>( itN->next() );
6328 TNodeOfNodeListMap::iterator nIt = mapNewNodes.find( node );
6329 if ( nIt == mapNewNodes.end() ) {
6330 nIt = mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
6331 list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
6334 Standard_Real aAngle1x, aAngleT1T0, aTolAng;
6335 gp_Pnt aP0x, aP1x, aPN0, aPN1, aV0x, aV1x;
6336 gp_Ax1 anAx1, anAxT1T0;
6337 gp_Dir aDT1x, aDT0x, aDT1T0;
6342 aPN0 = SMESH_TNodeXYZ( node );
6344 const SMESH_MeshEditor_PathPoint& aPP0 = aPPs[0];
6346 aDT0x= aPP0.Tangent();
6347 //cout<<"j = 0 PP: Pnt("<<aP0x.X()<<","<<aP0x.Y()<<","<<aP0x.Z()<<")"<<endl;
6349 for ( int j = 1; j < aNbTP; ++j ) {
6350 const SMESH_MeshEditor_PathPoint& aPP1 = aPPs[j];
6352 aDT1x = aPP1.Tangent();
6353 aAngle1x = aPP1.Angle();
6355 gp_Trsf aTrsf, aTrsfRot, aTrsfRotT1T0;
6357 gp_Vec aV01x( aP0x, aP1x );
6358 aTrsf.SetTranslation( aV01x );
6361 aV1x = aV0x.Transformed( aTrsf );
6362 aPN1 = aPN0.Transformed( aTrsf );
6364 // rotation 1 [ T1,T0 ]
6365 aAngleT1T0=-aDT1x.Angle( aDT0x );
6366 if (fabs(aAngleT1T0) > aTolAng) {
6368 anAxT1T0.SetLocation( aV1x );
6369 anAxT1T0.SetDirection( aDT1T0 );
6370 aTrsfRotT1T0.SetRotation( anAxT1T0, aAngleT1T0 );
6372 aPN1 = aPN1.Transformed( aTrsfRotT1T0 );
6376 if ( theHasAngles ) {
6377 anAx1.SetLocation( aV1x );
6378 anAx1.SetDirection( aDT1x );
6379 aTrsfRot.SetRotation( anAx1, aAngle1x );
6381 aPN1 = aPN1.Transformed( aTrsfRot );
6385 //MESSAGE("elem->IsQuadratic " << elem->IsQuadratic() << " " << elem->IsMediumNode(node));
6386 if( elem->IsQuadratic() && !elem->IsMediumNode(node) ) {
6387 // create additional node
6388 double x = ( aPN1.X() + aPN0.X() )/2.;
6389 double y = ( aPN1.Y() + aPN0.Y() )/2.;
6390 double z = ( aPN1.Z() + aPN0.Z() )/2.;
6391 const SMDS_MeshNode* newNode = aMesh->AddNode(x,y,z);
6392 myLastCreatedNodes.Append(newNode);
6393 srcNodes.Append( node );
6394 listNewNodes.push_back( newNode );
6396 const SMDS_MeshNode* newNode = aMesh->AddNode( aPN1.X(), aPN1.Y(), aPN1.Z() );
6397 myLastCreatedNodes.Append(newNode);
6398 srcNodes.Append( node );
6399 listNewNodes.push_back( newNode );
6409 // if current elem is quadratic and current node is not medium
6410 // we have to check - may be it is needed to insert additional nodes
6411 if( elem->IsQuadratic() && !elem->IsMediumNode(node) ) {
6412 list< const SMDS_MeshNode* > & listNewNodes = nIt->second;
6413 if(listNewNodes.size()==aNbTP-1) {
6414 vector<const SMDS_MeshNode*> aNodes(2*(aNbTP-1));
6415 gp_XYZ P(node->X(), node->Y(), node->Z());
6416 list< const SMDS_MeshNode* >::iterator it = listNewNodes.begin();
6418 for(i=0; i<aNbTP-1; i++) {
6419 const SMDS_MeshNode* N = *it;
6420 double x = ( N->X() + P.X() )/2.;
6421 double y = ( N->Y() + P.Y() )/2.;
6422 double z = ( N->Z() + P.Z() )/2.;
6423 const SMDS_MeshNode* newN = aMesh->AddNode(x,y,z);
6424 srcNodes.Append( node );
6425 myLastCreatedNodes.Append(newN);
6428 P = gp_XYZ(N->X(),N->Y(),N->Z());
6430 listNewNodes.clear();
6431 for(i=0; i<2*(aNbTP-1); i++) {
6432 listNewNodes.push_back(aNodes[i]);
6438 newNodesItVec.push_back( nIt );
6440 // make new elements
6441 //sweepElement( aMesh, elem, newNodesItVec, newElemsMap[elem],
6442 // newNodesItVec[0]->second.size(), myLastCreatedElems );
6443 sweepElement( elem, newNodesItVec, newElemsMap[elem], aNbTP-1, srcElems );
6447 makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElemSets[0], aNbTP-1, srcElems );
6449 if ( theMakeGroups )
6450 generateGroups( srcNodes, srcElems, "extruded");
6456 //=======================================================================
6457 //function : LinearAngleVariation
6458 //purpose : auxilary for ExtrusionAlongTrack
6459 //=======================================================================
6460 void SMESH_MeshEditor::LinearAngleVariation(const int nbSteps,
6461 list<double>& Angles)
6463 int nbAngles = Angles.size();
6464 if( nbSteps > nbAngles ) {
6465 vector<double> theAngles(nbAngles);
6466 list<double>::iterator it = Angles.begin();
6468 for(; it!=Angles.end(); it++) {
6470 theAngles[i] = (*it);
6473 double rAn2St = double( nbAngles ) / double( nbSteps );
6474 double angPrev = 0, angle;
6475 for ( int iSt = 0; iSt < nbSteps; ++iSt ) {
6476 double angCur = rAn2St * ( iSt+1 );
6477 double angCurFloor = floor( angCur );
6478 double angPrevFloor = floor( angPrev );
6479 if ( angPrevFloor == angCurFloor )
6480 angle = rAn2St * theAngles[ int( angCurFloor ) ];
6482 int iP = int( angPrevFloor );
6483 double angPrevCeil = ceil(angPrev);
6484 angle = ( angPrevCeil - angPrev ) * theAngles[ iP ];
6486 int iC = int( angCurFloor );
6487 if ( iC < nbAngles )
6488 angle += ( angCur - angCurFloor ) * theAngles[ iC ];
6490 iP = int( angPrevCeil );
6492 angle += theAngles[ iC ];
6494 res.push_back(angle);
6499 for(; it!=res.end(); it++)
6500 Angles.push_back( *it );
6505 //================================================================================
6507 * \brief Move or copy theElements applying theTrsf to their nodes
6508 * \param theElems - elements to transform, if theElems is empty then apply to all mesh nodes
6509 * \param theTrsf - transformation to apply
6510 * \param theCopy - if true, create translated copies of theElems
6511 * \param theMakeGroups - if true and theCopy, create translated groups
6512 * \param theTargetMesh - mesh to copy translated elements into
6513 * \return SMESH_MeshEditor::PGroupIDs - list of ids of created groups
6515 //================================================================================
6517 SMESH_MeshEditor::PGroupIDs
6518 SMESH_MeshEditor::Transform (TIDSortedElemSet & theElems,
6519 const gp_Trsf& theTrsf,
6521 const bool theMakeGroups,
6522 SMESH_Mesh* theTargetMesh)
6524 myLastCreatedElems.Clear();
6525 myLastCreatedNodes.Clear();
6527 bool needReverse = false;
6528 string groupPostfix;
6529 switch ( theTrsf.Form() ) {
6531 MESSAGE("gp_PntMirror");
6533 groupPostfix = "mirrored";
6536 MESSAGE("gp_Ax1Mirror");
6537 groupPostfix = "mirrored";
6540 MESSAGE("gp_Ax2Mirror");
6542 groupPostfix = "mirrored";
6545 MESSAGE("gp_Rotation");
6546 groupPostfix = "rotated";
6548 case gp_Translation:
6549 MESSAGE("gp_Translation");
6550 groupPostfix = "translated";
6553 MESSAGE("gp_Scale");
6554 groupPostfix = "scaled";
6556 case gp_CompoundTrsf: // different scale by axis
6557 MESSAGE("gp_CompoundTrsf");
6558 groupPostfix = "scaled";
6562 needReverse = false;
6563 groupPostfix = "transformed";
6566 SMESH_MeshEditor targetMeshEditor( theTargetMesh );
6567 SMESHDS_Mesh* aTgtMesh = theTargetMesh ? theTargetMesh->GetMeshDS() : 0;
6568 SMESHDS_Mesh* aMesh = GetMeshDS();
6571 // map old node to new one
6572 TNodeNodeMap nodeMap;
6574 // elements sharing moved nodes; those of them which have all
6575 // nodes mirrored but are not in theElems are to be reversed
6576 TIDSortedElemSet inverseElemSet;
6578 // source elements for each generated one
6579 SMESH_SequenceOfElemPtr srcElems, srcNodes;
6581 // issue 021015: EDF 1578 SMESH: Free nodes are removed when translating a mesh
6582 TIDSortedElemSet orphanNode;
6584 if ( theElems.empty() ) // transform the whole mesh
6587 SMDS_ElemIteratorPtr eIt = aMesh->elementsIterator();
6588 while ( eIt->more() ) theElems.insert( eIt->next() );
6590 SMDS_NodeIteratorPtr nIt = aMesh->nodesIterator();
6591 while ( nIt->more() )
6593 const SMDS_MeshNode* node = nIt->next();
6594 if ( node->NbInverseElements() == 0)
6595 orphanNode.insert( node );
6599 // loop on elements to transform nodes : first orphan nodes then elems
6600 TIDSortedElemSet::iterator itElem;
6601 TIDSortedElemSet *elements[] = {&orphanNode, &theElems };
6602 for (int i=0; i<2; i++)
6603 for ( itElem = elements[i]->begin(); itElem != elements[i]->end(); itElem++ ) {
6604 const SMDS_MeshElement* elem = *itElem;
6608 // loop on elem nodes
6609 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6610 while ( itN->more() ) {
6612 const SMDS_MeshNode* node = cast2Node( itN->next() );
6613 // check if a node has been already transformed
6614 pair<TNodeNodeMap::iterator,bool> n2n_isnew =
6615 nodeMap.insert( make_pair ( node, node ));
6616 if ( !n2n_isnew.second )
6620 coord[0] = node->X();
6621 coord[1] = node->Y();
6622 coord[2] = node->Z();
6623 theTrsf.Transforms( coord[0], coord[1], coord[2] );
6624 if ( theTargetMesh ) {
6625 const SMDS_MeshNode * newNode = aTgtMesh->AddNode( coord[0], coord[1], coord[2] );
6626 n2n_isnew.first->second = newNode;
6627 myLastCreatedNodes.Append(newNode);
6628 srcNodes.Append( node );
6630 else if ( theCopy ) {
6631 const SMDS_MeshNode * newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
6632 n2n_isnew.first->second = newNode;
6633 myLastCreatedNodes.Append(newNode);
6634 srcNodes.Append( node );
6637 aMesh->MoveNode( node, coord[0], coord[1], coord[2] );
6638 // node position on shape becomes invalid
6639 const_cast< SMDS_MeshNode* > ( node )->SetPosition
6640 ( SMDS_SpacePosition::originSpacePosition() );
6643 // keep inverse elements
6644 if ( !theCopy && !theTargetMesh && needReverse ) {
6645 SMDS_ElemIteratorPtr invElemIt = node->GetInverseElementIterator();
6646 while ( invElemIt->more() ) {
6647 const SMDS_MeshElement* iel = invElemIt->next();
6648 inverseElemSet.insert( iel );
6654 // either create new elements or reverse mirrored ones
6655 if ( !theCopy && !needReverse && !theTargetMesh )
6658 TIDSortedElemSet::iterator invElemIt = inverseElemSet.begin();
6659 for ( ; invElemIt != inverseElemSet.end(); invElemIt++ )
6660 theElems.insert( *invElemIt );
6662 // Replicate or reverse elements
6664 std::vector<int> iForw;
6665 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
6667 const SMDS_MeshElement* elem = *itElem;
6668 if ( !elem ) continue;
6670 SMDSAbs_GeometryType geomType = elem->GetGeomType();
6671 int nbNodes = elem->NbNodes();
6672 if ( geomType == SMDSGeom_NONE ) continue; // node
6674 switch ( geomType ) {
6676 case SMDSGeom_POLYGON: // ---------------------- polygon
6678 vector<const SMDS_MeshNode*> poly_nodes (nbNodes);
6680 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6681 while (itN->more()) {
6682 const SMDS_MeshNode* node =
6683 static_cast<const SMDS_MeshNode*>(itN->next());
6684 TNodeNodeMap::iterator nodeMapIt = nodeMap.find(node);
6685 if (nodeMapIt == nodeMap.end())
6686 break; // not all nodes transformed
6688 // reverse mirrored faces and volumes
6689 poly_nodes[nbNodes - iNode - 1] = (*nodeMapIt).second;
6691 poly_nodes[iNode] = (*nodeMapIt).second;
6695 if ( iNode != nbNodes )
6696 continue; // not all nodes transformed
6698 if ( theTargetMesh ) {
6699 myLastCreatedElems.Append(aTgtMesh->AddPolygonalFace(poly_nodes));
6700 srcElems.Append( elem );
6702 else if ( theCopy ) {
6703 myLastCreatedElems.Append(aMesh->AddPolygonalFace(poly_nodes));
6704 srcElems.Append( elem );
6707 aMesh->ChangePolygonNodes(elem, poly_nodes);
6712 case SMDSGeom_POLYHEDRA: // ------------------ polyhedral volume
6714 const SMDS_VtkVolume* aPolyedre =
6715 dynamic_cast<const SMDS_VtkVolume*>( elem );
6717 MESSAGE("Warning: bad volumic element");
6721 vector<const SMDS_MeshNode*> poly_nodes; poly_nodes.reserve( nbNodes );
6722 vector<int> quantities; quantities.reserve( nbNodes );
6724 bool allTransformed = true;
6725 int nbFaces = aPolyedre->NbFaces();
6726 for (int iface = 1; iface <= nbFaces && allTransformed; iface++) {
6727 int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
6728 for (int inode = 1; inode <= nbFaceNodes && allTransformed; inode++) {
6729 const SMDS_MeshNode* node = aPolyedre->GetFaceNode(iface, inode);
6730 TNodeNodeMap::iterator nodeMapIt = nodeMap.find(node);
6731 if (nodeMapIt == nodeMap.end()) {
6732 allTransformed = false; // not all nodes transformed
6734 poly_nodes.push_back((*nodeMapIt).second);
6736 if ( needReverse && allTransformed )
6737 std::reverse( poly_nodes.end() - nbFaceNodes, poly_nodes.end() );
6739 quantities.push_back(nbFaceNodes);
6741 if ( !allTransformed )
6742 continue; // not all nodes transformed
6744 if ( theTargetMesh ) {
6745 myLastCreatedElems.Append(aTgtMesh->AddPolyhedralVolume(poly_nodes, quantities));
6746 srcElems.Append( elem );
6748 else if ( theCopy ) {
6749 myLastCreatedElems.Append(aMesh->AddPolyhedralVolume(poly_nodes, quantities));
6750 srcElems.Append( elem );
6753 aMesh->ChangePolyhedronNodes(elem, poly_nodes, quantities);
6758 case SMDSGeom_BALL: // -------------------- Ball
6760 if ( !theCopy && !theTargetMesh ) continue;
6762 TNodeNodeMap::iterator nodeMapIt = nodeMap.find( elem->GetNode(0) );
6763 if (nodeMapIt == nodeMap.end())
6764 continue; // not all nodes transformed
6766 double diameter = static_cast<const SMDS_BallElement*>(elem)->GetDiameter();
6767 if ( theTargetMesh ) {
6768 myLastCreatedElems.Append(aTgtMesh->AddBall( nodeMapIt->second, diameter ));
6769 srcElems.Append( elem );
6772 myLastCreatedElems.Append(aMesh->AddBall( nodeMapIt->second, diameter ));
6773 srcElems.Append( elem );
6778 default: // ----------------------- Regular elements
6780 while ( iForw.size() < nbNodes ) iForw.push_back( iForw.size() );
6781 const std::vector<int>& iRev = SMDS_MeshCell::reverseSmdsOrder( elem->GetEntityType() );
6782 const std::vector<int>& i = needReverse ? iRev : iForw;
6784 // find transformed nodes
6785 vector<const SMDS_MeshNode*> nodes(nbNodes);
6787 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6788 while ( itN->more() ) {
6789 const SMDS_MeshNode* node =
6790 static_cast<const SMDS_MeshNode*>( itN->next() );
6791 TNodeNodeMap::iterator nodeMapIt = nodeMap.find( node );
6792 if ( nodeMapIt == nodeMap.end() )
6793 break; // not all nodes transformed
6794 nodes[ i [ iNode++ ]] = (*nodeMapIt).second;
6796 if ( iNode != nbNodes )
6797 continue; // not all nodes transformed
6799 if ( theTargetMesh ) {
6800 if ( SMDS_MeshElement* copy =
6801 targetMeshEditor.AddElement( nodes, elem->GetType(), elem->IsPoly() )) {
6802 myLastCreatedElems.Append( copy );
6803 srcElems.Append( elem );
6806 else if ( theCopy ) {
6807 if ( AddElement( nodes, elem->GetType(), elem->IsPoly() ))
6808 srcElems.Append( elem );
6811 // reverse element as it was reversed by transformation
6813 aMesh->ChangeElementNodes( elem, &nodes[0], nbNodes );
6815 } // switch ( geomType )
6817 } // loop on elements
6819 PGroupIDs newGroupIDs;
6821 if ( ( theMakeGroups && theCopy ) ||
6822 ( theMakeGroups && theTargetMesh ) )
6823 newGroupIDs = generateGroups( srcNodes, srcElems, groupPostfix, theTargetMesh, false );
6828 //=======================================================================
6830 * \brief Create groups of elements made during transformation
6831 * \param nodeGens - nodes making corresponding myLastCreatedNodes
6832 * \param elemGens - elements making corresponding myLastCreatedElems
6833 * \param postfix - to append to names of new groups
6834 * \param targetMesh - mesh to create groups in
6835 * \param topPresent - is there "top" elements that are created by sweeping
6837 //=======================================================================
6839 SMESH_MeshEditor::PGroupIDs
6840 SMESH_MeshEditor::generateGroups(const SMESH_SequenceOfElemPtr& nodeGens,
6841 const SMESH_SequenceOfElemPtr& elemGens,
6842 const std::string& postfix,
6843 SMESH_Mesh* targetMesh,
6844 const bool topPresent)
6846 PGroupIDs newGroupIDs( new list<int> );
6847 SMESH_Mesh* mesh = targetMesh ? targetMesh : GetMesh();
6849 // Sort existing groups by types and collect their names
6851 // containers to store an old group and generated new ones;
6852 // 1st new group is for result elems of different type than a source one;
6853 // 2nd new group is for same type result elems ("top" group at extrusion)
6855 using boost::make_tuple;
6856 typedef tuple< SMESHDS_GroupBase*, SMESHDS_Group*, SMESHDS_Group* > TOldNewGroup;
6857 vector< list< TOldNewGroup > > groupsByType( SMDSAbs_NbElementTypes );
6858 vector< TOldNewGroup* > orderedOldNewGroups; // in order of old groups
6860 set< string > groupNames;
6862 SMESH_Mesh::GroupIteratorPtr groupIt = GetMesh()->GetGroups();
6863 if ( !groupIt->more() ) return newGroupIDs;
6865 int newGroupID = mesh->GetGroupIds().back()+1;
6866 while ( groupIt->more() )
6868 SMESH_Group * group = groupIt->next();
6869 if ( !group ) continue;
6870 SMESHDS_GroupBase* groupDS = group->GetGroupDS();
6871 if ( !groupDS || groupDS->IsEmpty() ) continue;
6872 groupNames.insert ( group->GetName() );
6873 groupDS->SetStoreName( group->GetName() );
6874 const SMDSAbs_ElementType type = groupDS->GetType();
6875 SMESHDS_Group* newGroup = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
6876 SMESHDS_Group* newTopGroup = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
6877 groupsByType[ type ].push_back( make_tuple( groupDS, newGroup, newTopGroup ));
6878 orderedOldNewGroups.push_back( & groupsByType[ type ].back() );
6881 // Loop on nodes and elements to add them in new groups
6883 vector< const SMDS_MeshElement* > resultElems;
6884 for ( int isNodes = 0; isNodes < 2; ++isNodes )
6886 const SMESH_SequenceOfElemPtr& gens = isNodes ? nodeGens : elemGens;
6887 const SMESH_SequenceOfElemPtr& elems = isNodes ? myLastCreatedNodes : myLastCreatedElems;
6888 if ( gens.Length() != elems.Length() )
6889 throw SALOME_Exception("SMESH_MeshEditor::generateGroups(): invalid args");
6891 // loop on created elements
6892 for (int iElem = 1; iElem <= elems.Length(); ++iElem )
6894 const SMDS_MeshElement* sourceElem = gens( iElem );
6895 if ( !sourceElem ) {
6896 MESSAGE("generateGroups(): NULL source element");
6899 list< TOldNewGroup > & groupsOldNew = groupsByType[ sourceElem->GetType() ];
6900 if ( groupsOldNew.empty() ) { // no groups of this type at all
6901 while ( iElem < gens.Length() && gens( iElem+1 ) == sourceElem )
6902 ++iElem; // skip all elements made by sourceElem
6905 // collect all elements made by the iElem-th sourceElem
6906 resultElems.clear();
6907 if ( const SMDS_MeshElement* resElem = elems( iElem ))
6908 if ( resElem != sourceElem )
6909 resultElems.push_back( resElem );
6910 while ( iElem < gens.Length() && gens( iElem+1 ) == sourceElem )
6911 if ( const SMDS_MeshElement* resElem = elems( ++iElem ))
6912 if ( resElem != sourceElem )
6913 resultElems.push_back( resElem );
6915 const SMDS_MeshElement* topElem = 0;
6916 if ( isNodes ) // there must be a top element
6918 topElem = resultElems.back();
6919 resultElems.pop_back();
6923 vector< const SMDS_MeshElement* >::reverse_iterator resElemIt = resultElems.rbegin();
6924 for ( ; resElemIt != resultElems.rend() ; ++resElemIt )
6925 if ( (*resElemIt)->GetType() == sourceElem->GetType() )
6927 topElem = *resElemIt;
6928 *resElemIt = 0; // erase *resElemIt
6932 // add resultElems to groups originted from ones the sourceElem belongs to
6933 list< TOldNewGroup >::iterator gOldNew, gLast = groupsOldNew.end();
6934 for ( gOldNew = groupsOldNew.begin(); gOldNew != gLast; ++gOldNew )
6936 SMESHDS_GroupBase* oldGroup = gOldNew->get<0>();
6937 if ( oldGroup->Contains( sourceElem )) // sourceElem is in oldGroup
6939 // fill in a new group
6940 SMDS_MeshGroup & newGroup = gOldNew->get<1>()->SMDSGroup();
6941 vector< const SMDS_MeshElement* >::iterator resLast = resultElems.end(), resElemIt;
6942 for ( resElemIt = resultElems.begin(); resElemIt != resLast; ++resElemIt )
6944 newGroup.Add( *resElemIt );
6946 // fill a "top" group
6949 SMDS_MeshGroup & newTopGroup = gOldNew->get<2>()->SMDSGroup();
6950 newTopGroup.Add( topElem );
6954 } // loop on created elements
6955 }// loop on nodes and elements
6957 // Create new SMESH_Groups from SMESHDS_Groups and remove empty SMESHDS_Groups
6959 list<int> topGrouIds;
6960 for ( size_t i = 0; i < orderedOldNewGroups.size(); ++i )
6962 SMESHDS_GroupBase* oldGroupDS = orderedOldNewGroups[i]->get<0>();
6963 SMESHDS_Group* newGroups[2] = { orderedOldNewGroups[i]->get<1>(),
6964 orderedOldNewGroups[i]->get<2>() };
6965 for ( int is2nd = 0; is2nd < 2; ++is2nd )
6967 SMESHDS_Group* newGroupDS = newGroups[ is2nd ];
6968 if ( newGroupDS->IsEmpty() )
6970 mesh->GetMeshDS()->RemoveGroup( newGroupDS );
6975 newGroupDS->SetType( newGroupDS->GetElements()->next()->GetType() );
6978 const bool isTop = ( topPresent &&
6979 newGroupDS->GetType() == oldGroupDS->GetType() &&
6982 string name = oldGroupDS->GetStoreName();
6983 { // remove trailing whitespaces (issue 22599)
6984 size_t size = name.size();
6985 while ( size > 1 && isspace( name[ size-1 ]))
6987 if ( size != name.size() )
6989 name.resize( size );
6990 oldGroupDS->SetStoreName( name.c_str() );
6993 if ( !targetMesh ) {
6994 string suffix = ( isTop ? "top": postfix.c_str() );
6998 while ( !groupNames.insert( name ).second ) // name exists
6999 name = SMESH_Comment( oldGroupDS->GetStoreName() ) << "_" << suffix << "_" << nb++;
7004 newGroupDS->SetStoreName( name.c_str() );
7006 // make a SMESH_Groups
7007 mesh->AddGroup( newGroupDS );
7009 topGrouIds.push_back( newGroupDS->GetID() );
7011 newGroupIDs->push_back( newGroupDS->GetID() );
7015 newGroupIDs->splice( newGroupIDs->end(), topGrouIds );
7020 //================================================================================
7022 * \brief Return list of group of nodes close to each other within theTolerance
7023 * Search among theNodes or in the whole mesh if theNodes is empty using
7024 * an Octree algorithm
7026 //================================================================================
7028 void SMESH_MeshEditor::FindCoincidentNodes (TIDSortedNodeSet & theNodes,
7029 const double theTolerance,
7030 TListOfListOfNodes & theGroupsOfNodes)
7032 myLastCreatedElems.Clear();
7033 myLastCreatedNodes.Clear();
7035 if ( theNodes.empty() )
7036 { // get all nodes in the mesh
7037 SMDS_NodeIteratorPtr nIt = GetMeshDS()->nodesIterator(/*idInceasingOrder=*/true);
7038 while ( nIt->more() )
7039 theNodes.insert( theNodes.end(),nIt->next());
7042 SMESH_OctreeNode::FindCoincidentNodes ( theNodes, &theGroupsOfNodes, theTolerance);
7045 //=======================================================================
7046 //function : SimplifyFace
7048 //=======================================================================
7050 int SMESH_MeshEditor::SimplifyFace (const vector<const SMDS_MeshNode *>& faceNodes,
7051 vector<const SMDS_MeshNode *>& poly_nodes,
7052 vector<int>& quantities) const
7054 int nbNodes = faceNodes.size();
7059 set<const SMDS_MeshNode*> nodeSet;
7061 // get simple seq of nodes
7062 //const SMDS_MeshNode* simpleNodes[ nbNodes ];
7063 vector<const SMDS_MeshNode*> simpleNodes( nbNodes );
7064 int iSimple = 0, nbUnique = 0;
7066 simpleNodes[iSimple++] = faceNodes[0];
7068 for (int iCur = 1; iCur < nbNodes; iCur++) {
7069 if (faceNodes[iCur] != simpleNodes[iSimple - 1]) {
7070 simpleNodes[iSimple++] = faceNodes[iCur];
7071 if (nodeSet.insert( faceNodes[iCur] ).second)
7075 int nbSimple = iSimple;
7076 if (simpleNodes[nbSimple - 1] == simpleNodes[0]) {
7086 bool foundLoop = (nbSimple > nbUnique);
7089 set<const SMDS_MeshNode*> loopSet;
7090 for (iSimple = 0; iSimple < nbSimple && !foundLoop; iSimple++) {
7091 const SMDS_MeshNode* n = simpleNodes[iSimple];
7092 if (!loopSet.insert( n ).second) {
7096 int iC = 0, curLast = iSimple;
7097 for (; iC < curLast; iC++) {
7098 if (simpleNodes[iC] == n) break;
7100 int loopLen = curLast - iC;
7102 // create sub-element
7104 quantities.push_back(loopLen);
7105 for (; iC < curLast; iC++) {
7106 poly_nodes.push_back(simpleNodes[iC]);
7109 // shift the rest nodes (place from the first loop position)
7110 for (iC = curLast + 1; iC < nbSimple; iC++) {
7111 simpleNodes[iC - loopLen] = simpleNodes[iC];
7113 nbSimple -= loopLen;
7116 } // for (iSimple = 0; iSimple < nbSimple; iSimple++)
7117 } // while (foundLoop)
7121 quantities.push_back(iSimple);
7122 for (int i = 0; i < iSimple; i++)
7123 poly_nodes.push_back(simpleNodes[i]);
7129 //=======================================================================
7130 //function : MergeNodes
7131 //purpose : In each group, the cdr of nodes are substituted by the first one
7133 //=======================================================================
7135 void SMESH_MeshEditor::MergeNodes (TListOfListOfNodes & theGroupsOfNodes)
7137 MESSAGE("MergeNodes");
7138 myLastCreatedElems.Clear();
7139 myLastCreatedNodes.Clear();
7141 SMESHDS_Mesh* aMesh = GetMeshDS();
7143 TNodeNodeMap nodeNodeMap; // node to replace - new node
7144 set<const SMDS_MeshElement*> elems; // all elements with changed nodes
7145 list< int > rmElemIds, rmNodeIds;
7147 // Fill nodeNodeMap and elems
7149 TListOfListOfNodes::iterator grIt = theGroupsOfNodes.begin();
7150 for ( ; grIt != theGroupsOfNodes.end(); grIt++ ) {
7151 list<const SMDS_MeshNode*>& nodes = *grIt;
7152 list<const SMDS_MeshNode*>::iterator nIt = nodes.begin();
7153 const SMDS_MeshNode* nToKeep = *nIt;
7154 //MESSAGE("node to keep " << nToKeep->GetID());
7155 for ( ++nIt; nIt != nodes.end(); nIt++ ) {
7156 const SMDS_MeshNode* nToRemove = *nIt;
7157 nodeNodeMap.insert( TNodeNodeMap::value_type( nToRemove, nToKeep ));
7158 if ( nToRemove != nToKeep ) {
7159 //MESSAGE(" node to remove " << nToRemove->GetID());
7160 rmNodeIds.push_back( nToRemove->GetID() );
7161 AddToSameGroups( nToKeep, nToRemove, aMesh );
7162 // set _alwaysComputed to a sub-mesh of VERTEX to enable mesh computing
7163 // after MergeNodes() w/o creating node in place of merged ones.
7164 const SMDS_PositionPtr& pos = nToRemove->GetPosition();
7165 if ( pos && pos->GetTypeOfPosition() == SMDS_TOP_VERTEX )
7166 if ( SMESH_subMesh* sm = myMesh->GetSubMeshContaining( nToRemove->getshapeId() ))
7167 sm->SetIsAlwaysComputed( true );
7170 SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
7171 while ( invElemIt->more() ) {
7172 const SMDS_MeshElement* elem = invElemIt->next();
7177 // Change element nodes or remove an element
7179 set<const SMDS_MeshElement*>::iterator eIt = elems.begin();
7180 for ( ; eIt != elems.end(); eIt++ ) {
7181 const SMDS_MeshElement* elem = *eIt;
7182 //MESSAGE(" ---- inverse elem on node to remove " << elem->GetID());
7183 int nbNodes = elem->NbNodes();
7184 int aShapeId = FindShape( elem );
7186 set<const SMDS_MeshNode*> nodeSet;
7187 vector< const SMDS_MeshNode*> curNodes( nbNodes ), uniqueNodes( nbNodes );
7188 int iUnique = 0, iCur = 0, nbRepl = 0;
7189 vector<int> iRepl( nbNodes );
7191 // get new seq of nodes
7192 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
7193 while ( itN->more() ) {
7194 const SMDS_MeshNode* n =
7195 static_cast<const SMDS_MeshNode*>( itN->next() );
7197 TNodeNodeMap::iterator nnIt = nodeNodeMap.find( n );
7198 if ( nnIt != nodeNodeMap.end() ) { // n sticks
7200 // BUG 0020185: begin
7202 bool stopRecur = false;
7203 set<const SMDS_MeshNode*> nodesRecur;
7204 nodesRecur.insert(n);
7205 while (!stopRecur) {
7206 TNodeNodeMap::iterator nnIt_i = nodeNodeMap.find( n );
7207 if ( nnIt_i != nodeNodeMap.end() ) { // n sticks
7208 n = (*nnIt_i).second;
7209 if (!nodesRecur.insert(n).second) {
7210 // error: recursive dependancy
7220 curNodes[ iCur ] = n;
7221 bool isUnique = nodeSet.insert( n ).second;
7223 uniqueNodes[ iUnique++ ] = n;
7225 iRepl[ nbRepl++ ] = iCur;
7229 // Analyse element topology after replacement
7232 int nbUniqueNodes = nodeSet.size();
7233 //MESSAGE("nbNodes nbUniqueNodes " << nbNodes << " " << nbUniqueNodes);
7234 if ( nbNodes != nbUniqueNodes ) { // some nodes stick
7235 // Polygons and Polyhedral volumes
7236 if (elem->IsPoly()) {
7238 if (elem->GetType() == SMDSAbs_Face) {
7240 vector<const SMDS_MeshNode *> face_nodes (nbNodes);
7242 for (; inode < nbNodes; inode++) {
7243 face_nodes[inode] = curNodes[inode];
7246 vector<const SMDS_MeshNode *> polygons_nodes;
7247 vector<int> quantities;
7248 int nbNew = SimplifyFace(face_nodes, polygons_nodes, quantities);
7251 for (int iface = 0; iface < nbNew; iface++) {
7252 int nbNodes = quantities[iface];
7253 vector<const SMDS_MeshNode *> poly_nodes (nbNodes);
7254 for (int ii = 0; ii < nbNodes; ii++, inode++) {
7255 poly_nodes[ii] = polygons_nodes[inode];
7257 SMDS_MeshElement* newElem = aMesh->AddPolygonalFace(poly_nodes);
7258 myLastCreatedElems.Append(newElem);
7260 aMesh->SetMeshElementOnShape(newElem, aShapeId);
7263 MESSAGE("ChangeElementNodes MergeNodes Polygon");
7264 //aMesh->ChangeElementNodes(elem, &polygons_nodes[inode], quantities[nbNew - 1]);
7265 vector<const SMDS_MeshNode *> polynodes(polygons_nodes.begin()+inode,polygons_nodes.end());
7267 if (nbNew > 0) quid = nbNew - 1;
7268 vector<int> newquant(quantities.begin()+quid, quantities.end());
7269 const SMDS_MeshElement* newElem = 0;
7270 newElem = aMesh->AddPolyhedralVolume(polynodes, newquant);
7271 myLastCreatedElems.Append(newElem);
7272 if ( aShapeId && newElem )
7273 aMesh->SetMeshElementOnShape( newElem, aShapeId );
7274 rmElemIds.push_back(elem->GetID());
7277 rmElemIds.push_back(elem->GetID());
7281 else if (elem->GetType() == SMDSAbs_Volume) {
7282 // Polyhedral volume
7283 if (nbUniqueNodes < 4) {
7284 rmElemIds.push_back(elem->GetID());
7287 // each face has to be analyzed in order to check volume validity
7288 const SMDS_VtkVolume* aPolyedre =
7289 dynamic_cast<const SMDS_VtkVolume*>( elem );
7291 int nbFaces = aPolyedre->NbFaces();
7293 vector<const SMDS_MeshNode *> poly_nodes;
7294 vector<int> quantities;
7296 for (int iface = 1; iface <= nbFaces; iface++) {
7297 int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
7298 vector<const SMDS_MeshNode *> faceNodes (nbFaceNodes);
7300 for (int inode = 1; inode <= nbFaceNodes; inode++) {
7301 const SMDS_MeshNode * faceNode = aPolyedre->GetFaceNode(iface, inode);
7302 TNodeNodeMap::iterator nnIt = nodeNodeMap.find(faceNode);
7303 if (nnIt != nodeNodeMap.end()) { // faceNode sticks
7304 faceNode = (*nnIt).second;
7306 faceNodes[inode - 1] = faceNode;
7309 SimplifyFace(faceNodes, poly_nodes, quantities);
7312 if (quantities.size() > 3) {
7313 // to be done: remove coincident faces
7316 if (quantities.size() > 3)
7318 MESSAGE("ChangeElementNodes MergeNodes Polyhedron");
7319 //aMesh->ChangePolyhedronNodes(elem, poly_nodes, quantities);
7320 const SMDS_MeshElement* newElem = 0;
7321 newElem = aMesh->AddPolyhedralVolume(poly_nodes, quantities);
7322 myLastCreatedElems.Append(newElem);
7323 if ( aShapeId && newElem )
7324 aMesh->SetMeshElementOnShape( newElem, aShapeId );
7325 rmElemIds.push_back(elem->GetID());
7329 rmElemIds.push_back(elem->GetID());
7340 // TODO not all the possible cases are solved. Find something more generic?
7341 switch ( nbNodes ) {
7342 case 2: ///////////////////////////////////// EDGE
7343 isOk = false; break;
7344 case 3: ///////////////////////////////////// TRIANGLE
7345 isOk = false; break;
7347 if ( elem->GetType() == SMDSAbs_Volume ) // TETRAHEDRON
7349 else { //////////////////////////////////// QUADRANGLE
7350 if ( nbUniqueNodes < 3 )
7352 else if ( nbRepl == 2 && iRepl[ 1 ] - iRepl[ 0 ] == 2 )
7353 isOk = false; // opposite nodes stick
7354 //MESSAGE("isOk " << isOk);
7357 case 6: ///////////////////////////////////// PENTAHEDRON
7358 if ( nbUniqueNodes == 4 ) {
7359 // ---------------------------------> tetrahedron
7361 iRepl[ 0 ] > 2 && iRepl[ 1 ] > 2 && iRepl[ 2 ] > 2 ) {
7362 // all top nodes stick: reverse a bottom
7363 uniqueNodes[ 0 ] = curNodes [ 1 ];
7364 uniqueNodes[ 1 ] = curNodes [ 0 ];
7366 else if (nbRepl == 3 &&
7367 iRepl[ 0 ] < 3 && iRepl[ 1 ] < 3 && iRepl[ 2 ] < 3 ) {
7368 // all bottom nodes stick: set a top before
7369 uniqueNodes[ 3 ] = uniqueNodes [ 0 ];
7370 uniqueNodes[ 0 ] = curNodes [ 3 ];
7371 uniqueNodes[ 1 ] = curNodes [ 4 ];
7372 uniqueNodes[ 2 ] = curNodes [ 5 ];
7374 else if (nbRepl == 4 &&
7375 iRepl[ 2 ] - iRepl [ 0 ] == 3 && iRepl[ 3 ] - iRepl [ 1 ] == 3 ) {
7376 // a lateral face turns into a line: reverse a bottom
7377 uniqueNodes[ 0 ] = curNodes [ 1 ];
7378 uniqueNodes[ 1 ] = curNodes [ 0 ];
7383 else if ( nbUniqueNodes == 5 ) {
7384 // PENTAHEDRON --------------------> 2 tetrahedrons
7385 if ( nbRepl == 2 && iRepl[ 1 ] - iRepl [ 0 ] == 3 ) {
7386 // a bottom node sticks with a linked top one
7388 SMDS_MeshElement* newElem =
7389 aMesh->AddVolume(curNodes[ 3 ],
7392 curNodes[ iRepl[ 0 ] == 2 ? 1 : 2 ]);
7393 myLastCreatedElems.Append(newElem);
7395 aMesh->SetMeshElementOnShape( newElem, aShapeId );
7396 // 2. : reverse a bottom
7397 uniqueNodes[ 0 ] = curNodes [ 1 ];
7398 uniqueNodes[ 1 ] = curNodes [ 0 ];
7408 if(elem->IsQuadratic()) { // Quadratic quadrangle
7420 MESSAGE("nbRepl=2: " << iRepl[0] << " " << iRepl[1]);
7423 MESSAGE("nbRepl=3: " << iRepl[0] << " " << iRepl[1] << " " << iRepl[2]);
7425 if( iRepl[0]==0 && iRepl[1]==1 && iRepl[2]==4 ) {
7426 uniqueNodes[0] = curNodes[0];
7427 uniqueNodes[1] = curNodes[2];
7428 uniqueNodes[2] = curNodes[3];
7429 uniqueNodes[3] = curNodes[5];
7430 uniqueNodes[4] = curNodes[6];
7431 uniqueNodes[5] = curNodes[7];
7434 if( iRepl[0]==0 && iRepl[1]==3 && iRepl[2]==7 ) {
7435 uniqueNodes[0] = curNodes[0];
7436 uniqueNodes[1] = curNodes[1];
7437 uniqueNodes[2] = curNodes[2];
7438 uniqueNodes[3] = curNodes[4];
7439 uniqueNodes[4] = curNodes[5];
7440 uniqueNodes[5] = curNodes[6];
7443 if( iRepl[0]==0 && iRepl[1]==4 && iRepl[2]==7 ) {
7444 uniqueNodes[0] = curNodes[1];
7445 uniqueNodes[1] = curNodes[2];
7446 uniqueNodes[2] = curNodes[3];
7447 uniqueNodes[3] = curNodes[5];
7448 uniqueNodes[4] = curNodes[6];
7449 uniqueNodes[5] = curNodes[0];
7452 if( iRepl[0]==1 && iRepl[1]==2 && iRepl[2]==5 ) {
7453 uniqueNodes[0] = curNodes[0];
7454 uniqueNodes[1] = curNodes[1];
7455 uniqueNodes[2] = curNodes[3];
7456 uniqueNodes[3] = curNodes[4];
7457 uniqueNodes[4] = curNodes[6];
7458 uniqueNodes[5] = curNodes[7];
7461 if( iRepl[0]==1 && iRepl[1]==4 && iRepl[2]==5 ) {
7462 uniqueNodes[0] = curNodes[0];
7463 uniqueNodes[1] = curNodes[2];
7464 uniqueNodes[2] = curNodes[3];
7465 uniqueNodes[3] = curNodes[1];
7466 uniqueNodes[4] = curNodes[6];
7467 uniqueNodes[5] = curNodes[7];
7470 if( iRepl[0]==2 && iRepl[1]==3 && iRepl[2]==6 ) {
7471 uniqueNodes[0] = curNodes[0];
7472 uniqueNodes[1] = curNodes[1];
7473 uniqueNodes[2] = curNodes[2];
7474 uniqueNodes[3] = curNodes[4];
7475 uniqueNodes[4] = curNodes[5];
7476 uniqueNodes[5] = curNodes[7];
7479 if( iRepl[0]==2 && iRepl[1]==5 && iRepl[2]==6 ) {
7480 uniqueNodes[0] = curNodes[0];
7481 uniqueNodes[1] = curNodes[1];
7482 uniqueNodes[2] = curNodes[3];
7483 uniqueNodes[3] = curNodes[4];
7484 uniqueNodes[4] = curNodes[2];
7485 uniqueNodes[5] = curNodes[7];
7488 if( iRepl[0]==3 && iRepl[1]==6 && iRepl[2]==7 ) {
7489 uniqueNodes[0] = curNodes[0];
7490 uniqueNodes[1] = curNodes[1];
7491 uniqueNodes[2] = curNodes[2];
7492 uniqueNodes[3] = curNodes[4];
7493 uniqueNodes[4] = curNodes[5];
7494 uniqueNodes[5] = curNodes[3];
7499 MESSAGE("nbRepl=4: " << iRepl[0] << " " << iRepl[1] << " " << iRepl[2] << " " << iRepl[3]);
7502 MESSAGE("nbRepl=5: " << iRepl[0] << " " << iRepl[1] << " " << iRepl[2] << " " << iRepl[3] << " " << iRepl[4]);
7506 //////////////////////////////////// HEXAHEDRON
7508 SMDS_VolumeTool hexa (elem);
7509 hexa.SetExternalNormal();
7510 if ( nbUniqueNodes == 4 && nbRepl == 4 ) {
7511 //////////////////////// HEX ---> 1 tetrahedron
7512 for ( int iFace = 0; iFace < 6; iFace++ ) {
7513 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7514 if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7515 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7516 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7517 // one face turns into a point ...
7518 int iOppFace = hexa.GetOppFaceIndex( iFace );
7519 ind = hexa.GetFaceNodesIndices( iOppFace );
7521 for ( iCur = 0; iCur < 4 && nbStick < 2; iCur++ ) {
7522 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7525 if ( nbStick == 1 ) {
7526 // ... and the opposite one - into a triangle.
7528 ind = hexa.GetFaceNodesIndices( iFace );
7529 uniqueNodes[ 3 ] = curNodes[ind[ 0 ]];
7536 else if ( nbUniqueNodes == 6 && nbRepl == 2 ) {
7537 //////////////////////// HEX ---> 1 prism
7538 int nbTria = 0, iTria[3];
7539 const int *ind; // indices of face nodes
7540 // look for triangular faces
7541 for ( int iFace = 0; iFace < 6 && nbTria < 3; iFace++ ) {
7542 ind = hexa.GetFaceNodesIndices( iFace );
7543 TIDSortedNodeSet faceNodes;
7544 for ( iCur = 0; iCur < 4; iCur++ )
7545 faceNodes.insert( curNodes[ind[iCur]] );
7546 if ( faceNodes.size() == 3 )
7547 iTria[ nbTria++ ] = iFace;
7549 // check if triangles are opposite
7550 if ( nbTria == 2 && iTria[0] == hexa.GetOppFaceIndex( iTria[1] ))
7553 // set nodes of the bottom triangle
7554 ind = hexa.GetFaceNodesIndices( iTria[ 0 ]);
7556 for ( iCur = 0; iCur < 4; iCur++ )
7557 if ( ind[iCur] != iRepl[0] && ind[iCur] != iRepl[1])
7558 indB.push_back( ind[iCur] );
7559 if ( !hexa.IsForward() )
7560 std::swap( indB[0], indB[2] );
7561 for ( iCur = 0; iCur < 3; iCur++ )
7562 uniqueNodes[ iCur ] = curNodes[indB[iCur]];
7563 // set nodes of the top triangle
7564 const int *indT = hexa.GetFaceNodesIndices( iTria[ 1 ]);
7565 for ( iCur = 0; iCur < 3; ++iCur )
7566 for ( int j = 0; j < 4; ++j )
7567 if ( hexa.IsLinked( indB[ iCur ], indT[ j ] ))
7569 uniqueNodes[ iCur + 3 ] = curNodes[ indT[ j ]];
7575 else if (nbUniqueNodes == 5 && nbRepl == 4 ) {
7576 //////////////////// HEXAHEDRON ---> 2 tetrahedrons
7577 for ( int iFace = 0; iFace < 6; iFace++ ) {
7578 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7579 if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7580 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7581 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7582 // one face turns into a point ...
7583 int iOppFace = hexa.GetOppFaceIndex( iFace );
7584 ind = hexa.GetFaceNodesIndices( iOppFace );
7586 iUnique = 2; // reverse a tetrahedron 1 bottom
7587 for ( iCur = 0; iCur < 4 && nbStick == 0; iCur++ ) {
7588 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7590 else if ( iUnique >= 0 )
7591 uniqueNodes[ iUnique-- ] = curNodes[ind[ iCur ]];
7593 if ( nbStick == 0 ) {
7594 // ... and the opposite one is a quadrangle
7596 const int* indTop = hexa.GetFaceNodesIndices( iFace );
7597 uniqueNodes[ 3 ] = curNodes[indTop[ 0 ]];
7600 SMDS_MeshElement* newElem =
7601 aMesh->AddVolume(curNodes[ind[ 0 ]],
7604 curNodes[indTop[ 0 ]]);
7605 myLastCreatedElems.Append(newElem);
7607 aMesh->SetMeshElementOnShape( newElem, aShapeId );
7614 else if ( nbUniqueNodes == 6 && nbRepl == 4 ) {
7615 ////////////////// HEXAHEDRON ---> 2 tetrahedrons or 1 prism
7616 // find indices of quad and tri faces
7617 int iQuadFace[ 6 ], iTriFace[ 6 ], nbQuad = 0, nbTri = 0, iFace;
7618 for ( iFace = 0; iFace < 6; iFace++ ) {
7619 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7621 for ( iCur = 0; iCur < 4; iCur++ )
7622 nodeSet.insert( curNodes[ind[ iCur ]] );
7623 nbUniqueNodes = nodeSet.size();
7624 if ( nbUniqueNodes == 3 )
7625 iTriFace[ nbTri++ ] = iFace;
7626 else if ( nbUniqueNodes == 4 )
7627 iQuadFace[ nbQuad++ ] = iFace;
7629 if (nbQuad == 2 && nbTri == 4 &&
7630 hexa.GetOppFaceIndex( iQuadFace[ 0 ] ) == iQuadFace[ 1 ]) {
7631 // 2 opposite quadrangles stuck with a diagonal;
7632 // sample groups of merged indices: (0-4)(2-6)
7633 // --------------------------------------------> 2 tetrahedrons
7634 const int *ind1 = hexa.GetFaceNodesIndices( iQuadFace[ 0 ]); // indices of quad1 nodes
7635 const int *ind2 = hexa.GetFaceNodesIndices( iQuadFace[ 1 ]);
7636 int i0, i1d, i2, i3d, i0t, i2t; // d-daigonal, t-top
7637 if (curNodes[ind1[ 0 ]] == curNodes[ind2[ 0 ]] &&
7638 curNodes[ind1[ 2 ]] == curNodes[ind2[ 2 ]]) {
7639 // stuck with 0-2 diagonal
7647 else if (curNodes[ind1[ 1 ]] == curNodes[ind2[ 3 ]] &&
7648 curNodes[ind1[ 3 ]] == curNodes[ind2[ 1 ]]) {
7649 // stuck with 1-3 diagonal
7661 uniqueNodes[ 0 ] = curNodes [ i0 ];
7662 uniqueNodes[ 1 ] = curNodes [ i1d ];
7663 uniqueNodes[ 2 ] = curNodes [ i3d ];
7664 uniqueNodes[ 3 ] = curNodes [ i0t ];
7667 SMDS_MeshElement* newElem = aMesh->AddVolume(curNodes[ i1d ],
7671 myLastCreatedElems.Append(newElem);
7673 aMesh->SetMeshElementOnShape( newElem, aShapeId );
7676 else if (( nbTri == 2 && nbQuad == 3 ) || // merged (0-4)(1-5)
7677 ( nbTri == 4 && nbQuad == 2 )) { // merged (7-4)(1-5)
7678 // --------------------------------------------> prism
7679 // find 2 opposite triangles
7681 for ( iFace = 0; iFace + 1 < nbTri; iFace++ ) {
7682 if ( hexa.GetOppFaceIndex( iTriFace[ iFace ] ) == iTriFace[ iFace + 1 ]) {
7683 // find indices of kept and replaced nodes
7684 // and fill unique nodes of 2 opposite triangles
7685 const int *ind1 = hexa.GetFaceNodesIndices( iTriFace[ iFace ]);
7686 const int *ind2 = hexa.GetFaceNodesIndices( iTriFace[ iFace + 1 ]);
7687 const SMDS_MeshNode** hexanodes = hexa.GetNodes();
7688 // fill unique nodes
7691 for ( iCur = 0; iCur < 4 && isOk; iCur++ ) {
7692 const SMDS_MeshNode* n = curNodes[ind1[ iCur ]];
7693 const SMDS_MeshNode* nInit = hexanodes[ind1[ iCur ]];
7695 // iCur of a linked node of the opposite face (make normals co-directed):
7696 int iCurOpp = ( iCur == 1 || iCur == 3 ) ? 4 - iCur : iCur;
7697 // check that correspondent corners of triangles are linked
7698 if ( !hexa.IsLinked( ind1[ iCur ], ind2[ iCurOpp ] ))
7701 uniqueNodes[ iUnique ] = n;
7702 uniqueNodes[ iUnique + 3 ] = curNodes[ind2[ iCurOpp ]];
7711 } // if ( nbUniqueNodes == 6 && nbRepl == 4 )
7714 MESSAGE("MergeNodes() removes hexahedron "<< elem);
7721 } // switch ( nbNodes )
7723 } // if ( nbNodes != nbUniqueNodes ) // some nodes stick
7725 if ( isOk ) { // the elem remains valid after sticking nodes
7726 if (elem->IsPoly() && elem->GetType() == SMDSAbs_Volume)
7728 // Change nodes of polyedre
7729 const SMDS_VtkVolume* aPolyedre =
7730 dynamic_cast<const SMDS_VtkVolume*>( elem );
7732 int nbFaces = aPolyedre->NbFaces();
7734 vector<const SMDS_MeshNode *> poly_nodes;
7735 vector<int> quantities (nbFaces);
7737 for (int iface = 1; iface <= nbFaces; iface++) {
7738 int inode, nbFaceNodes = aPolyedre->NbFaceNodes(iface);
7739 quantities[iface - 1] = nbFaceNodes;
7741 for (inode = 1; inode <= nbFaceNodes; inode++) {
7742 const SMDS_MeshNode* curNode = aPolyedre->GetFaceNode(iface, inode);
7744 TNodeNodeMap::iterator nnIt = nodeNodeMap.find( curNode );
7745 if (nnIt != nodeNodeMap.end()) { // curNode sticks
7746 curNode = (*nnIt).second;
7748 poly_nodes.push_back(curNode);
7751 aMesh->ChangePolyhedronNodes( elem, poly_nodes, quantities );
7754 else // replace non-polyhedron elements
7756 const SMDSAbs_ElementType etyp = elem->GetType();
7757 const int elemId = elem->GetID();
7758 const bool isPoly = (elem->GetEntityType() == SMDSEntity_Polygon);
7759 uniqueNodes.resize(nbUniqueNodes);
7761 SMESHDS_SubMesh * sm = aShapeId > 0 ? aMesh->MeshElements(aShapeId) : 0;
7763 aMesh->RemoveFreeElement(elem, sm, /*fromGroups=*/false);
7764 SMDS_MeshElement* newElem = this->AddElement(uniqueNodes, etyp, isPoly, elemId);
7765 if ( sm && newElem )
7766 sm->AddElement( newElem );
7767 if ( elem != newElem )
7768 ReplaceElemInGroups( elem, newElem, aMesh );
7772 // Remove invalid regular element or invalid polygon
7773 rmElemIds.push_back( elem->GetID() );
7776 } // loop on elements
7778 // Remove bad elements, then equal nodes (order important)
7780 Remove( rmElemIds, false );
7781 Remove( rmNodeIds, true );
7786 // ========================================================
7787 // class : SortableElement
7788 // purpose : allow sorting elements basing on their nodes
7789 // ========================================================
7790 class SortableElement : public set <const SMDS_MeshElement*>
7794 SortableElement( const SMDS_MeshElement* theElem )
7797 SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator();
7798 while ( nodeIt->more() )
7799 this->insert( nodeIt->next() );
7802 const SMDS_MeshElement* Get() const
7805 void Set(const SMDS_MeshElement* e) const
7810 mutable const SMDS_MeshElement* myElem;
7813 //=======================================================================
7814 //function : FindEqualElements
7815 //purpose : Return list of group of elements built on the same nodes.
7816 // Search among theElements or in the whole mesh if theElements is empty
7817 //=======================================================================
7819 void SMESH_MeshEditor::FindEqualElements(TIDSortedElemSet & theElements,
7820 TListOfListOfElementsID & theGroupsOfElementsID)
7822 myLastCreatedElems.Clear();
7823 myLastCreatedNodes.Clear();
7825 typedef map< SortableElement, int > TMapOfNodeSet;
7826 typedef list<int> TGroupOfElems;
7828 if ( theElements.empty() )
7829 { // get all elements in the mesh
7830 SMDS_ElemIteratorPtr eIt = GetMeshDS()->elementsIterator();
7831 while ( eIt->more() )
7832 theElements.insert( theElements.end(), eIt->next());
7835 vector< TGroupOfElems > arrayOfGroups;
7836 TGroupOfElems groupOfElems;
7837 TMapOfNodeSet mapOfNodeSet;
7839 TIDSortedElemSet::iterator elemIt = theElements.begin();
7840 for ( int i = 0, j=0; elemIt != theElements.end(); ++elemIt, ++j ) {
7841 const SMDS_MeshElement* curElem = *elemIt;
7842 SortableElement SE(curElem);
7845 pair< TMapOfNodeSet::iterator, bool> pp = mapOfNodeSet.insert(make_pair(SE, i));
7846 if( !(pp.second) ) {
7847 TMapOfNodeSet::iterator& itSE = pp.first;
7848 ind = (*itSE).second;
7849 arrayOfGroups[ind].push_back(curElem->GetID());
7852 groupOfElems.clear();
7853 groupOfElems.push_back(curElem->GetID());
7854 arrayOfGroups.push_back(groupOfElems);
7859 vector< TGroupOfElems >::iterator groupIt = arrayOfGroups.begin();
7860 for ( ; groupIt != arrayOfGroups.end(); ++groupIt ) {
7861 groupOfElems = *groupIt;
7862 if ( groupOfElems.size() > 1 ) {
7863 groupOfElems.sort();
7864 theGroupsOfElementsID.push_back(groupOfElems);
7869 //=======================================================================
7870 //function : MergeElements
7871 //purpose : In each given group, substitute all elements by the first one.
7872 //=======================================================================
7874 void SMESH_MeshEditor::MergeElements(TListOfListOfElementsID & theGroupsOfElementsID)
7876 myLastCreatedElems.Clear();
7877 myLastCreatedNodes.Clear();
7879 typedef list<int> TListOfIDs;
7880 TListOfIDs rmElemIds; // IDs of elems to remove
7882 SMESHDS_Mesh* aMesh = GetMeshDS();
7884 TListOfListOfElementsID::iterator groupsIt = theGroupsOfElementsID.begin();
7885 while ( groupsIt != theGroupsOfElementsID.end() ) {
7886 TListOfIDs& aGroupOfElemID = *groupsIt;
7887 aGroupOfElemID.sort();
7888 int elemIDToKeep = aGroupOfElemID.front();
7889 const SMDS_MeshElement* elemToKeep = aMesh->FindElement(elemIDToKeep);
7890 aGroupOfElemID.pop_front();
7891 TListOfIDs::iterator idIt = aGroupOfElemID.begin();
7892 while ( idIt != aGroupOfElemID.end() ) {
7893 int elemIDToRemove = *idIt;
7894 const SMDS_MeshElement* elemToRemove = aMesh->FindElement(elemIDToRemove);
7895 // add the kept element in groups of removed one (PAL15188)
7896 AddToSameGroups( elemToKeep, elemToRemove, aMesh );
7897 rmElemIds.push_back( elemIDToRemove );
7903 Remove( rmElemIds, false );
7906 //=======================================================================
7907 //function : MergeEqualElements
7908 //purpose : Remove all but one of elements built on the same nodes.
7909 //=======================================================================
7911 void SMESH_MeshEditor::MergeEqualElements()
7913 TIDSortedElemSet aMeshElements; /* empty input ==
7914 to merge equal elements in the whole mesh */
7915 TListOfListOfElementsID aGroupsOfElementsID;
7916 FindEqualElements(aMeshElements, aGroupsOfElementsID);
7917 MergeElements(aGroupsOfElementsID);
7920 //=======================================================================
7921 //function : findAdjacentFace
7923 //=======================================================================
7925 static const SMDS_MeshElement* findAdjacentFace(const SMDS_MeshNode* n1,
7926 const SMDS_MeshNode* n2,
7927 const SMDS_MeshElement* elem)
7929 TIDSortedElemSet elemSet, avoidSet;
7931 avoidSet.insert ( elem );
7932 return SMESH_MeshAlgos::FindFaceInSet( n1, n2, elemSet, avoidSet );
7935 //=======================================================================
7936 //function : FindFreeBorder
7938 //=======================================================================
7940 #define ControlFreeBorder SMESH::Controls::FreeEdges::IsFreeEdge
7942 bool SMESH_MeshEditor::FindFreeBorder (const SMDS_MeshNode* theFirstNode,
7943 const SMDS_MeshNode* theSecondNode,
7944 const SMDS_MeshNode* theLastNode,
7945 list< const SMDS_MeshNode* > & theNodes,
7946 list< const SMDS_MeshElement* >& theFaces)
7948 if ( !theFirstNode || !theSecondNode )
7950 // find border face between theFirstNode and theSecondNode
7951 const SMDS_MeshElement* curElem = findAdjacentFace( theFirstNode, theSecondNode, 0 );
7955 theFaces.push_back( curElem );
7956 theNodes.push_back( theFirstNode );
7957 theNodes.push_back( theSecondNode );
7959 //vector<const SMDS_MeshNode*> nodes;
7960 const SMDS_MeshNode *nIgnore = theFirstNode, *nStart = theSecondNode;
7961 TIDSortedElemSet foundElems;
7962 bool needTheLast = ( theLastNode != 0 );
7964 while ( nStart != theLastNode ) {
7965 if ( nStart == theFirstNode )
7966 return !needTheLast;
7968 // find all free border faces sharing form nStart
7970 list< const SMDS_MeshElement* > curElemList;
7971 list< const SMDS_MeshNode* > nStartList;
7972 SMDS_ElemIteratorPtr invElemIt = nStart->GetInverseElementIterator(SMDSAbs_Face);
7973 while ( invElemIt->more() ) {
7974 const SMDS_MeshElement* e = invElemIt->next();
7975 if ( e == curElem || foundElems.insert( e ).second ) {
7977 int iNode = 0, nbNodes = e->NbNodes();
7978 //const SMDS_MeshNode* nodes[nbNodes+1];
7979 vector<const SMDS_MeshNode*> nodes(nbNodes+1);
7981 if(e->IsQuadratic()) {
7982 const SMDS_VtkFace* F =
7983 dynamic_cast<const SMDS_VtkFace*>(e);
7984 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
7985 // use special nodes iterator
7986 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
7987 while( anIter->more() ) {
7988 nodes[ iNode++ ] = cast2Node(anIter->next());
7992 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
7993 while ( nIt->more() )
7994 nodes[ iNode++ ] = static_cast<const SMDS_MeshNode*>( nIt->next() );
7996 nodes[ iNode ] = nodes[ 0 ];
7998 for ( iNode = 0; iNode < nbNodes; iNode++ )
7999 if (((nodes[ iNode ] == nStart && nodes[ iNode + 1] != nIgnore ) ||
8000 (nodes[ iNode + 1] == nStart && nodes[ iNode ] != nIgnore )) &&
8001 ControlFreeBorder( &nodes[ iNode ], e->GetID() ))
8003 nStartList.push_back( nodes[ iNode + ( nodes[ iNode ] == nStart ? 1 : 0 )]);
8004 curElemList.push_back( e );
8008 // analyse the found
8010 int nbNewBorders = curElemList.size();
8011 if ( nbNewBorders == 0 ) {
8012 // no free border furthermore
8013 return !needTheLast;
8015 else if ( nbNewBorders == 1 ) {
8016 // one more element found
8018 nStart = nStartList.front();
8019 curElem = curElemList.front();
8020 theFaces.push_back( curElem );
8021 theNodes.push_back( nStart );
8024 // several continuations found
8025 list< const SMDS_MeshElement* >::iterator curElemIt;
8026 list< const SMDS_MeshNode* >::iterator nStartIt;
8027 // check if one of them reached the last node
8028 if ( needTheLast ) {
8029 for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
8030 curElemIt!= curElemList.end();
8031 curElemIt++, nStartIt++ )
8032 if ( *nStartIt == theLastNode ) {
8033 theFaces.push_back( *curElemIt );
8034 theNodes.push_back( *nStartIt );
8038 // find the best free border by the continuations
8039 list<const SMDS_MeshNode*> contNodes[ 2 ], *cNL;
8040 list<const SMDS_MeshElement*> contFaces[ 2 ], *cFL;
8041 for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
8042 curElemIt!= curElemList.end();
8043 curElemIt++, nStartIt++ )
8045 cNL = & contNodes[ contNodes[0].empty() ? 0 : 1 ];
8046 cFL = & contFaces[ contFaces[0].empty() ? 0 : 1 ];
8047 // find one more free border
8048 if ( ! SMESH_MeshEditor::FindFreeBorder( nStart, *nStartIt, theLastNode, *cNL, *cFL )) {
8052 else if ( !contNodes[0].empty() && !contNodes[1].empty() ) {
8053 // choice: clear a worse one
8054 int iLongest = ( contNodes[0].size() < contNodes[1].size() ? 1 : 0 );
8055 int iWorse = ( needTheLast ? 1 - iLongest : iLongest );
8056 contNodes[ iWorse ].clear();
8057 contFaces[ iWorse ].clear();
8060 if ( contNodes[0].empty() && contNodes[1].empty() )
8063 // append the best free border
8064 cNL = & contNodes[ contNodes[0].empty() ? 1 : 0 ];
8065 cFL = & contFaces[ contFaces[0].empty() ? 1 : 0 ];
8066 theNodes.pop_back(); // remove nIgnore
8067 theNodes.pop_back(); // remove nStart
8068 theFaces.pop_back(); // remove curElem
8069 list< const SMDS_MeshNode* >::iterator nIt = cNL->begin();
8070 list< const SMDS_MeshElement* >::iterator fIt = cFL->begin();
8071 for ( ; nIt != cNL->end(); nIt++ ) theNodes.push_back( *nIt );
8072 for ( ; fIt != cFL->end(); fIt++ ) theFaces.push_back( *fIt );
8075 } // several continuations found
8076 } // while ( nStart != theLastNode )
8081 //=======================================================================
8082 //function : CheckFreeBorderNodes
8083 //purpose : Return true if the tree nodes are on a free border
8084 //=======================================================================
8086 bool SMESH_MeshEditor::CheckFreeBorderNodes(const SMDS_MeshNode* theNode1,
8087 const SMDS_MeshNode* theNode2,
8088 const SMDS_MeshNode* theNode3)
8090 list< const SMDS_MeshNode* > nodes;
8091 list< const SMDS_MeshElement* > faces;
8092 return FindFreeBorder( theNode1, theNode2, theNode3, nodes, faces);
8095 //=======================================================================
8096 //function : SewFreeBorder
8098 //=======================================================================
8100 SMESH_MeshEditor::Sew_Error
8101 SMESH_MeshEditor::SewFreeBorder (const SMDS_MeshNode* theBordFirstNode,
8102 const SMDS_MeshNode* theBordSecondNode,
8103 const SMDS_MeshNode* theBordLastNode,
8104 const SMDS_MeshNode* theSideFirstNode,
8105 const SMDS_MeshNode* theSideSecondNode,
8106 const SMDS_MeshNode* theSideThirdNode,
8107 const bool theSideIsFreeBorder,
8108 const bool toCreatePolygons,
8109 const bool toCreatePolyedrs)
8111 myLastCreatedElems.Clear();
8112 myLastCreatedNodes.Clear();
8114 MESSAGE("::SewFreeBorder()");
8115 Sew_Error aResult = SEW_OK;
8117 // ====================================
8118 // find side nodes and elements
8119 // ====================================
8121 list< const SMDS_MeshNode* > nSide[ 2 ];
8122 list< const SMDS_MeshElement* > eSide[ 2 ];
8123 list< const SMDS_MeshNode* >::iterator nIt[ 2 ];
8124 list< const SMDS_MeshElement* >::iterator eIt[ 2 ];
8128 if (!FindFreeBorder(theBordFirstNode,theBordSecondNode,theBordLastNode,
8129 nSide[0], eSide[0])) {
8130 MESSAGE(" Free Border 1 not found " );
8131 aResult = SEW_BORDER1_NOT_FOUND;
8133 if (theSideIsFreeBorder) {
8136 if (!FindFreeBorder(theSideFirstNode, theSideSecondNode, theSideThirdNode,
8137 nSide[1], eSide[1])) {
8138 MESSAGE(" Free Border 2 not found " );
8139 aResult = ( aResult != SEW_OK ? SEW_BOTH_BORDERS_NOT_FOUND : SEW_BORDER2_NOT_FOUND );
8142 if ( aResult != SEW_OK )
8145 if (!theSideIsFreeBorder) {
8149 // -------------------------------------------------------------------------
8151 // 1. If nodes to merge are not coincident, move nodes of the free border
8152 // from the coord sys defined by the direction from the first to last
8153 // nodes of the border to the correspondent sys of the side 2
8154 // 2. On the side 2, find the links most co-directed with the correspondent
8155 // links of the free border
8156 // -------------------------------------------------------------------------
8158 // 1. Since sewing may break if there are volumes to split on the side 2,
8159 // we wont move nodes but just compute new coordinates for them
8160 typedef map<const SMDS_MeshNode*, gp_XYZ> TNodeXYZMap;
8161 TNodeXYZMap nBordXYZ;
8162 list< const SMDS_MeshNode* >& bordNodes = nSide[ 0 ];
8163 list< const SMDS_MeshNode* >::iterator nBordIt;
8165 gp_XYZ Pb1( theBordFirstNode->X(), theBordFirstNode->Y(), theBordFirstNode->Z() );
8166 gp_XYZ Pb2( theBordLastNode->X(), theBordLastNode->Y(), theBordLastNode->Z() );
8167 gp_XYZ Ps1( theSideFirstNode->X(), theSideFirstNode->Y(), theSideFirstNode->Z() );
8168 gp_XYZ Ps2( theSideSecondNode->X(), theSideSecondNode->Y(), theSideSecondNode->Z() );
8169 double tol2 = 1.e-8;
8170 gp_Vec Vbs1( Pb1 - Ps1 ),Vbs2( Pb2 - Ps2 );
8171 if ( Vbs1.SquareMagnitude() > tol2 || Vbs2.SquareMagnitude() > tol2 ) {
8172 // Need node movement.
8174 // find X and Z axes to create trsf
8175 gp_Vec Zb( Pb1 - Pb2 ), Zs( Ps1 - Ps2 );
8177 if ( X.SquareMagnitude() <= gp::Resolution() * gp::Resolution() )
8179 X = gp_Ax2( gp::Origin(), Zb ).XDirection();
8182 gp_Ax3 toBordAx( Pb1, Zb, X );
8183 gp_Ax3 fromSideAx( Ps1, Zs, X );
8184 gp_Ax3 toGlobalAx( gp::Origin(), gp::DZ(), gp::DX() );
8186 gp_Trsf toBordSys, fromSide2Sys;
8187 toBordSys.SetTransformation( toBordAx );
8188 fromSide2Sys.SetTransformation( fromSideAx, toGlobalAx );
8189 fromSide2Sys.SetScaleFactor( Zs.Magnitude() / Zb.Magnitude() );
8192 for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
8193 const SMDS_MeshNode* n = *nBordIt;
8194 gp_XYZ xyz( n->X(),n->Y(),n->Z() );
8195 toBordSys.Transforms( xyz );
8196 fromSide2Sys.Transforms( xyz );
8197 nBordXYZ.insert( TNodeXYZMap::value_type( n, xyz ));
8201 // just insert nodes XYZ in the nBordXYZ map
8202 for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
8203 const SMDS_MeshNode* n = *nBordIt;
8204 nBordXYZ.insert( TNodeXYZMap::value_type( n, gp_XYZ( n->X(),n->Y(),n->Z() )));
8208 // 2. On the side 2, find the links most co-directed with the correspondent
8209 // links of the free border
8211 list< const SMDS_MeshElement* >& sideElems = eSide[ 1 ];
8212 list< const SMDS_MeshNode* >& sideNodes = nSide[ 1 ];
8213 sideNodes.push_back( theSideFirstNode );
8215 bool hasVolumes = false;
8216 LinkID_Gen aLinkID_Gen( GetMeshDS() );
8217 set<long> foundSideLinkIDs, checkedLinkIDs;
8218 SMDS_VolumeTool volume;
8219 //const SMDS_MeshNode* faceNodes[ 4 ];
8221 const SMDS_MeshNode* sideNode;
8222 const SMDS_MeshElement* sideElem;
8223 const SMDS_MeshNode* prevSideNode = theSideFirstNode;
8224 const SMDS_MeshNode* prevBordNode = theBordFirstNode;
8225 nBordIt = bordNodes.begin();
8227 // border node position and border link direction to compare with
8228 gp_XYZ bordPos = nBordXYZ[ *nBordIt ];
8229 gp_XYZ bordDir = bordPos - nBordXYZ[ prevBordNode ];
8230 // choose next side node by link direction or by closeness to
8231 // the current border node:
8232 bool searchByDir = ( *nBordIt != theBordLastNode );
8234 // find the next node on the Side 2
8236 double maxDot = -DBL_MAX, minDist = DBL_MAX;
8238 checkedLinkIDs.clear();
8239 gp_XYZ prevXYZ( prevSideNode->X(), prevSideNode->Y(), prevSideNode->Z() );
8241 // loop on inverse elements of current node (prevSideNode) on the Side 2
8242 SMDS_ElemIteratorPtr invElemIt = prevSideNode->GetInverseElementIterator();
8243 while ( invElemIt->more() )
8245 const SMDS_MeshElement* elem = invElemIt->next();
8246 // prepare data for a loop on links coming to prevSideNode, of a face or a volume
8247 int iPrevNode, iNode = 0, nbNodes = elem->NbNodes();
8248 vector< const SMDS_MeshNode* > faceNodes( nbNodes, (const SMDS_MeshNode*)0 );
8249 bool isVolume = volume.Set( elem );
8250 const SMDS_MeshNode** nodes = isVolume ? volume.GetNodes() : & faceNodes[0];
8251 if ( isVolume ) // --volume
8253 else if ( elem->GetType()==SMDSAbs_Face ) { // --face
8254 // retrieve all face nodes and find iPrevNode - an index of the prevSideNode
8255 if(elem->IsQuadratic()) {
8256 const SMDS_VtkFace* F =
8257 dynamic_cast<const SMDS_VtkFace*>(elem);
8258 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8259 // use special nodes iterator
8260 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8261 while( anIter->more() ) {
8262 nodes[ iNode ] = cast2Node(anIter->next());
8263 if ( nodes[ iNode++ ] == prevSideNode )
8264 iPrevNode = iNode - 1;
8268 SMDS_ElemIteratorPtr nIt = elem->nodesIterator();
8269 while ( nIt->more() ) {
8270 nodes[ iNode ] = cast2Node( nIt->next() );
8271 if ( nodes[ iNode++ ] == prevSideNode )
8272 iPrevNode = iNode - 1;
8275 // there are 2 links to check
8280 // loop on links, to be precise, on the second node of links
8281 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
8282 const SMDS_MeshNode* n = nodes[ iNode ];
8284 if ( !volume.IsLinked( n, prevSideNode ))
8288 if ( iNode ) // a node before prevSideNode
8289 n = nodes[ iPrevNode == 0 ? elem->NbNodes() - 1 : iPrevNode - 1 ];
8290 else // a node after prevSideNode
8291 n = nodes[ iPrevNode + 1 == elem->NbNodes() ? 0 : iPrevNode + 1 ];
8293 // check if this link was already used
8294 long iLink = aLinkID_Gen.GetLinkID( prevSideNode, n );
8295 bool isJustChecked = !checkedLinkIDs.insert( iLink ).second;
8296 if (!isJustChecked &&
8297 foundSideLinkIDs.find( iLink ) == foundSideLinkIDs.end() )
8299 // test a link geometrically
8300 gp_XYZ nextXYZ ( n->X(), n->Y(), n->Z() );
8301 bool linkIsBetter = false;
8302 double dot = 0.0, dist = 0.0;
8303 if ( searchByDir ) { // choose most co-directed link
8304 dot = bordDir * ( nextXYZ - prevXYZ ).Normalized();
8305 linkIsBetter = ( dot > maxDot );
8307 else { // choose link with the node closest to bordPos
8308 dist = ( nextXYZ - bordPos ).SquareModulus();
8309 linkIsBetter = ( dist < minDist );
8311 if ( linkIsBetter ) {
8320 } // loop on inverse elements of prevSideNode
8323 MESSAGE(" Cant find path by links of the Side 2 ");
8324 return SEW_BAD_SIDE_NODES;
8326 sideNodes.push_back( sideNode );
8327 sideElems.push_back( sideElem );
8328 foundSideLinkIDs.insert ( linkID );
8329 prevSideNode = sideNode;
8331 if ( *nBordIt == theBordLastNode )
8332 searchByDir = false;
8334 // find the next border link to compare with
8335 gp_XYZ sidePos( sideNode->X(), sideNode->Y(), sideNode->Z() );
8336 searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8337 // move to next border node if sideNode is before forward border node (bordPos)
8338 while ( *nBordIt != theBordLastNode && !searchByDir ) {
8339 prevBordNode = *nBordIt;
8341 bordPos = nBordXYZ[ *nBordIt ];
8342 bordDir = bordPos - nBordXYZ[ prevBordNode ];
8343 searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8347 while ( sideNode != theSideSecondNode );
8349 if ( hasVolumes && sideNodes.size () != bordNodes.size() && !toCreatePolyedrs) {
8350 MESSAGE("VOLUME SPLITTING IS FORBIDDEN");
8351 return SEW_VOLUMES_TO_SPLIT; // volume splitting is forbidden
8353 } // end nodes search on the side 2
8355 // ============================
8356 // sew the border to the side 2
8357 // ============================
8359 int nbNodes[] = { nSide[0].size(), nSide[1].size() };
8360 int maxNbNodes = Max( nbNodes[0], nbNodes[1] );
8362 TListOfListOfNodes nodeGroupsToMerge;
8363 if ( nbNodes[0] == nbNodes[1] ||
8364 ( theSideIsFreeBorder && !theSideThirdNode)) {
8366 // all nodes are to be merged
8368 for (nIt[0] = nSide[0].begin(), nIt[1] = nSide[1].begin();
8369 nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end();
8370 nIt[0]++, nIt[1]++ )
8372 nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8373 nodeGroupsToMerge.back().push_back( *nIt[1] ); // to keep
8374 nodeGroupsToMerge.back().push_back( *nIt[0] ); // to remove
8379 // insert new nodes into the border and the side to get equal nb of segments
8381 // get normalized parameters of nodes on the borders
8382 //double param[ 2 ][ maxNbNodes ];
8384 param[0] = new double [ maxNbNodes ];
8385 param[1] = new double [ maxNbNodes ];
8387 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8388 list< const SMDS_MeshNode* >& nodes = nSide[ iBord ];
8389 list< const SMDS_MeshNode* >::iterator nIt = nodes.begin();
8390 const SMDS_MeshNode* nPrev = *nIt;
8391 double bordLength = 0;
8392 for ( iNode = 0; nIt != nodes.end(); nIt++, iNode++ ) { // loop on border nodes
8393 const SMDS_MeshNode* nCur = *nIt;
8394 gp_XYZ segment (nCur->X() - nPrev->X(),
8395 nCur->Y() - nPrev->Y(),
8396 nCur->Z() - nPrev->Z());
8397 double segmentLen = segment.Modulus();
8398 bordLength += segmentLen;
8399 param[ iBord ][ iNode ] = bordLength;
8402 // normalize within [0,1]
8403 for ( iNode = 0; iNode < nbNodes[ iBord ]; iNode++ ) {
8404 param[ iBord ][ iNode ] /= bordLength;
8408 // loop on border segments
8409 const SMDS_MeshNode *nPrev[ 2 ] = { 0, 0 };
8410 int i[ 2 ] = { 0, 0 };
8411 nIt[0] = nSide[0].begin(); eIt[0] = eSide[0].begin();
8412 nIt[1] = nSide[1].begin(); eIt[1] = eSide[1].begin();
8414 TElemOfNodeListMap insertMap;
8415 TElemOfNodeListMap::iterator insertMapIt;
8417 // key: elem to insert nodes into
8418 // value: 2 nodes to insert between + nodes to be inserted
8420 bool next[ 2 ] = { false, false };
8422 // find min adjacent segment length after sewing
8423 double nextParam = 10., prevParam = 0;
8424 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8425 if ( i[ iBord ] + 1 < nbNodes[ iBord ])
8426 nextParam = Min( nextParam, param[iBord][ i[iBord] + 1 ]);
8427 if ( i[ iBord ] > 0 )
8428 prevParam = Max( prevParam, param[iBord][ i[iBord] - 1 ]);
8430 double minParam = Min( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8431 double maxParam = Max( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8432 double minSegLen = Min( nextParam - minParam, maxParam - prevParam );
8434 // choose to insert or to merge nodes
8435 double du = param[ 1 ][ i[1] ] - param[ 0 ][ i[0] ];
8436 if ( Abs( du ) <= minSegLen * 0.2 ) {
8439 nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8440 const SMDS_MeshNode* n0 = *nIt[0];
8441 const SMDS_MeshNode* n1 = *nIt[1];
8442 nodeGroupsToMerge.back().push_back( n1 );
8443 nodeGroupsToMerge.back().push_back( n0 );
8444 // position of node of the border changes due to merge
8445 param[ 0 ][ i[0] ] += du;
8446 // move n1 for the sake of elem shape evaluation during insertion.
8447 // n1 will be removed by MergeNodes() anyway
8448 const_cast<SMDS_MeshNode*>( n0 )->setXYZ( n1->X(), n1->Y(), n1->Z() );
8449 next[0] = next[1] = true;
8454 int intoBord = ( du < 0 ) ? 0 : 1;
8455 const SMDS_MeshElement* elem = *eIt[ intoBord ];
8456 const SMDS_MeshNode* n1 = nPrev[ intoBord ];
8457 const SMDS_MeshNode* n2 = *nIt[ intoBord ];
8458 const SMDS_MeshNode* nIns = *nIt[ 1 - intoBord ];
8459 if ( intoBord == 1 ) {
8460 // move node of the border to be on a link of elem of the side
8461 gp_XYZ p1 (n1->X(), n1->Y(), n1->Z());
8462 gp_XYZ p2 (n2->X(), n2->Y(), n2->Z());
8463 double ratio = du / ( param[ 1 ][ i[1] ] - param[ 1 ][ i[1]-1 ]);
8464 gp_XYZ p = p2 * ( 1 - ratio ) + p1 * ratio;
8465 GetMeshDS()->MoveNode( nIns, p.X(), p.Y(), p.Z() );
8467 insertMapIt = insertMap.find( elem );
8468 bool notFound = ( insertMapIt == insertMap.end() );
8469 bool otherLink = ( !notFound && (*insertMapIt).second.front() != n1 );
8471 // insert into another link of the same element:
8472 // 1. perform insertion into the other link of the elem
8473 list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8474 const SMDS_MeshNode* n12 = nodeList.front(); nodeList.pop_front();
8475 const SMDS_MeshNode* n22 = nodeList.front(); nodeList.pop_front();
8476 InsertNodesIntoLink( elem, n12, n22, nodeList, toCreatePolygons );
8477 // 2. perform insertion into the link of adjacent faces
8479 const SMDS_MeshElement* adjElem = findAdjacentFace( n12, n22, elem );
8481 InsertNodesIntoLink( adjElem, n12, n22, nodeList, toCreatePolygons );
8485 if (toCreatePolyedrs) {
8486 // perform insertion into the links of adjacent volumes
8487 UpdateVolumes(n12, n22, nodeList);
8489 // 3. find an element appeared on n1 and n2 after the insertion
8490 insertMap.erase( elem );
8491 elem = findAdjacentFace( n1, n2, 0 );
8493 if ( notFound || otherLink ) {
8494 // add element and nodes of the side into the insertMap
8495 insertMapIt = insertMap.insert
8496 ( TElemOfNodeListMap::value_type( elem, list<const SMDS_MeshNode*>() )).first;
8497 (*insertMapIt).second.push_back( n1 );
8498 (*insertMapIt).second.push_back( n2 );
8500 // add node to be inserted into elem
8501 (*insertMapIt).second.push_back( nIns );
8502 next[ 1 - intoBord ] = true;
8505 // go to the next segment
8506 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8507 if ( next[ iBord ] ) {
8508 if ( i[ iBord ] != 0 && eIt[ iBord ] != eSide[ iBord ].end())
8510 nPrev[ iBord ] = *nIt[ iBord ];
8511 nIt[ iBord ]++; i[ iBord ]++;
8515 while ( nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end());
8517 // perform insertion of nodes into elements
8519 for (insertMapIt = insertMap.begin();
8520 insertMapIt != insertMap.end();
8523 const SMDS_MeshElement* elem = (*insertMapIt).first;
8524 list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8525 const SMDS_MeshNode* n1 = nodeList.front(); nodeList.pop_front();
8526 const SMDS_MeshNode* n2 = nodeList.front(); nodeList.pop_front();
8528 InsertNodesIntoLink( elem, n1, n2, nodeList, toCreatePolygons );
8530 if ( !theSideIsFreeBorder ) {
8531 // look for and insert nodes into the faces adjacent to elem
8533 const SMDS_MeshElement* adjElem = findAdjacentFace( n1, n2, elem );
8535 InsertNodesIntoLink( adjElem, n1, n2, nodeList, toCreatePolygons );
8540 if (toCreatePolyedrs) {
8541 // perform insertion into the links of adjacent volumes
8542 UpdateVolumes(n1, n2, nodeList);
8548 } // end: insert new nodes
8550 MergeNodes ( nodeGroupsToMerge );
8555 //=======================================================================
8556 //function : InsertNodesIntoLink
8557 //purpose : insert theNodesToInsert into theFace between theBetweenNode1
8558 // and theBetweenNode2 and split theElement
8559 //=======================================================================
8561 void SMESH_MeshEditor::InsertNodesIntoLink(const SMDS_MeshElement* theFace,
8562 const SMDS_MeshNode* theBetweenNode1,
8563 const SMDS_MeshNode* theBetweenNode2,
8564 list<const SMDS_MeshNode*>& theNodesToInsert,
8565 const bool toCreatePoly)
8567 if ( theFace->GetType() != SMDSAbs_Face ) return;
8569 // find indices of 2 link nodes and of the rest nodes
8570 int iNode = 0, il1, il2, i3, i4;
8571 il1 = il2 = i3 = i4 = -1;
8572 //const SMDS_MeshNode* nodes[ theFace->NbNodes() ];
8573 vector<const SMDS_MeshNode*> nodes( theFace->NbNodes() );
8575 if(theFace->IsQuadratic()) {
8576 const SMDS_VtkFace* F =
8577 dynamic_cast<const SMDS_VtkFace*>(theFace);
8578 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8579 // use special nodes iterator
8580 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8581 while( anIter->more() ) {
8582 const SMDS_MeshNode* n = cast2Node(anIter->next());
8583 if ( n == theBetweenNode1 )
8585 else if ( n == theBetweenNode2 )
8591 nodes[ iNode++ ] = n;
8595 SMDS_ElemIteratorPtr nodeIt = theFace->nodesIterator();
8596 while ( nodeIt->more() ) {
8597 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8598 if ( n == theBetweenNode1 )
8600 else if ( n == theBetweenNode2 )
8606 nodes[ iNode++ ] = n;
8609 if ( il1 < 0 || il2 < 0 || i3 < 0 )
8612 // arrange link nodes to go one after another regarding the face orientation
8613 bool reverse = ( Abs( il2 - il1 ) == 1 ? il2 < il1 : il1 < il2 );
8614 list<const SMDS_MeshNode *> aNodesToInsert = theNodesToInsert;
8619 aNodesToInsert.reverse();
8621 // check that not link nodes of a quadrangles are in good order
8622 int nbFaceNodes = theFace->NbNodes();
8623 if ( nbFaceNodes == 4 && i4 - i3 != 1 ) {
8629 if (toCreatePoly || theFace->IsPoly()) {
8632 vector<const SMDS_MeshNode *> poly_nodes (nbFaceNodes + aNodesToInsert.size());
8634 // add nodes of face up to first node of link
8637 if(theFace->IsQuadratic()) {
8638 const SMDS_VtkFace* F =
8639 dynamic_cast<const SMDS_VtkFace*>(theFace);
8640 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8641 // use special nodes iterator
8642 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8643 while( anIter->more() && !isFLN ) {
8644 const SMDS_MeshNode* n = cast2Node(anIter->next());
8645 poly_nodes[iNode++] = n;
8646 if (n == nodes[il1]) {
8650 // add nodes to insert
8651 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8652 for (; nIt != aNodesToInsert.end(); nIt++) {
8653 poly_nodes[iNode++] = *nIt;
8655 // add nodes of face starting from last node of link
8656 while ( anIter->more() ) {
8657 poly_nodes[iNode++] = cast2Node(anIter->next());
8661 SMDS_ElemIteratorPtr nodeIt = theFace->nodesIterator();
8662 while ( nodeIt->more() && !isFLN ) {
8663 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8664 poly_nodes[iNode++] = n;
8665 if (n == nodes[il1]) {
8669 // add nodes to insert
8670 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8671 for (; nIt != aNodesToInsert.end(); nIt++) {
8672 poly_nodes[iNode++] = *nIt;
8674 // add nodes of face starting from last node of link
8675 while ( nodeIt->more() ) {
8676 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8677 poly_nodes[iNode++] = n;
8681 // edit or replace the face
8682 SMESHDS_Mesh *aMesh = GetMeshDS();
8684 if (theFace->IsPoly()) {
8685 aMesh->ChangePolygonNodes(theFace, poly_nodes);
8688 int aShapeId = FindShape( theFace );
8690 SMDS_MeshElement* newElem = aMesh->AddPolygonalFace(poly_nodes);
8691 myLastCreatedElems.Append(newElem);
8692 if ( aShapeId && newElem )
8693 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8695 aMesh->RemoveElement(theFace);
8700 SMESHDS_Mesh *aMesh = GetMeshDS();
8701 if( !theFace->IsQuadratic() ) {
8703 // put aNodesToInsert between theBetweenNode1 and theBetweenNode2
8704 int nbLinkNodes = 2 + aNodesToInsert.size();
8705 //const SMDS_MeshNode* linkNodes[ nbLinkNodes ];
8706 vector<const SMDS_MeshNode*> linkNodes( nbLinkNodes );
8707 linkNodes[ 0 ] = nodes[ il1 ];
8708 linkNodes[ nbLinkNodes - 1 ] = nodes[ il2 ];
8709 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8710 for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
8711 linkNodes[ iNode++ ] = *nIt;
8713 // decide how to split a quadrangle: compare possible variants
8714 // and choose which of splits to be a quadrangle
8715 int i1, i2, iSplit, nbSplits = nbLinkNodes - 1, iBestQuad;
8716 if ( nbFaceNodes == 3 ) {
8717 iBestQuad = nbSplits;
8720 else if ( nbFaceNodes == 4 ) {
8721 SMESH::Controls::NumericalFunctorPtr aCrit( new SMESH::Controls::AspectRatio);
8722 double aBestRate = DBL_MAX;
8723 for ( int iQuad = 0; iQuad < nbSplits; iQuad++ ) {
8725 double aBadRate = 0;
8726 // evaluate elements quality
8727 for ( iSplit = 0; iSplit < nbSplits; iSplit++ ) {
8728 if ( iSplit == iQuad ) {
8729 SMDS_FaceOfNodes quad (linkNodes[ i1++ ],
8733 aBadRate += getBadRate( &quad, aCrit );
8736 SMDS_FaceOfNodes tria (linkNodes[ i1++ ],
8738 nodes[ iSplit < iQuad ? i4 : i3 ]);
8739 aBadRate += getBadRate( &tria, aCrit );
8743 if ( aBadRate < aBestRate ) {
8745 aBestRate = aBadRate;
8750 // create new elements
8751 int aShapeId = FindShape( theFace );
8754 for ( iSplit = 0; iSplit < nbSplits - 1; iSplit++ ) {
8755 SMDS_MeshElement* newElem = 0;
8756 if ( iSplit == iBestQuad )
8757 newElem = aMesh->AddFace (linkNodes[ i1++ ],
8762 newElem = aMesh->AddFace (linkNodes[ i1++ ],
8764 nodes[ iSplit < iBestQuad ? i4 : i3 ]);
8765 myLastCreatedElems.Append(newElem);
8766 if ( aShapeId && newElem )
8767 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8770 // change nodes of theFace
8771 const SMDS_MeshNode* newNodes[ 4 ];
8772 newNodes[ 0 ] = linkNodes[ i1 ];
8773 newNodes[ 1 ] = linkNodes[ i2 ];
8774 newNodes[ 2 ] = nodes[ iSplit >= iBestQuad ? i3 : i4 ];
8775 newNodes[ 3 ] = nodes[ i4 ];
8776 //aMesh->ChangeElementNodes( theFace, newNodes, iSplit == iBestQuad ? 4 : 3 );
8777 const SMDS_MeshElement* newElem = 0;
8778 if (iSplit == iBestQuad)
8779 newElem = aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2], newNodes[3] );
8781 newElem = aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2] );
8782 myLastCreatedElems.Append(newElem);
8783 if ( aShapeId && newElem )
8784 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8785 } // end if(!theFace->IsQuadratic())
8786 else { // theFace is quadratic
8787 // we have to split theFace on simple triangles and one simple quadrangle
8789 int nbshift = tmp*2;
8790 // shift nodes in nodes[] by nbshift
8792 for(i=0; i<nbshift; i++) {
8793 const SMDS_MeshNode* n = nodes[0];
8794 for(j=0; j<nbFaceNodes-1; j++) {
8795 nodes[j] = nodes[j+1];
8797 nodes[nbFaceNodes-1] = n;
8799 il1 = il1 - nbshift;
8800 // now have to insert nodes between n0 and n1 or n1 and n2 (see below)
8801 // n0 n1 n2 n0 n1 n2
8802 // +-----+-----+ +-----+-----+
8811 // create new elements
8812 int aShapeId = FindShape( theFace );
8815 if(nbFaceNodes==6) { // quadratic triangle
8816 SMDS_MeshElement* newElem =
8817 aMesh->AddFace(nodes[3],nodes[4],nodes[5]);
8818 myLastCreatedElems.Append(newElem);
8819 if ( aShapeId && newElem )
8820 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8821 if(theFace->IsMediumNode(nodes[il1])) {
8822 // create quadrangle
8823 newElem = aMesh->AddFace(nodes[0],nodes[1],nodes[3],nodes[5]);
8824 myLastCreatedElems.Append(newElem);
8825 if ( aShapeId && newElem )
8826 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8832 // create quadrangle
8833 newElem = aMesh->AddFace(nodes[1],nodes[2],nodes[3],nodes[5]);
8834 myLastCreatedElems.Append(newElem);
8835 if ( aShapeId && newElem )
8836 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8842 else { // nbFaceNodes==8 - quadratic quadrangle
8843 SMDS_MeshElement* newElem =
8844 aMesh->AddFace(nodes[3],nodes[4],nodes[5]);
8845 myLastCreatedElems.Append(newElem);
8846 if ( aShapeId && newElem )
8847 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8848 newElem = aMesh->AddFace(nodes[5],nodes[6],nodes[7]);
8849 myLastCreatedElems.Append(newElem);
8850 if ( aShapeId && newElem )
8851 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8852 newElem = aMesh->AddFace(nodes[5],nodes[7],nodes[3]);
8853 myLastCreatedElems.Append(newElem);
8854 if ( aShapeId && newElem )
8855 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8856 if(theFace->IsMediumNode(nodes[il1])) {
8857 // create quadrangle
8858 newElem = aMesh->AddFace(nodes[0],nodes[1],nodes[3],nodes[7]);
8859 myLastCreatedElems.Append(newElem);
8860 if ( aShapeId && newElem )
8861 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8867 // create quadrangle
8868 newElem = aMesh->AddFace(nodes[1],nodes[2],nodes[3],nodes[7]);
8869 myLastCreatedElems.Append(newElem);
8870 if ( aShapeId && newElem )
8871 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8877 // create needed triangles using n1,n2,n3 and inserted nodes
8878 int nbn = 2 + aNodesToInsert.size();
8879 //const SMDS_MeshNode* aNodes[nbn];
8880 vector<const SMDS_MeshNode*> aNodes(nbn);
8881 aNodes[0] = nodes[n1];
8882 aNodes[nbn-1] = nodes[n2];
8883 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8884 for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
8885 aNodes[iNode++] = *nIt;
8887 for(i=1; i<nbn; i++) {
8888 SMDS_MeshElement* newElem =
8889 aMesh->AddFace(aNodes[i-1],aNodes[i],nodes[n3]);
8890 myLastCreatedElems.Append(newElem);
8891 if ( aShapeId && newElem )
8892 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8896 aMesh->RemoveElement(theFace);
8899 //=======================================================================
8900 //function : UpdateVolumes
8902 //=======================================================================
8903 void SMESH_MeshEditor::UpdateVolumes (const SMDS_MeshNode* theBetweenNode1,
8904 const SMDS_MeshNode* theBetweenNode2,
8905 list<const SMDS_MeshNode*>& theNodesToInsert)
8907 myLastCreatedElems.Clear();
8908 myLastCreatedNodes.Clear();
8910 SMDS_ElemIteratorPtr invElemIt = theBetweenNode1->GetInverseElementIterator(SMDSAbs_Volume);
8911 while (invElemIt->more()) { // loop on inverse elements of theBetweenNode1
8912 const SMDS_MeshElement* elem = invElemIt->next();
8914 // check, if current volume has link theBetweenNode1 - theBetweenNode2
8915 SMDS_VolumeTool aVolume (elem);
8916 if (!aVolume.IsLinked(theBetweenNode1, theBetweenNode2))
8919 // insert new nodes in all faces of the volume, sharing link theBetweenNode1 - theBetweenNode2
8920 int iface, nbFaces = aVolume.NbFaces();
8921 vector<const SMDS_MeshNode *> poly_nodes;
8922 vector<int> quantities (nbFaces);
8924 for (iface = 0; iface < nbFaces; iface++) {
8925 int nbFaceNodes = aVolume.NbFaceNodes(iface), nbInserted = 0;
8926 // faceNodes will contain (nbFaceNodes + 1) nodes, last = first
8927 const SMDS_MeshNode** faceNodes = aVolume.GetFaceNodes(iface);
8929 for (int inode = 0; inode < nbFaceNodes; inode++) {
8930 poly_nodes.push_back(faceNodes[inode]);
8932 if (nbInserted == 0) {
8933 if (faceNodes[inode] == theBetweenNode1) {
8934 if (faceNodes[inode + 1] == theBetweenNode2) {
8935 nbInserted = theNodesToInsert.size();
8937 // add nodes to insert
8938 list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.begin();
8939 for (; nIt != theNodesToInsert.end(); nIt++) {
8940 poly_nodes.push_back(*nIt);
8944 else if (faceNodes[inode] == theBetweenNode2) {
8945 if (faceNodes[inode + 1] == theBetweenNode1) {
8946 nbInserted = theNodesToInsert.size();
8948 // add nodes to insert in reversed order
8949 list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.end();
8951 for (; nIt != theNodesToInsert.begin(); nIt--) {
8952 poly_nodes.push_back(*nIt);
8954 poly_nodes.push_back(*nIt);
8961 quantities[iface] = nbFaceNodes + nbInserted;
8964 // Replace or update the volume
8965 SMESHDS_Mesh *aMesh = GetMeshDS();
8967 if (elem->IsPoly()) {
8968 aMesh->ChangePolyhedronNodes(elem, poly_nodes, quantities);
8972 int aShapeId = FindShape( elem );
8974 SMDS_MeshElement* newElem =
8975 aMesh->AddPolyhedralVolume(poly_nodes, quantities);
8976 myLastCreatedElems.Append(newElem);
8977 if (aShapeId && newElem)
8978 aMesh->SetMeshElementOnShape(newElem, aShapeId);
8980 aMesh->RemoveElement(elem);
8987 //================================================================================
8989 * \brief Transform any volume into data of SMDSEntity_Polyhedra
8991 //================================================================================
8993 void volumeToPolyhedron( const SMDS_MeshElement* elem,
8994 vector<const SMDS_MeshNode *> & nodes,
8995 vector<int> & nbNodeInFaces )
8998 nbNodeInFaces.clear();
8999 SMDS_VolumeTool vTool ( elem );
9000 for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
9002 const SMDS_MeshNode** fNodes = vTool.GetFaceNodes( iF );
9003 nodes.insert( nodes.end(), fNodes, fNodes + vTool.NbFaceNodes( iF ));
9004 nbNodeInFaces.push_back( vTool.NbFaceNodes( iF ));
9009 //=======================================================================
9011 * \brief Convert elements contained in a submesh to quadratic
9012 * \return int - nb of checked elements
9014 //=======================================================================
9016 int SMESH_MeshEditor::convertElemToQuadratic(SMESHDS_SubMesh * theSm,
9017 SMESH_MesherHelper& theHelper,
9018 const bool theForce3d)
9021 if( !theSm ) return nbElem;
9023 vector<int> nbNodeInFaces;
9024 vector<const SMDS_MeshNode *> nodes;
9025 SMDS_ElemIteratorPtr ElemItr = theSm->GetElements();
9026 while(ElemItr->more())
9029 const SMDS_MeshElement* elem = ElemItr->next();
9030 if( !elem ) continue;
9032 // analyse a necessity of conversion
9033 const SMDSAbs_ElementType aType = elem->GetType();
9034 if ( aType < SMDSAbs_Edge || aType > SMDSAbs_Volume )
9036 const SMDSAbs_EntityType aGeomType = elem->GetEntityType();
9037 bool hasCentralNodes = false;
9038 if ( elem->IsQuadratic() )
9041 switch ( aGeomType ) {
9042 case SMDSEntity_Quad_Triangle:
9043 case SMDSEntity_Quad_Quadrangle:
9044 case SMDSEntity_Quad_Hexa:
9045 alreadyOK = !theHelper.GetIsBiQuadratic(); break;
9047 case SMDSEntity_BiQuad_Triangle:
9048 case SMDSEntity_BiQuad_Quadrangle:
9049 case SMDSEntity_TriQuad_Hexa:
9050 alreadyOK = theHelper.GetIsBiQuadratic();
9051 hasCentralNodes = true;
9056 // take into account already present modium nodes
9058 case SMDSAbs_Volume:
9059 theHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( elem )); break;
9061 theHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( elem )); break;
9063 theHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( elem )); break;
9069 // get elem data needed to re-create it
9071 const int id = elem->GetID();
9072 const int nbNodes = elem->NbCornerNodes();
9073 nodes.assign(elem->begin_nodes(), elem->end_nodes());
9074 if ( aGeomType == SMDSEntity_Polyhedra )
9075 nbNodeInFaces = static_cast<const SMDS_VtkVolume* >( elem )->GetQuantities();
9076 else if ( aGeomType == SMDSEntity_Hexagonal_Prism )
9077 volumeToPolyhedron( elem, nodes, nbNodeInFaces );
9079 // remove a linear element
9080 GetMeshDS()->RemoveFreeElement(elem, theSm, /*fromGroups=*/false);
9082 // remove central nodes of biquadratic elements (biquad->quad convertion)
9083 if ( hasCentralNodes )
9084 for ( size_t i = nbNodes * 2; i < nodes.size(); ++i )
9085 if ( nodes[i]->NbInverseElements() == 0 )
9086 GetMeshDS()->RemoveFreeNode( nodes[i], theSm, /*fromGroups=*/true );
9088 const SMDS_MeshElement* NewElem = 0;
9094 NewElem = theHelper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9102 NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
9105 NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9108 NewElem = theHelper.AddPolygonalFace(nodes, id, theForce3d);
9112 case SMDSAbs_Volume :
9116 case SMDSEntity_Tetra:
9117 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9119 case SMDSEntity_Pyramid:
9120 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], id, theForce3d);
9122 case SMDSEntity_Penta:
9123 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], nodes[5], id, theForce3d);
9125 case SMDSEntity_Hexa:
9126 case SMDSEntity_Quad_Hexa:
9127 case SMDSEntity_TriQuad_Hexa:
9128 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9129 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9131 case SMDSEntity_Hexagonal_Prism:
9133 NewElem = theHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9140 ReplaceElemInGroups( elem, NewElem, GetMeshDS());
9141 if( NewElem && NewElem->getshapeId() < 1 )
9142 theSm->AddElement( NewElem );
9146 //=======================================================================
9147 //function : ConvertToQuadratic
9149 //=======================================================================
9151 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d, const bool theToBiQuad)
9153 SMESHDS_Mesh* meshDS = GetMeshDS();
9155 SMESH_MesherHelper aHelper(*myMesh);
9157 aHelper.SetIsQuadratic( true );
9158 aHelper.SetIsBiQuadratic( theToBiQuad );
9159 aHelper.SetElementsOnShape(true);
9160 aHelper.ToFixNodeParameters( true );
9162 // convert elements assigned to sub-meshes
9163 int nbCheckedElems = 0;
9164 if ( myMesh->HasShapeToMesh() )
9166 if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9168 SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9169 while ( smIt->more() ) {
9170 SMESH_subMesh* sm = smIt->next();
9171 if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() ) {
9172 aHelper.SetSubShape( sm->GetSubShape() );
9173 nbCheckedElems += convertElemToQuadratic(smDS, aHelper, theForce3d);
9179 // convert elements NOT assigned to sub-meshes
9180 int totalNbElems = meshDS->NbEdges() + meshDS->NbFaces() + meshDS->NbVolumes();
9181 if ( nbCheckedElems < totalNbElems ) // not all elements are in sub-meshes
9183 aHelper.SetElementsOnShape(false);
9184 SMESHDS_SubMesh *smDS = 0;
9187 SMDS_EdgeIteratorPtr aEdgeItr = meshDS->edgesIterator();
9188 while( aEdgeItr->more() )
9190 const SMDS_MeshEdge* edge = aEdgeItr->next();
9191 if ( !edge->IsQuadratic() )
9193 int id = edge->GetID();
9194 const SMDS_MeshNode* n1 = edge->GetNode(0);
9195 const SMDS_MeshNode* n2 = edge->GetNode(1);
9197 meshDS->RemoveFreeElement(edge, smDS, /*fromGroups=*/false);
9199 const SMDS_MeshEdge* NewEdge = aHelper.AddEdge(n1, n2, id, theForce3d);
9200 ReplaceElemInGroups( edge, NewEdge, GetMeshDS());
9204 aHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( edge ));
9209 SMDS_FaceIteratorPtr aFaceItr = meshDS->facesIterator();
9210 while( aFaceItr->more() )
9212 const SMDS_MeshFace* face = aFaceItr->next();
9213 if ( !face ) continue;
9215 const SMDSAbs_EntityType type = face->GetEntityType();
9219 case SMDSEntity_Quad_Triangle:
9220 case SMDSEntity_Quad_Quadrangle:
9221 alreadyOK = !theToBiQuad;
9222 aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
9224 case SMDSEntity_BiQuad_Triangle:
9225 case SMDSEntity_BiQuad_Quadrangle:
9226 alreadyOK = theToBiQuad;
9227 aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
9229 default: alreadyOK = false;
9234 const int id = face->GetID();
9235 vector<const SMDS_MeshNode *> nodes ( face->begin_nodes(), face->end_nodes());
9237 meshDS->RemoveFreeElement(face, smDS, /*fromGroups=*/false);
9239 SMDS_MeshFace * NewFace = 0;
9242 case SMDSEntity_Triangle:
9243 case SMDSEntity_Quad_Triangle:
9244 case SMDSEntity_BiQuad_Triangle:
9245 NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
9246 if ( nodes.size() == 7 && nodes[6]->NbInverseElements() == 0 ) // rm a central node
9247 GetMeshDS()->RemoveFreeNode( nodes[6], /*sm=*/0, /*fromGroups=*/true );
9250 case SMDSEntity_Quadrangle:
9251 case SMDSEntity_Quad_Quadrangle:
9252 case SMDSEntity_BiQuad_Quadrangle:
9253 NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9254 if ( nodes.size() == 9 && nodes[8]->NbInverseElements() == 0 ) // rm a central node
9255 GetMeshDS()->RemoveFreeNode( nodes[8], /*sm=*/0, /*fromGroups=*/true );
9259 NewFace = aHelper.AddPolygonalFace(nodes, id, theForce3d);
9261 ReplaceElemInGroups( face, NewFace, GetMeshDS());
9265 vector<int> nbNodeInFaces;
9266 SMDS_VolumeIteratorPtr aVolumeItr = meshDS->volumesIterator();
9267 while(aVolumeItr->more())
9269 const SMDS_MeshVolume* volume = aVolumeItr->next();
9270 if ( !volume ) continue;
9272 const SMDSAbs_EntityType type = volume->GetEntityType();
9273 if ( volume->IsQuadratic() )
9278 case SMDSEntity_Quad_Hexa: alreadyOK = !theToBiQuad; break;
9279 case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; break;
9280 default: alreadyOK = true;
9284 aHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( volume ));
9288 const int id = volume->GetID();
9289 vector<const SMDS_MeshNode *> nodes (volume->begin_nodes(), volume->end_nodes());
9290 if ( type == SMDSEntity_Polyhedra )
9291 nbNodeInFaces = static_cast<const SMDS_VtkVolume* >(volume)->GetQuantities();
9292 else if ( type == SMDSEntity_Hexagonal_Prism )
9293 volumeToPolyhedron( volume, nodes, nbNodeInFaces );
9295 meshDS->RemoveFreeElement(volume, smDS, /*fromGroups=*/false);
9297 SMDS_MeshVolume * NewVolume = 0;
9300 case SMDSEntity_Tetra:
9301 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d );
9303 case SMDSEntity_Hexa:
9304 case SMDSEntity_Quad_Hexa:
9305 case SMDSEntity_TriQuad_Hexa:
9306 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9307 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9308 for ( size_t i = 20; i < nodes.size(); ++i ) // rm central nodes
9309 if ( nodes[i]->NbInverseElements() == 0 )
9310 GetMeshDS()->RemoveFreeNode( nodes[i], /*sm=*/0, /*fromGroups=*/true );
9312 case SMDSEntity_Pyramid:
9313 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9314 nodes[3], nodes[4], id, theForce3d);
9316 case SMDSEntity_Penta:
9317 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9318 nodes[3], nodes[4], nodes[5], id, theForce3d);
9320 case SMDSEntity_Hexagonal_Prism:
9322 NewVolume = aHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9324 ReplaceElemInGroups(volume, NewVolume, meshDS);
9329 { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9330 // aHelper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9331 // aHelper.FixQuadraticElements(myError);
9332 SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
9336 //================================================================================
9338 * \brief Makes given elements quadratic
9339 * \param theForce3d - if true, the medium nodes will be placed in the middle of link
9340 * \param theElements - elements to make quadratic
9342 //================================================================================
9344 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d,
9345 TIDSortedElemSet& theElements,
9346 const bool theToBiQuad)
9348 if ( theElements.empty() ) return;
9350 // we believe that all theElements are of the same type
9351 const SMDSAbs_ElementType elemType = (*theElements.begin())->GetType();
9353 // get all nodes shared by theElements
9354 TIDSortedNodeSet allNodes;
9355 TIDSortedElemSet::iterator eIt = theElements.begin();
9356 for ( ; eIt != theElements.end(); ++eIt )
9357 allNodes.insert( (*eIt)->begin_nodes(), (*eIt)->end_nodes() );
9359 // complete theElements with elements of lower dim whose all nodes are in allNodes
9361 TIDSortedElemSet quadAdjacentElems [ SMDSAbs_NbElementTypes ]; // quadratic adjacent elements
9362 TIDSortedElemSet checkedAdjacentElems [ SMDSAbs_NbElementTypes ];
9363 TIDSortedNodeSet::iterator nIt = allNodes.begin();
9364 for ( ; nIt != allNodes.end(); ++nIt )
9366 const SMDS_MeshNode* n = *nIt;
9367 SMDS_ElemIteratorPtr invIt = n->GetInverseElementIterator();
9368 while ( invIt->more() )
9370 const SMDS_MeshElement* e = invIt->next();
9371 const SMDSAbs_ElementType type = e->GetType();
9372 if ( e->IsQuadratic() )
9374 quadAdjacentElems[ type ].insert( e );
9377 switch ( e->GetEntityType() ) {
9378 case SMDSEntity_Quad_Triangle:
9379 case SMDSEntity_Quad_Quadrangle:
9380 case SMDSEntity_Quad_Hexa: alreadyOK = !theToBiQuad; break;
9381 case SMDSEntity_BiQuad_Triangle:
9382 case SMDSEntity_BiQuad_Quadrangle:
9383 case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; break;
9384 default: alreadyOK = true;
9389 if ( type >= elemType )
9390 continue; // same type or more complex linear element
9392 if ( !checkedAdjacentElems[ type ].insert( e ).second )
9393 continue; // e is already checked
9397 SMDS_NodeIteratorPtr nodeIt = e->nodeIterator();
9398 while ( nodeIt->more() && allIn )
9399 allIn = allNodes.count( nodeIt->next() );
9401 theElements.insert(e );
9405 SMESH_MesherHelper helper(*myMesh);
9406 helper.SetIsQuadratic( true );
9407 helper.SetIsBiQuadratic( theToBiQuad );
9409 // add links of quadratic adjacent elements to the helper
9411 if ( !quadAdjacentElems[SMDSAbs_Edge].empty() )
9412 for ( eIt = quadAdjacentElems[SMDSAbs_Edge].begin();
9413 eIt != quadAdjacentElems[SMDSAbs_Edge].end(); ++eIt )
9415 helper.AddTLinks( static_cast< const SMDS_MeshEdge*> (*eIt) );
9417 if ( !quadAdjacentElems[SMDSAbs_Face].empty() )
9418 for ( eIt = quadAdjacentElems[SMDSAbs_Face].begin();
9419 eIt != quadAdjacentElems[SMDSAbs_Face].end(); ++eIt )
9421 helper.AddTLinks( static_cast< const SMDS_MeshFace*> (*eIt) );
9423 if ( !quadAdjacentElems[SMDSAbs_Volume].empty() )
9424 for ( eIt = quadAdjacentElems[SMDSAbs_Volume].begin();
9425 eIt != quadAdjacentElems[SMDSAbs_Volume].end(); ++eIt )
9427 helper.AddTLinks( static_cast< const SMDS_MeshVolume*> (*eIt) );
9430 // make quadratic (or bi-tri-quadratic) elements instead of linear ones
9432 SMESHDS_Mesh* meshDS = GetMeshDS();
9433 SMESHDS_SubMesh* smDS = 0;
9434 for ( eIt = theElements.begin(); eIt != theElements.end(); ++eIt )
9436 const SMDS_MeshElement* elem = *eIt;
9439 int nbCentralNodes = 0;
9440 switch ( elem->GetEntityType() ) {
9441 // linear convertible
9442 case SMDSEntity_Edge:
9443 case SMDSEntity_Triangle:
9444 case SMDSEntity_Quadrangle:
9445 case SMDSEntity_Tetra:
9446 case SMDSEntity_Pyramid:
9447 case SMDSEntity_Hexa:
9448 case SMDSEntity_Penta: alreadyOK = false; nbCentralNodes = 0; break;
9449 // quadratic that can become bi-quadratic
9450 case SMDSEntity_Quad_Triangle:
9451 case SMDSEntity_Quad_Quadrangle:
9452 case SMDSEntity_Quad_Hexa: alreadyOK =!theToBiQuad; nbCentralNodes = 0; break;
9454 case SMDSEntity_BiQuad_Triangle:
9455 case SMDSEntity_BiQuad_Quadrangle: alreadyOK = theToBiQuad; nbCentralNodes = 1; break;
9456 case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; nbCentralNodes = 7; break;
9458 default: alreadyOK = true;
9460 if ( alreadyOK ) continue;
9462 const SMDSAbs_ElementType type = elem->GetType();
9463 const int id = elem->GetID();
9464 const int nbNodes = elem->NbCornerNodes();
9465 vector<const SMDS_MeshNode *> nodes ( elem->begin_nodes(), elem->end_nodes());
9467 helper.SetSubShape( elem->getshapeId() );
9469 if ( !smDS || !smDS->Contains( elem ))
9470 smDS = meshDS->MeshElements( elem->getshapeId() );
9471 meshDS->RemoveFreeElement(elem, smDS, /*fromGroups=*/false);
9473 SMDS_MeshElement * newElem = 0;
9476 case 4: // cases for most frequently used element types go first (for optimization)
9477 if ( type == SMDSAbs_Volume )
9478 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9480 newElem = helper.AddFace (nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9483 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9484 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9487 newElem = helper.AddFace (nodes[0], nodes[1], nodes[2], id, theForce3d);
9490 newElem = helper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9493 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9494 nodes[4], id, theForce3d);
9497 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9498 nodes[4], nodes[5], id, theForce3d);
9502 ReplaceElemInGroups( elem, newElem, meshDS);
9503 if( newElem && smDS )
9504 smDS->AddElement( newElem );
9506 // remove central nodes
9507 for ( size_t i = nodes.size() - nbCentralNodes; i < nodes.size(); ++i )
9508 if ( nodes[i]->NbInverseElements() == 0 )
9509 meshDS->RemoveFreeNode( nodes[i], smDS, /*fromGroups=*/true );
9511 } // loop on theElements
9514 { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9515 // helper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9516 // helper.FixQuadraticElements( myError );
9517 SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
9521 //=======================================================================
9523 * \brief Convert quadratic elements to linear ones and remove quadratic nodes
9524 * \return int - nb of checked elements
9526 //=======================================================================
9528 int SMESH_MeshEditor::removeQuadElem(SMESHDS_SubMesh * theSm,
9529 SMDS_ElemIteratorPtr theItr,
9530 const int theShapeID)
9533 SMESHDS_Mesh* meshDS = GetMeshDS();
9535 while( theItr->more() )
9537 const SMDS_MeshElement* elem = theItr->next();
9539 if( elem && elem->IsQuadratic())
9541 int id = elem->GetID();
9542 int nbCornerNodes = elem->NbCornerNodes();
9543 SMDSAbs_ElementType aType = elem->GetType();
9545 vector<const SMDS_MeshNode *> nodes( elem->begin_nodes(), elem->end_nodes() );
9547 //remove a quadratic element
9548 if ( !theSm || !theSm->Contains( elem ))
9549 theSm = meshDS->MeshElements( elem->getshapeId() );
9550 meshDS->RemoveFreeElement( elem, theSm, /*fromGroups=*/false );
9552 // remove medium nodes
9553 for ( unsigned i = nbCornerNodes; i < nodes.size(); ++i )
9554 if ( nodes[i]->NbInverseElements() == 0 )
9555 meshDS->RemoveFreeNode( nodes[i], theSm );
9557 // add a linear element
9558 nodes.resize( nbCornerNodes );
9559 SMDS_MeshElement * newElem = AddElement( nodes, aType, false, id );
9560 ReplaceElemInGroups(elem, newElem, meshDS);
9561 if( theSm && newElem )
9562 theSm->AddElement( newElem );
9568 //=======================================================================
9569 //function : ConvertFromQuadratic
9571 //=======================================================================
9573 bool SMESH_MeshEditor::ConvertFromQuadratic()
9575 int nbCheckedElems = 0;
9576 if ( myMesh->HasShapeToMesh() )
9578 if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9580 SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9581 while ( smIt->more() ) {
9582 SMESH_subMesh* sm = smIt->next();
9583 if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() )
9584 nbCheckedElems += removeQuadElem( smDS, smDS->GetElements(), sm->GetId() );
9590 GetMeshDS()->NbEdges() + GetMeshDS()->NbFaces() + GetMeshDS()->NbVolumes();
9591 if ( nbCheckedElems < totalNbElems ) // not all elements are in submeshes
9593 SMESHDS_SubMesh *aSM = 0;
9594 removeQuadElem( aSM, GetMeshDS()->elementsIterator(), 0 );
9602 //================================================================================
9604 * \brief Return true if all medium nodes of the element are in the node set
9606 //================================================================================
9608 bool allMediumNodesIn(const SMDS_MeshElement* elem, TIDSortedNodeSet& nodeSet )
9610 for ( int i = elem->NbCornerNodes(); i < elem->NbNodes(); ++i )
9611 if ( !nodeSet.count( elem->GetNode(i) ))
9617 //================================================================================
9619 * \brief Makes given elements linear
9621 //================================================================================
9623 void SMESH_MeshEditor::ConvertFromQuadratic(TIDSortedElemSet& theElements)
9625 if ( theElements.empty() ) return;
9627 // collect IDs of medium nodes of theElements; some of these nodes will be removed
9628 set<int> mediumNodeIDs;
9629 TIDSortedElemSet::iterator eIt = theElements.begin();
9630 for ( ; eIt != theElements.end(); ++eIt )
9632 const SMDS_MeshElement* e = *eIt;
9633 for ( int i = e->NbCornerNodes(); i < e->NbNodes(); ++i )
9634 mediumNodeIDs.insert( e->GetNode(i)->GetID() );
9637 // replace given elements by linear ones
9638 SMDS_ElemIteratorPtr elemIt = elemSetIterator( theElements );
9639 removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9641 // we need to convert remaining elements whose all medium nodes are in mediumNodeIDs
9642 // except those elements sharing medium nodes of quadratic element whose medium nodes
9643 // are not all in mediumNodeIDs
9645 // get remaining medium nodes
9646 TIDSortedNodeSet mediumNodes;
9647 set<int>::iterator nIdsIt = mediumNodeIDs.begin();
9648 for ( ; nIdsIt != mediumNodeIDs.end(); ++nIdsIt )
9649 if ( const SMDS_MeshNode* n = GetMeshDS()->FindNode( *nIdsIt ))
9650 mediumNodes.insert( mediumNodes.end(), n );
9652 // find more quadratic elements to convert
9653 TIDSortedElemSet moreElemsToConvert;
9654 TIDSortedNodeSet::iterator nIt = mediumNodes.begin();
9655 for ( ; nIt != mediumNodes.end(); ++nIt )
9657 SMDS_ElemIteratorPtr invIt = (*nIt)->GetInverseElementIterator();
9658 while ( invIt->more() )
9660 const SMDS_MeshElement* e = invIt->next();
9661 if ( e->IsQuadratic() && allMediumNodesIn( e, mediumNodes ))
9663 // find a more complex element including e and
9664 // whose medium nodes are not in mediumNodes
9665 bool complexFound = false;
9666 for ( int type = e->GetType() + 1; type < SMDSAbs_0DElement; ++type )
9668 SMDS_ElemIteratorPtr invIt2 =
9669 (*nIt)->GetInverseElementIterator( SMDSAbs_ElementType( type ));
9670 while ( invIt2->more() )
9672 const SMDS_MeshElement* eComplex = invIt2->next();
9673 if ( eComplex->IsQuadratic() && !allMediumNodesIn( eComplex, mediumNodes))
9675 int nbCommonNodes = SMESH_MeshAlgos::GetCommonNodes( e, eComplex ).size();
9676 if ( nbCommonNodes == e->NbNodes())
9678 complexFound = true;
9679 type = SMDSAbs_NbElementTypes; // to quit from the outer loop
9685 if ( !complexFound )
9686 moreElemsToConvert.insert( e );
9690 elemIt = elemSetIterator( moreElemsToConvert );
9691 removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9694 //=======================================================================
9695 //function : SewSideElements
9697 //=======================================================================
9699 SMESH_MeshEditor::Sew_Error
9700 SMESH_MeshEditor::SewSideElements (TIDSortedElemSet& theSide1,
9701 TIDSortedElemSet& theSide2,
9702 const SMDS_MeshNode* theFirstNode1,
9703 const SMDS_MeshNode* theFirstNode2,
9704 const SMDS_MeshNode* theSecondNode1,
9705 const SMDS_MeshNode* theSecondNode2)
9707 myLastCreatedElems.Clear();
9708 myLastCreatedNodes.Clear();
9710 MESSAGE ("::::SewSideElements()");
9711 if ( theSide1.size() != theSide2.size() )
9712 return SEW_DIFF_NB_OF_ELEMENTS;
9714 Sew_Error aResult = SEW_OK;
9716 // 1. Build set of faces representing each side
9717 // 2. Find which nodes of the side 1 to merge with ones on the side 2
9718 // 3. Replace nodes in elements of the side 1 and remove replaced nodes
9720 // =======================================================================
9721 // 1. Build set of faces representing each side:
9722 // =======================================================================
9723 // a. build set of nodes belonging to faces
9724 // b. complete set of faces: find missing faces whose nodes are in set of nodes
9725 // c. create temporary faces representing side of volumes if correspondent
9726 // face does not exist
9728 SMESHDS_Mesh* aMesh = GetMeshDS();
9729 // TODO algoritm not OK with vtkUnstructuredGrid: 2 meshes can't share nodes
9730 //SMDS_Mesh aTmpFacesMesh; // try to use the same mesh
9731 TIDSortedElemSet faceSet1, faceSet2;
9732 set<const SMDS_MeshElement*> volSet1, volSet2;
9733 set<const SMDS_MeshNode*> nodeSet1, nodeSet2;
9734 TIDSortedElemSet * faceSetPtr[] = { &faceSet1, &faceSet2 };
9735 set<const SMDS_MeshElement*> * volSetPtr[] = { &volSet1, &volSet2 };
9736 set<const SMDS_MeshNode*> * nodeSetPtr[] = { &nodeSet1, &nodeSet2 };
9737 TIDSortedElemSet * elemSetPtr[] = { &theSide1, &theSide2 };
9738 int iSide, iFace, iNode;
9740 list<const SMDS_MeshElement* > tempFaceList;
9741 for ( iSide = 0; iSide < 2; iSide++ ) {
9742 set<const SMDS_MeshNode*> * nodeSet = nodeSetPtr[ iSide ];
9743 TIDSortedElemSet * elemSet = elemSetPtr[ iSide ];
9744 TIDSortedElemSet * faceSet = faceSetPtr[ iSide ];
9745 set<const SMDS_MeshElement*> * volSet = volSetPtr [ iSide ];
9746 set<const SMDS_MeshElement*>::iterator vIt;
9747 TIDSortedElemSet::iterator eIt;
9748 set<const SMDS_MeshNode*>::iterator nIt;
9750 // check that given nodes belong to given elements
9751 const SMDS_MeshNode* n1 = ( iSide == 0 ) ? theFirstNode1 : theFirstNode2;
9752 const SMDS_MeshNode* n2 = ( iSide == 0 ) ? theSecondNode1 : theSecondNode2;
9753 int firstIndex = -1, secondIndex = -1;
9754 for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9755 const SMDS_MeshElement* elem = *eIt;
9756 if ( firstIndex < 0 ) firstIndex = elem->GetNodeIndex( n1 );
9757 if ( secondIndex < 0 ) secondIndex = elem->GetNodeIndex( n2 );
9758 if ( firstIndex > -1 && secondIndex > -1 ) break;
9760 if ( firstIndex < 0 || secondIndex < 0 ) {
9761 // we can simply return until temporary faces created
9762 return (iSide == 0 ) ? SEW_BAD_SIDE1_NODES : SEW_BAD_SIDE2_NODES;
9765 // -----------------------------------------------------------
9766 // 1a. Collect nodes of existing faces
9767 // and build set of face nodes in order to detect missing
9768 // faces corresponding to sides of volumes
9769 // -----------------------------------------------------------
9771 set< set <const SMDS_MeshNode*> > setOfFaceNodeSet;
9773 // loop on the given element of a side
9774 for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9775 //const SMDS_MeshElement* elem = *eIt;
9776 const SMDS_MeshElement* elem = *eIt;
9777 if ( elem->GetType() == SMDSAbs_Face ) {
9778 faceSet->insert( elem );
9779 set <const SMDS_MeshNode*> faceNodeSet;
9780 SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
9781 while ( nodeIt->more() ) {
9782 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9783 nodeSet->insert( n );
9784 faceNodeSet.insert( n );
9786 setOfFaceNodeSet.insert( faceNodeSet );
9788 else if ( elem->GetType() == SMDSAbs_Volume )
9789 volSet->insert( elem );
9791 // ------------------------------------------------------------------------------
9792 // 1b. Complete set of faces: find missing faces whose nodes are in set of nodes
9793 // ------------------------------------------------------------------------------
9795 for ( nIt = nodeSet->begin(); nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
9796 SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
9797 while ( fIt->more() ) { // loop on faces sharing a node
9798 const SMDS_MeshElement* f = fIt->next();
9799 if ( faceSet->find( f ) == faceSet->end() ) {
9800 // check if all nodes are in nodeSet and
9801 // complete setOfFaceNodeSet if they are
9802 set <const SMDS_MeshNode*> faceNodeSet;
9803 SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
9804 bool allInSet = true;
9805 while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
9806 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9807 if ( nodeSet->find( n ) == nodeSet->end() )
9810 faceNodeSet.insert( n );
9813 faceSet->insert( f );
9814 setOfFaceNodeSet.insert( faceNodeSet );
9820 // -------------------------------------------------------------------------
9821 // 1c. Create temporary faces representing sides of volumes if correspondent
9822 // face does not exist
9823 // -------------------------------------------------------------------------
9825 if ( !volSet->empty() ) {
9826 //int nodeSetSize = nodeSet->size();
9828 // loop on given volumes
9829 for ( vIt = volSet->begin(); vIt != volSet->end(); vIt++ ) {
9830 SMDS_VolumeTool vol (*vIt);
9831 // loop on volume faces: find free faces
9832 // --------------------------------------
9833 list<const SMDS_MeshElement* > freeFaceList;
9834 for ( iFace = 0; iFace < vol.NbFaces(); iFace++ ) {
9835 if ( !vol.IsFreeFace( iFace ))
9837 // check if there is already a face with same nodes in a face set
9838 const SMDS_MeshElement* aFreeFace = 0;
9839 const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iFace );
9840 int nbNodes = vol.NbFaceNodes( iFace );
9841 set <const SMDS_MeshNode*> faceNodeSet;
9842 vol.GetFaceNodes( iFace, faceNodeSet );
9843 bool isNewFace = setOfFaceNodeSet.insert( faceNodeSet ).second;
9845 // no such a face is given but it still can exist, check it
9846 vector<const SMDS_MeshNode *> nodes ( fNodes, fNodes + nbNodes);
9847 aFreeFace = aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false );
9850 // create a temporary face
9851 if ( nbNodes == 3 ) {
9852 //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2] );
9853 aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2] );
9855 else if ( nbNodes == 4 ) {
9856 //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
9857 aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
9860 vector<const SMDS_MeshNode *> poly_nodes ( fNodes, & fNodes[nbNodes]);
9861 //aFreeFace = aTmpFacesMesh.AddPolygonalFace(poly_nodes);
9862 aFreeFace = aMesh->AddPolygonalFace(poly_nodes);
9865 tempFaceList.push_back( aFreeFace );
9869 freeFaceList.push_back( aFreeFace );
9871 } // loop on faces of a volume
9873 // choose one of several free faces of a volume
9874 // --------------------------------------------
9875 if ( freeFaceList.size() > 1 ) {
9876 // choose a face having max nb of nodes shared by other elems of a side
9877 int maxNbNodes = -1;
9878 list<const SMDS_MeshElement* >::iterator fIt = freeFaceList.begin();
9879 while ( fIt != freeFaceList.end() ) { // loop on free faces
9880 int nbSharedNodes = 0;
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 SMDS_ElemIteratorPtr invElemIt = n->GetInverseElementIterator();
9886 while ( invElemIt->more() ) {
9887 const SMDS_MeshElement* e = invElemIt->next();
9888 nbSharedNodes += faceSet->count( e );
9889 nbSharedNodes += elemSet->count( e );
9892 if ( nbSharedNodes > maxNbNodes ) {
9893 maxNbNodes = nbSharedNodes;
9894 freeFaceList.erase( freeFaceList.begin(), fIt++ );
9896 else if ( nbSharedNodes == maxNbNodes ) {
9900 freeFaceList.erase( fIt++ ); // here fIt++ occurs before erase
9903 if ( freeFaceList.size() > 1 )
9905 // could not choose one face, use another way
9906 // choose a face most close to the bary center of the opposite side
9907 gp_XYZ aBC( 0., 0., 0. );
9908 set <const SMDS_MeshNode*> addedNodes;
9909 TIDSortedElemSet * elemSet2 = elemSetPtr[ 1 - iSide ];
9910 eIt = elemSet2->begin();
9911 for ( eIt = elemSet2->begin(); eIt != elemSet2->end(); eIt++ ) {
9912 SMDS_ElemIteratorPtr nodeIt = (*eIt)->nodesIterator();
9913 while ( nodeIt->more() ) { // loop on free face nodes
9914 const SMDS_MeshNode* n =
9915 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9916 if ( addedNodes.insert( n ).second )
9917 aBC += gp_XYZ( n->X(),n->Y(),n->Z() );
9920 aBC /= addedNodes.size();
9921 double minDist = DBL_MAX;
9922 fIt = freeFaceList.begin();
9923 while ( fIt != freeFaceList.end() ) { // loop on free faces
9925 SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
9926 while ( nodeIt->more() ) { // loop on free face nodes
9927 const SMDS_MeshNode* n =
9928 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9929 gp_XYZ p( n->X(),n->Y(),n->Z() );
9930 dist += ( aBC - p ).SquareModulus();
9932 if ( dist < minDist ) {
9934 freeFaceList.erase( freeFaceList.begin(), fIt++ );
9937 fIt = freeFaceList.erase( fIt++ );
9940 } // choose one of several free faces of a volume
9942 if ( freeFaceList.size() == 1 ) {
9943 const SMDS_MeshElement* aFreeFace = freeFaceList.front();
9944 faceSet->insert( aFreeFace );
9945 // complete a node set with nodes of a found free face
9946 // for ( iNode = 0; iNode < ; iNode++ )
9947 // nodeSet->insert( fNodes[ iNode ] );
9950 } // loop on volumes of a side
9952 // // complete a set of faces if new nodes in a nodeSet appeared
9953 // // ----------------------------------------------------------
9954 // if ( nodeSetSize != nodeSet->size() ) {
9955 // for ( ; nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
9956 // SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
9957 // while ( fIt->more() ) { // loop on faces sharing a node
9958 // const SMDS_MeshElement* f = fIt->next();
9959 // if ( faceSet->find( f ) == faceSet->end() ) {
9960 // // check if all nodes are in nodeSet and
9961 // // complete setOfFaceNodeSet if they are
9962 // set <const SMDS_MeshNode*> faceNodeSet;
9963 // SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
9964 // bool allInSet = true;
9965 // while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
9966 // const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9967 // if ( nodeSet->find( n ) == nodeSet->end() )
9968 // allInSet = false;
9970 // faceNodeSet.insert( n );
9972 // if ( allInSet ) {
9973 // faceSet->insert( f );
9974 // setOfFaceNodeSet.insert( faceNodeSet );
9980 } // Create temporary faces, if there are volumes given
9983 if ( faceSet1.size() != faceSet2.size() ) {
9984 // delete temporary faces: they are in reverseElements of actual nodes
9985 // SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
9986 // while ( tmpFaceIt->more() )
9987 // aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
9988 // list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
9989 // for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
9990 // aMesh->RemoveElement(*tmpFaceIt);
9991 MESSAGE("Diff nb of faces");
9992 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9995 // ============================================================
9996 // 2. Find nodes to merge:
9997 // bind a node to remove to a node to put instead
9998 // ============================================================
10000 TNodeNodeMap nReplaceMap; // bind a node to remove to a node to put instead
10001 if ( theFirstNode1 != theFirstNode2 )
10002 nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
10003 if ( theSecondNode1 != theSecondNode2 )
10004 nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
10006 LinkID_Gen aLinkID_Gen( GetMeshDS() );
10007 set< long > linkIdSet; // links to process
10008 linkIdSet.insert( aLinkID_Gen.GetLinkID( theFirstNode1, theSecondNode1 ));
10010 typedef pair< const SMDS_MeshNode*, const SMDS_MeshNode* > NLink;
10011 list< NLink > linkList[2];
10012 linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
10013 linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
10014 // loop on links in linkList; find faces by links and append links
10015 // of the found faces to linkList
10016 list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
10017 for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ )
10019 NLink link[] = { *linkIt[0], *linkIt[1] };
10020 long linkID = aLinkID_Gen.GetLinkID( link[0].first, link[0].second );
10021 if ( !linkIdSet.count( linkID ) )
10024 // by links, find faces in the face sets,
10025 // and find indices of link nodes in the found faces;
10026 // in a face set, there is only one or no face sharing a link
10027 // ---------------------------------------------------------------
10029 const SMDS_MeshElement* face[] = { 0, 0 };
10030 vector<const SMDS_MeshNode*> fnodes[2];
10031 int iLinkNode[2][2];
10032 TIDSortedElemSet avoidSet;
10033 for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
10034 const SMDS_MeshNode* n1 = link[iSide].first;
10035 const SMDS_MeshNode* n2 = link[iSide].second;
10036 //cout << "Side " << iSide << " ";
10037 //cout << "L( " << n1->GetID() << ", " << n2->GetID() << " ) " << endl;
10038 // find a face by two link nodes
10039 face[ iSide ] = SMESH_MeshAlgos::FindFaceInSet( n1, n2,
10040 *faceSetPtr[ iSide ], avoidSet,
10041 &iLinkNode[iSide][0],
10042 &iLinkNode[iSide][1] );
10043 if ( face[ iSide ])
10045 //cout << " F " << face[ iSide]->GetID() <<endl;
10046 faceSetPtr[ iSide ]->erase( face[ iSide ]);
10047 // put face nodes to fnodes
10048 if ( face[ iSide ]->IsQuadratic() )
10050 // use interlaced nodes iterator
10051 const SMDS_VtkFace* F = dynamic_cast<const SMDS_VtkFace*>( face[ iSide ]);
10052 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
10053 SMDS_ElemIteratorPtr nIter = F->interlacedNodesElemIterator();
10054 while ( nIter->more() )
10055 fnodes[ iSide ].push_back( cast2Node( nIter->next() ));
10059 fnodes[ iSide ].assign( face[ iSide ]->begin_nodes(),
10060 face[ iSide ]->end_nodes() );
10062 fnodes[ iSide ].push_back( fnodes[ iSide ].front());
10066 // check similarity of elements of the sides
10067 if (aResult == SEW_OK && (( face[0] && !face[1] ) || ( !face[0] && face[1] ))) {
10068 MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
10069 if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
10070 aResult = ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10073 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10075 break; // do not return because it's necessary to remove tmp faces
10078 // set nodes to merge
10079 // -------------------
10081 if ( face[0] && face[1] ) {
10082 const int nbNodes = face[0]->NbNodes();
10083 if ( nbNodes != face[1]->NbNodes() ) {
10084 MESSAGE("Diff nb of face nodes");
10085 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10086 break; // do not return because it s necessary to remove tmp faces
10088 bool reverse[] = { false, false }; // order of nodes in the link
10089 for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
10090 // analyse link orientation in faces
10091 int i1 = iLinkNode[ iSide ][ 0 ];
10092 int i2 = iLinkNode[ iSide ][ 1 ];
10093 reverse[ iSide ] = Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1;
10095 int di1 = reverse[0] ? -1 : +1, i1 = iLinkNode[0][1] + di1;
10096 int di2 = reverse[1] ? -1 : +1, i2 = iLinkNode[1][1] + di2;
10097 for ( int i = nbNodes - 2; i > 0; --i, i1 += di1, i2 += di2 )
10099 nReplaceMap.insert ( make_pair ( fnodes[0][ ( i1 + nbNodes ) % nbNodes ],
10100 fnodes[1][ ( i2 + nbNodes ) % nbNodes ]));
10103 // add other links of the faces to linkList
10104 // -----------------------------------------
10106 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
10107 linkID = aLinkID_Gen.GetLinkID( fnodes[0][iNode], fnodes[0][iNode+1] );
10108 pair< set<long>::iterator, bool > iter_isnew = linkIdSet.insert( linkID );
10109 if ( !iter_isnew.second ) { // already in a set: no need to process
10110 linkIdSet.erase( iter_isnew.first );
10112 else // new in set == encountered for the first time: add
10114 const SMDS_MeshNode* n1 = fnodes[0][ iNode ];
10115 const SMDS_MeshNode* n2 = fnodes[0][ iNode + 1];
10116 linkList[0].push_back ( NLink( n1, n2 ));
10117 linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10122 if ( faceSetPtr[0]->empty() || faceSetPtr[1]->empty() )
10125 } // loop on link lists
10127 if ( aResult == SEW_OK &&
10128 ( //linkIt[0] != linkList[0].end() ||
10129 !faceSetPtr[0]->empty() || !faceSetPtr[1]->empty() )) {
10130 MESSAGE( (linkIt[0] != linkList[0].end()) <<" "<< (faceSetPtr[0]->empty()) <<
10131 " " << (faceSetPtr[1]->empty()));
10132 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10135 // ====================================================================
10136 // 3. Replace nodes in elements of the side 1 and remove replaced nodes
10137 // ====================================================================
10139 // delete temporary faces
10140 // SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
10141 // while ( tmpFaceIt->more() )
10142 // aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
10143 list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
10144 for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
10145 aMesh->RemoveElement(*tmpFaceIt);
10147 if ( aResult != SEW_OK)
10150 list< int > nodeIDsToRemove/*, elemIDsToRemove*/;
10151 // loop on nodes replacement map
10152 TNodeNodeMap::iterator nReplaceMapIt = nReplaceMap.begin(), nnIt;
10153 for ( ; nReplaceMapIt != nReplaceMap.end(); nReplaceMapIt++ )
10154 if ( (*nReplaceMapIt).first != (*nReplaceMapIt).second ) {
10155 const SMDS_MeshNode* nToRemove = (*nReplaceMapIt).first;
10156 nodeIDsToRemove.push_back( nToRemove->GetID() );
10157 // loop on elements sharing nToRemove
10158 SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
10159 while ( invElemIt->more() ) {
10160 const SMDS_MeshElement* e = invElemIt->next();
10161 // get a new suite of nodes: make replacement
10162 int nbReplaced = 0, i = 0, nbNodes = e->NbNodes();
10163 vector< const SMDS_MeshNode*> nodes( nbNodes );
10164 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
10165 while ( nIt->more() ) {
10166 const SMDS_MeshNode* n =
10167 static_cast<const SMDS_MeshNode*>( nIt->next() );
10168 nnIt = nReplaceMap.find( n );
10169 if ( nnIt != nReplaceMap.end() ) {
10171 n = (*nnIt).second;
10175 // if ( nbReplaced == nbNodes && e->GetType() == SMDSAbs_Face )
10176 // elemIDsToRemove.push_back( e->GetID() );
10180 SMDSAbs_ElementType etyp = e->GetType();
10181 SMDS_MeshElement* newElem = this->AddElement(nodes, etyp, false);
10184 myLastCreatedElems.Append(newElem);
10185 AddToSameGroups(newElem, e, aMesh);
10186 int aShapeId = e->getshapeId();
10189 aMesh->SetMeshElementOnShape( newElem, aShapeId );
10192 aMesh->RemoveElement(e);
10197 Remove( nodeIDsToRemove, true );
10202 //================================================================================
10204 * \brief Find corresponding nodes in two sets of faces
10205 * \param theSide1 - first face set
10206 * \param theSide2 - second first face
10207 * \param theFirstNode1 - a boundary node of set 1
10208 * \param theFirstNode2 - a node of set 2 corresponding to theFirstNode1
10209 * \param theSecondNode1 - a boundary node of set 1 linked with theFirstNode1
10210 * \param theSecondNode2 - a node of set 2 corresponding to theSecondNode1
10211 * \param nReplaceMap - output map of corresponding nodes
10212 * \return bool - is a success or not
10214 //================================================================================
10217 //#define DEBUG_MATCHING_NODES
10220 SMESH_MeshEditor::Sew_Error
10221 SMESH_MeshEditor::FindMatchingNodes(set<const SMDS_MeshElement*>& theSide1,
10222 set<const SMDS_MeshElement*>& theSide2,
10223 const SMDS_MeshNode* theFirstNode1,
10224 const SMDS_MeshNode* theFirstNode2,
10225 const SMDS_MeshNode* theSecondNode1,
10226 const SMDS_MeshNode* theSecondNode2,
10227 TNodeNodeMap & nReplaceMap)
10229 set<const SMDS_MeshElement*> * faceSetPtr[] = { &theSide1, &theSide2 };
10231 nReplaceMap.clear();
10232 if ( theFirstNode1 != theFirstNode2 )
10233 nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
10234 if ( theSecondNode1 != theSecondNode2 )
10235 nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
10237 set< SMESH_TLink > linkSet; // set of nodes where order of nodes is ignored
10238 linkSet.insert( SMESH_TLink( theFirstNode1, theSecondNode1 ));
10240 list< NLink > linkList[2];
10241 linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
10242 linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
10244 // loop on links in linkList; find faces by links and append links
10245 // of the found faces to linkList
10246 list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
10247 for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ ) {
10248 NLink link[] = { *linkIt[0], *linkIt[1] };
10249 if ( linkSet.find( link[0] ) == linkSet.end() )
10252 // by links, find faces in the face sets,
10253 // and find indices of link nodes in the found faces;
10254 // in a face set, there is only one or no face sharing a link
10255 // ---------------------------------------------------------------
10257 const SMDS_MeshElement* face[] = { 0, 0 };
10258 list<const SMDS_MeshNode*> notLinkNodes[2];
10259 //bool reverse[] = { false, false }; // order of notLinkNodes
10261 for ( int iSide = 0; iSide < 2; iSide++ ) // loop on 2 sides
10263 const SMDS_MeshNode* n1 = link[iSide].first;
10264 const SMDS_MeshNode* n2 = link[iSide].second;
10265 set<const SMDS_MeshElement*> * faceSet = faceSetPtr[ iSide ];
10266 set< const SMDS_MeshElement* > facesOfNode1;
10267 for ( int iNode = 0; iNode < 2; iNode++ ) // loop on 2 nodes of a link
10269 // during a loop of the first node, we find all faces around n1,
10270 // during a loop of the second node, we find one face sharing both n1 and n2
10271 const SMDS_MeshNode* n = iNode ? n1 : n2; // a node of a link
10272 SMDS_ElemIteratorPtr fIt = n->GetInverseElementIterator(SMDSAbs_Face);
10273 while ( fIt->more() ) { // loop on faces sharing a node
10274 const SMDS_MeshElement* f = fIt->next();
10275 if (faceSet->find( f ) != faceSet->end() && // f is in face set
10276 ! facesOfNode1.insert( f ).second ) // f encounters twice
10278 if ( face[ iSide ] ) {
10279 MESSAGE( "2 faces per link " );
10280 return ( iSide ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10283 faceSet->erase( f );
10285 // get not link nodes
10286 int nbN = f->NbNodes();
10287 if ( f->IsQuadratic() )
10289 nbNodes[ iSide ] = nbN;
10290 list< const SMDS_MeshNode* > & nodes = notLinkNodes[ iSide ];
10291 int i1 = f->GetNodeIndex( n1 );
10292 int i2 = f->GetNodeIndex( n2 );
10293 int iEnd = nbN, iBeg = -1, iDelta = 1;
10294 bool reverse = ( Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1 );
10296 std::swap( iEnd, iBeg ); iDelta = -1;
10301 if ( i == iEnd ) i = iBeg + iDelta;
10302 if ( i == i1 ) break;
10303 nodes.push_back ( f->GetNode( i ) );
10309 // check similarity of elements of the sides
10310 if (( face[0] && !face[1] ) || ( !face[0] && face[1] )) {
10311 MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
10312 if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
10313 return ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10316 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10320 // set nodes to merge
10321 // -------------------
10323 if ( face[0] && face[1] ) {
10324 if ( nbNodes[0] != nbNodes[1] ) {
10325 MESSAGE("Diff nb of face nodes");
10326 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10328 #ifdef DEBUG_MATCHING_NODES
10329 MESSAGE ( " Link 1: " << link[0].first->GetID() <<" "<< link[0].second->GetID()
10330 << " F 1: " << face[0] << "| Link 2: " << link[1].first->GetID() <<" "
10331 << link[1].second->GetID() << " F 2: " << face[1] << " | Bind: " ) ;
10333 int nbN = nbNodes[0];
10335 list<const SMDS_MeshNode*>::iterator n1 = notLinkNodes[0].begin();
10336 list<const SMDS_MeshNode*>::iterator n2 = notLinkNodes[1].begin();
10337 for ( int i = 0 ; i < nbN - 2; ++i ) {
10338 #ifdef DEBUG_MATCHING_NODES
10339 MESSAGE ( (*n1)->GetID() << " to " << (*n2)->GetID() );
10341 nReplaceMap.insert( make_pair( *(n1++), *(n2++) ));
10345 // add other links of the face 1 to linkList
10346 // -----------------------------------------
10348 const SMDS_MeshElement* f0 = face[0];
10349 const SMDS_MeshNode* n1 = f0->GetNode( nbN - 1 );
10350 for ( int i = 0; i < nbN; i++ )
10352 const SMDS_MeshNode* n2 = f0->GetNode( i );
10353 pair< set< SMESH_TLink >::iterator, bool > iter_isnew =
10354 linkSet.insert( SMESH_TLink( n1, n2 ));
10355 if ( !iter_isnew.second ) { // already in a set: no need to process
10356 linkSet.erase( iter_isnew.first );
10358 else // new in set == encountered for the first time: add
10360 #ifdef DEBUG_MATCHING_NODES
10361 MESSAGE ( "Add link 1: " << n1->GetID() << " " << n2->GetID() << " "
10362 << " | link 2: " << nReplaceMap[n1]->GetID() << " " << nReplaceMap[n2]->GetID() << " " );
10364 linkList[0].push_back ( NLink( n1, n2 ));
10365 linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10370 } // loop on link lists
10375 //================================================================================
10377 * \brief Create elements equal (on same nodes) to given ones
10378 * \param [in] theElements - a set of elems to duplicate. If it is empty, all
10379 * elements of the uppest dimension are duplicated.
10381 //================================================================================
10383 void SMESH_MeshEditor::DoubleElements( const TIDSortedElemSet& theElements )
10385 ClearLastCreated();
10386 SMESHDS_Mesh* mesh = GetMeshDS();
10388 // get an element type and an iterator over elements
10390 SMDSAbs_ElementType type;
10391 SMDS_ElemIteratorPtr elemIt;
10392 vector< const SMDS_MeshElement* > allElems;
10393 if ( theElements.empty() )
10395 if ( mesh->NbNodes() == 0 )
10397 // get most complex type
10398 SMDSAbs_ElementType types[SMDSAbs_NbElementTypes] = {
10399 SMDSAbs_Volume, SMDSAbs_Face, SMDSAbs_Edge,
10400 SMDSAbs_0DElement, SMDSAbs_Ball, SMDSAbs_Node
10402 for ( int i = 0; i < SMDSAbs_NbElementTypes; ++i )
10403 if ( mesh->GetMeshInfo().NbElements( types[i] ))
10408 // put all elements in the vector <allElems>
10409 allElems.reserve( mesh->GetMeshInfo().NbElements( type ));
10410 elemIt = mesh->elementsIterator( type );
10411 while ( elemIt->more() )
10412 allElems.push_back( elemIt->next());
10413 elemIt = elemSetIterator( allElems );
10417 type = (*theElements.begin())->GetType();
10418 elemIt = elemSetIterator( theElements );
10421 // duplicate elements
10423 if ( type == SMDSAbs_Ball )
10425 SMDS_UnstructuredGrid* vtkGrid = mesh->getGrid();
10426 while ( elemIt->more() )
10428 const SMDS_MeshElement* elem = elemIt->next();
10429 if ( elem->GetType() != SMDSAbs_Ball )
10431 if (( elem = mesh->AddBall( elem->GetNode(0),
10432 vtkGrid->GetBallDiameter( elem->getVtkId() ))))
10433 myLastCreatedElems.Append( elem );
10438 vector< const SMDS_MeshNode* > nodes;
10439 while ( elemIt->more() )
10441 const SMDS_MeshElement* elem = elemIt->next();
10442 if ( elem->GetType() != type )
10445 nodes.assign( elem->begin_nodes(), elem->end_nodes() );
10447 if ( type == SMDSAbs_Volume && elem->GetVtkType() == VTK_POLYHEDRON )
10449 std::vector<int> quantities =
10450 static_cast< const SMDS_VtkVolume* >( elem )->GetQuantities();
10451 elem = mesh->AddPolyhedralVolume( nodes, quantities );
10455 AddElement( nodes, type, elem->IsPoly() );
10456 elem = 0; // myLastCreatedElems is already filled
10459 myLastCreatedElems.Append( elem );
10464 //================================================================================
10466 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10467 \param theElems - the list of elements (edges or faces) to be replicated
10468 The nodes for duplication could be found from these elements
10469 \param theNodesNot - list of nodes to NOT replicate
10470 \param theAffectedElems - the list of elements (cells and edges) to which the
10471 replicated nodes should be associated to.
10472 \return TRUE if operation has been completed successfully, FALSE otherwise
10474 //================================================================================
10476 bool SMESH_MeshEditor::DoubleNodes( const TIDSortedElemSet& theElems,
10477 const TIDSortedElemSet& theNodesNot,
10478 const TIDSortedElemSet& theAffectedElems )
10480 myLastCreatedElems.Clear();
10481 myLastCreatedNodes.Clear();
10483 if ( theElems.size() == 0 )
10486 SMESHDS_Mesh* aMeshDS = GetMeshDS();
10491 std::map< const SMDS_MeshNode*, const SMDS_MeshNode* > anOldNodeToNewNode;
10492 // duplicate elements and nodes
10493 res = doubleNodes( aMeshDS, theElems, theNodesNot, anOldNodeToNewNode, true );
10494 // replce nodes by duplications
10495 res = doubleNodes( aMeshDS, theAffectedElems, theNodesNot, anOldNodeToNewNode, false );
10499 //================================================================================
10501 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10502 \param theMeshDS - mesh instance
10503 \param theElems - the elements replicated or modified (nodes should be changed)
10504 \param theNodesNot - nodes to NOT replicate
10505 \param theNodeNodeMap - relation of old node to new created node
10506 \param theIsDoubleElem - flag os to replicate element or modify
10507 \return TRUE if operation has been completed successfully, FALSE otherwise
10509 //================================================================================
10511 bool SMESH_MeshEditor::doubleNodes( SMESHDS_Mesh* theMeshDS,
10512 const TIDSortedElemSet& theElems,
10513 const TIDSortedElemSet& theNodesNot,
10514 std::map< const SMDS_MeshNode*,
10515 const SMDS_MeshNode* >& theNodeNodeMap,
10516 const bool theIsDoubleElem )
10518 MESSAGE("doubleNodes");
10519 // iterate on through element and duplicate them (by nodes duplication)
10521 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10522 for ( ; elemItr != theElems.end(); ++elemItr )
10524 const SMDS_MeshElement* anElem = *elemItr;
10528 bool isDuplicate = false;
10529 // duplicate nodes to duplicate element
10530 std::vector<const SMDS_MeshNode*> newNodes( anElem->NbNodes() );
10531 SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
10533 while ( anIter->more() )
10536 SMDS_MeshNode* aCurrNode = (SMDS_MeshNode*)anIter->next();
10537 SMDS_MeshNode* aNewNode = aCurrNode;
10538 if ( theNodeNodeMap.find( aCurrNode ) != theNodeNodeMap.end() )
10539 aNewNode = (SMDS_MeshNode*)theNodeNodeMap[ aCurrNode ];
10540 else if ( theIsDoubleElem && theNodesNot.find( aCurrNode ) == theNodesNot.end() )
10543 aNewNode = theMeshDS->AddNode( aCurrNode->X(), aCurrNode->Y(), aCurrNode->Z() );
10544 copyPosition( aCurrNode, aNewNode );
10545 theNodeNodeMap[ aCurrNode ] = aNewNode;
10546 myLastCreatedNodes.Append( aNewNode );
10548 isDuplicate |= (aCurrNode != aNewNode);
10549 newNodes[ ind++ ] = aNewNode;
10551 if ( !isDuplicate )
10554 if ( theIsDoubleElem )
10555 AddElement(newNodes, anElem->GetType(), anElem->IsPoly());
10557 theMeshDS->ChangeElementNodes( anElem, &newNodes[ 0 ], anElem->NbNodes() );
10564 //================================================================================
10566 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10567 \param theNodes - identifiers of nodes to be doubled
10568 \param theModifiedElems - identifiers of elements to be updated by the new (doubled)
10569 nodes. If list of element identifiers is empty then nodes are doubled but
10570 they not assigned to elements
10571 \return TRUE if operation has been completed successfully, FALSE otherwise
10573 //================================================================================
10575 bool SMESH_MeshEditor::DoubleNodes( const std::list< int >& theListOfNodes,
10576 const std::list< int >& theListOfModifiedElems )
10578 MESSAGE("DoubleNodes");
10579 myLastCreatedElems.Clear();
10580 myLastCreatedNodes.Clear();
10582 if ( theListOfNodes.size() == 0 )
10585 SMESHDS_Mesh* aMeshDS = GetMeshDS();
10589 // iterate through nodes and duplicate them
10591 std::map< const SMDS_MeshNode*, const SMDS_MeshNode* > anOldNodeToNewNode;
10593 std::list< int >::const_iterator aNodeIter;
10594 for ( aNodeIter = theListOfNodes.begin(); aNodeIter != theListOfNodes.end(); ++aNodeIter )
10596 int aCurr = *aNodeIter;
10597 SMDS_MeshNode* aNode = (SMDS_MeshNode*)aMeshDS->FindNode( aCurr );
10603 const SMDS_MeshNode* aNewNode = aMeshDS->AddNode( aNode->X(), aNode->Y(), aNode->Z() );
10606 copyPosition( aNode, aNewNode );
10607 anOldNodeToNewNode[ aNode ] = aNewNode;
10608 myLastCreatedNodes.Append( aNewNode );
10612 // Create map of new nodes for modified elements
10614 std::map< SMDS_MeshElement*, vector<const SMDS_MeshNode*> > anElemToNodes;
10616 std::list< int >::const_iterator anElemIter;
10617 for ( anElemIter = theListOfModifiedElems.begin();
10618 anElemIter != theListOfModifiedElems.end(); ++anElemIter )
10620 int aCurr = *anElemIter;
10621 SMDS_MeshElement* anElem = (SMDS_MeshElement*)aMeshDS->FindElement( aCurr );
10625 vector<const SMDS_MeshNode*> aNodeArr( anElem->NbNodes() );
10627 SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
10629 while ( anIter->more() )
10631 SMDS_MeshNode* aCurrNode = (SMDS_MeshNode*)anIter->next();
10632 if ( aCurr && anOldNodeToNewNode.find( aCurrNode ) != anOldNodeToNewNode.end() )
10634 const SMDS_MeshNode* aNewNode = anOldNodeToNewNode[ aCurrNode ];
10635 aNodeArr[ ind++ ] = aNewNode;
10638 aNodeArr[ ind++ ] = aCurrNode;
10640 anElemToNodes[ anElem ] = aNodeArr;
10643 // Change nodes of elements
10645 std::map< SMDS_MeshElement*, vector<const SMDS_MeshNode*> >::iterator
10646 anElemToNodesIter = anElemToNodes.begin();
10647 for ( ; anElemToNodesIter != anElemToNodes.end(); ++anElemToNodesIter )
10649 const SMDS_MeshElement* anElem = anElemToNodesIter->first;
10650 vector<const SMDS_MeshNode*> aNodeArr = anElemToNodesIter->second;
10653 MESSAGE("ChangeElementNodes");
10654 aMeshDS->ChangeElementNodes( anElem, &aNodeArr[ 0 ], anElem->NbNodes() );
10663 //================================================================================
10665 \brief Check if element located inside shape
10666 \return TRUE if IN or ON shape, FALSE otherwise
10668 //================================================================================
10670 template<class Classifier>
10671 bool isInside(const SMDS_MeshElement* theElem,
10672 Classifier& theClassifier,
10673 const double theTol)
10675 gp_XYZ centerXYZ (0, 0, 0);
10676 SMDS_ElemIteratorPtr aNodeItr = theElem->nodesIterator();
10677 while (aNodeItr->more())
10678 centerXYZ += SMESH_TNodeXYZ(cast2Node( aNodeItr->next()));
10680 gp_Pnt aPnt = centerXYZ / theElem->NbNodes();
10681 theClassifier.Perform(aPnt, theTol);
10682 TopAbs_State aState = theClassifier.State();
10683 return (aState == TopAbs_IN || aState == TopAbs_ON );
10686 //================================================================================
10688 * \brief Classifier of the 3D point on the TopoDS_Face
10689 * with interaface suitable for isInside()
10691 //================================================================================
10693 struct _FaceClassifier
10695 Extrema_ExtPS _extremum;
10696 BRepAdaptor_Surface _surface;
10697 TopAbs_State _state;
10699 _FaceClassifier(const TopoDS_Face& face):_extremum(),_surface(face),_state(TopAbs_OUT)
10701 _extremum.Initialize( _surface,
10702 _surface.FirstUParameter(), _surface.LastUParameter(),
10703 _surface.FirstVParameter(), _surface.LastVParameter(),
10704 _surface.Tolerance(), _surface.Tolerance() );
10706 void Perform(const gp_Pnt& aPnt, double theTol)
10709 _state = TopAbs_OUT;
10710 _extremum.Perform(aPnt);
10711 if ( _extremum.IsDone() )
10712 for ( int iSol = 1; iSol <= _extremum.NbExt() && _state == TopAbs_OUT; ++iSol)
10713 _state = ( _extremum.SquareDistance(iSol) <= theTol ? TopAbs_IN : TopAbs_OUT );
10715 TopAbs_State State() const
10722 //================================================================================
10724 \brief Identify the elements that will be affected by node duplication (actual duplication is not performed).
10725 This method is the first step of DoubleNodeElemGroupsInRegion.
10726 \param theElems - list of groups of elements (edges or faces) to be replicated
10727 \param theNodesNot - list of groups of nodes not to replicated
10728 \param theShape - shape to detect affected elements (element which geometric center
10729 located on or inside shape). If the shape is null, detection is done on faces orientations
10730 (select elements with a gravity center on the side given by faces normals).
10731 This mode (null shape) is faster, but works only when theElems are faces, with coherents orientations.
10732 The replicated nodes should be associated to affected elements.
10733 \return groups of affected elements
10734 \sa DoubleNodeElemGroupsInRegion()
10736 //================================================================================
10738 bool SMESH_MeshEditor::AffectedElemGroupsInRegion( const TIDSortedElemSet& theElems,
10739 const TIDSortedElemSet& theNodesNot,
10740 const TopoDS_Shape& theShape,
10741 TIDSortedElemSet& theAffectedElems)
10743 if ( theShape.IsNull() )
10745 std::set<const SMDS_MeshNode*> alreadyCheckedNodes;
10746 std::set<const SMDS_MeshElement*> alreadyCheckedElems;
10747 std::set<const SMDS_MeshElement*> edgesToCheck;
10748 alreadyCheckedNodes.clear();
10749 alreadyCheckedElems.clear();
10750 edgesToCheck.clear();
10752 // --- iterates on elements to be replicated and get elements by back references from their nodes
10754 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10756 for ( ielem=1; elemItr != theElems.end(); ++elemItr )
10758 SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
10759 if (!anElem || (anElem->GetType() != SMDSAbs_Face))
10762 SMESH_MeshAlgos::FaceNormal( anElem, normal, /*normalized=*/true );
10763 MESSAGE("element " << ielem++ << " normal " << normal.X() << " " << normal.Y() << " " << normal.Z());
10764 std::set<const SMDS_MeshNode*> nodesElem;
10766 SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
10767 while ( nodeItr->more() )
10769 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10770 nodesElem.insert(aNode);
10772 std::set<const SMDS_MeshNode*>::iterator nodit = nodesElem.begin();
10773 for (; nodit != nodesElem.end(); nodit++)
10775 MESSAGE(" noeud ");
10776 const SMDS_MeshNode* aNode = *nodit;
10777 if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
10779 if (alreadyCheckedNodes.find(aNode) != alreadyCheckedNodes.end())
10781 alreadyCheckedNodes.insert(aNode);
10782 SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
10783 while ( backElemItr->more() )
10785 MESSAGE(" backelem ");
10786 const SMDS_MeshElement* curElem = backElemItr->next();
10787 if (alreadyCheckedElems.find(curElem) != alreadyCheckedElems.end())
10789 if (theElems.find(curElem) != theElems.end())
10791 alreadyCheckedElems.insert(curElem);
10792 double x=0, y=0, z=0;
10794 SMDS_ElemIteratorPtr nodeItr2 = curElem->nodesIterator();
10795 while ( nodeItr2->more() )
10797 const SMDS_MeshNode* anotherNode = cast2Node(nodeItr2->next());
10798 x += anotherNode->X();
10799 y += anotherNode->Y();
10800 z += anotherNode->Z();
10804 p.SetCoord( x/nb -aNode->X(),
10806 z/nb -aNode->Z() );
10807 MESSAGE(" check " << p.X() << " " << p.Y() << " " << p.Z());
10810 MESSAGE(" --- inserted")
10811 theAffectedElems.insert( curElem );
10813 else if (curElem->GetType() == SMDSAbs_Edge)
10814 edgesToCheck.insert(curElem);
10818 // --- add also edges lying on the set of faces (all nodes in alreadyCheckedNodes)
10819 std::set<const SMDS_MeshElement*>::iterator eit = edgesToCheck.begin();
10820 for( ; eit != edgesToCheck.end(); eit++)
10822 bool onside = true;
10823 const SMDS_MeshElement* anEdge = *eit;
10824 SMDS_ElemIteratorPtr nodeItr = anEdge->nodesIterator();
10825 while ( nodeItr->more() )
10827 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10828 if (alreadyCheckedNodes.find(aNode) == alreadyCheckedNodes.end())
10836 MESSAGE(" --- edge onside inserted")
10837 theAffectedElems.insert(anEdge);
10843 const double aTol = Precision::Confusion();
10844 auto_ptr< BRepClass3d_SolidClassifier> bsc3d;
10845 auto_ptr<_FaceClassifier> aFaceClassifier;
10846 if ( theShape.ShapeType() == TopAbs_SOLID )
10848 bsc3d.reset( new BRepClass3d_SolidClassifier(theShape));;
10849 bsc3d->PerformInfinitePoint(aTol);
10851 else if (theShape.ShapeType() == TopAbs_FACE )
10853 aFaceClassifier.reset( new _FaceClassifier(TopoDS::Face(theShape)));
10856 // iterates on indicated elements and get elements by back references from their nodes
10857 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10859 for ( ielem = 1; elemItr != theElems.end(); ++elemItr )
10861 MESSAGE("element " << ielem++);
10862 SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
10865 SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
10866 while ( nodeItr->more() )
10868 MESSAGE(" noeud ");
10869 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10870 if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
10872 SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
10873 while ( backElemItr->more() )
10875 MESSAGE(" backelem ");
10876 const SMDS_MeshElement* curElem = backElemItr->next();
10877 if ( curElem && theElems.find(curElem) == theElems.end() &&
10879 isInside( curElem, *bsc3d, aTol ) :
10880 isInside( curElem, *aFaceClassifier, aTol )))
10881 theAffectedElems.insert( curElem );
10889 //================================================================================
10891 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10892 \param theElems - group of of elements (edges or faces) to be replicated
10893 \param theNodesNot - group of nodes not to replicate
10894 \param theShape - shape to detect affected elements (element which geometric center
10895 located on or inside shape).
10896 The replicated nodes should be associated to affected elements.
10897 \return TRUE if operation has been completed successfully, FALSE otherwise
10899 //================================================================================
10901 bool SMESH_MeshEditor::DoubleNodesInRegion( const TIDSortedElemSet& theElems,
10902 const TIDSortedElemSet& theNodesNot,
10903 const TopoDS_Shape& theShape )
10905 if ( theShape.IsNull() )
10908 const double aTol = Precision::Confusion();
10909 auto_ptr< BRepClass3d_SolidClassifier> bsc3d;
10910 auto_ptr<_FaceClassifier> aFaceClassifier;
10911 if ( theShape.ShapeType() == TopAbs_SOLID )
10913 bsc3d.reset( new BRepClass3d_SolidClassifier(theShape));;
10914 bsc3d->PerformInfinitePoint(aTol);
10916 else if (theShape.ShapeType() == TopAbs_FACE )
10918 aFaceClassifier.reset( new _FaceClassifier(TopoDS::Face(theShape)));
10921 // iterates on indicated elements and get elements by back references from their nodes
10922 TIDSortedElemSet anAffected;
10923 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10924 for ( ; elemItr != theElems.end(); ++elemItr )
10926 SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
10930 SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
10931 while ( nodeItr->more() )
10933 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10934 if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
10936 SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
10937 while ( backElemItr->more() )
10939 const SMDS_MeshElement* curElem = backElemItr->next();
10940 if ( curElem && theElems.find(curElem) == theElems.end() &&
10942 isInside( curElem, *bsc3d, aTol ) :
10943 isInside( curElem, *aFaceClassifier, aTol )))
10944 anAffected.insert( curElem );
10948 return DoubleNodes( theElems, theNodesNot, anAffected );
10952 * \brief compute an oriented angle between two planes defined by four points.
10953 * The vector (p0,p1) defines the intersection of the 2 planes (p0,p1,g1) and (p0,p1,g2)
10954 * @param p0 base of the rotation axe
10955 * @param p1 extremity of the rotation axe
10956 * @param g1 belongs to the first plane
10957 * @param g2 belongs to the second plane
10959 double SMESH_MeshEditor::OrientedAngle(const gp_Pnt& p0, const gp_Pnt& p1, const gp_Pnt& g1, const gp_Pnt& g2)
10961 // MESSAGE(" p0: " << p0.X() << " " << p0.Y() << " " << p0.Z());
10962 // MESSAGE(" p1: " << p1.X() << " " << p1.Y() << " " << p1.Z());
10963 // MESSAGE(" g1: " << g1.X() << " " << g1.Y() << " " << g1.Z());
10964 // MESSAGE(" g2: " << g2.X() << " " << g2.Y() << " " << g2.Z());
10965 gp_Vec vref(p0, p1);
10968 gp_Vec n1 = vref.Crossed(v1);
10969 gp_Vec n2 = vref.Crossed(v2);
10971 return n2.AngleWithRef(n1, vref);
10973 catch ( Standard_Failure ) {
10975 return Max( v1.Magnitude(), v2.Magnitude() );
10979 * \brief Double nodes on shared faces between groups of volumes and create flat elements on demand.
10980 * The list of groups must contain at least two groups. The groups have to be disjoint: no common element into two different groups.
10981 * The nodes of the internal faces at the boundaries of the groups are doubled. Optionally, the internal faces are replaced by flat elements.
10982 * Triangles are transformed into prisms, and quadrangles into hexahedrons.
10983 * The flat elements are stored in groups of volumes. These groups are named according to the position of the group in the list:
10984 * 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.
10985 * 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.
10986 * All the flat elements are gathered into the group named "joints3D" (or "joints2D" in 2D situation).
10987 * The flat element of the multiple junctions between the simple junction are stored in a group named "jointsMultiples".
10988 * \param theElems - list of groups of volumes, where a group of volume is a set of
10989 * SMDS_MeshElements sorted by Id.
10990 * \param createJointElems - if TRUE, create the elements
10991 * \param onAllBoundaries - if TRUE, the nodes and elements are also created on
10992 * the boundary between \a theDomains and the rest mesh
10993 * \return TRUE if operation has been completed successfully, FALSE otherwise
10995 bool SMESH_MeshEditor::DoubleNodesOnGroupBoundaries( const std::vector<TIDSortedElemSet>& theElems,
10996 bool createJointElems,
10997 bool onAllBoundaries)
10999 MESSAGE("----------------------------------------------");
11000 MESSAGE("SMESH_MeshEditor::doubleNodesOnGroupBoundaries");
11001 MESSAGE("----------------------------------------------");
11003 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11004 meshDS->BuildDownWardConnectivity(true);
11006 SMDS_UnstructuredGrid *grid = meshDS->getGrid();
11008 // --- build the list of faces shared by 2 domains (group of elements), with their domain and volume indexes
11009 // build the list of cells with only a node or an edge on the border, with their domain and volume indexes
11010 // build the list of nodes shared by 2 or more domains, with their domain indexes
11012 std::map<DownIdType, std::map<int,int>, DownIdCompare> faceDomains; // face --> (id domain --> id volume)
11013 std::map<int,int>celldom; // cell vtkId --> domain
11014 std::map<DownIdType, std::map<int,int>, DownIdCompare> cellDomains; // oldNode --> (id domain --> id cell)
11015 std::map<int, std::map<int,int> > nodeDomains; // oldId --> (domainId --> newId)
11016 faceDomains.clear();
11018 cellDomains.clear();
11019 nodeDomains.clear();
11020 std::map<int,int> emptyMap;
11021 std::set<int> emptySet;
11024 MESSAGE(".. Number of domains :"<<theElems.size());
11026 TIDSortedElemSet theRestDomElems;
11027 const int iRestDom = -1;
11028 const int idom0 = onAllBoundaries ? iRestDom : 0;
11029 const int nbDomains = theElems.size();
11031 // Check if the domains do not share an element
11032 for (int idom = 0; idom < nbDomains-1; idom++)
11034 // MESSAGE("... Check of domain #" << idom);
11035 const TIDSortedElemSet& domain = theElems[idom];
11036 TIDSortedElemSet::const_iterator elemItr = domain.begin();
11037 for (; elemItr != domain.end(); ++elemItr)
11039 const SMDS_MeshElement* anElem = *elemItr;
11040 int idombisdeb = idom + 1 ;
11041 for (int idombis = idombisdeb; idombis < theElems.size(); idombis++) // check if the element belongs to a domain further in the list
11043 const TIDSortedElemSet& domainbis = theElems[idombis];
11044 if ( domainbis.count(anElem) )
11046 MESSAGE(".... Domain #" << idom);
11047 MESSAGE(".... Domain #" << idombis);
11048 throw SALOME_Exception("The domains are not disjoint.");
11055 for (int idom = 0; idom < nbDomains; idom++)
11058 // --- build a map (face to duplicate --> volume to modify)
11059 // with all the faces shared by 2 domains (group of elements)
11060 // and corresponding volume of this domain, for each shared face.
11061 // a volume has a face shared by 2 domains if it has a neighbor which is not in his domain.
11063 MESSAGE("... Neighbors of domain #" << idom);
11064 const TIDSortedElemSet& domain = theElems[idom];
11065 TIDSortedElemSet::const_iterator elemItr = domain.begin();
11066 for (; elemItr != domain.end(); ++elemItr)
11068 const SMDS_MeshElement* anElem = *elemItr;
11071 int vtkId = anElem->getVtkId();
11072 //MESSAGE(" vtkId " << vtkId << " smdsId " << anElem->GetID());
11073 int neighborsVtkIds[NBMAXNEIGHBORS];
11074 int downIds[NBMAXNEIGHBORS];
11075 unsigned char downTypes[NBMAXNEIGHBORS];
11076 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
11077 for (int n = 0; n < nbNeighbors; n++)
11079 int smdsId = meshDS->fromVtkToSmds(neighborsVtkIds[n]);
11080 const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
11081 if (elem && ! domain.count(elem)) // neighbor is in another domain : face is shared
11084 for (int idombis = 0; idombis < theElems.size() && !ok; idombis++) // check if the neighbor belongs to another domain of the list
11086 // MESSAGE("Domain " << idombis);
11087 const TIDSortedElemSet& domainbis = theElems[idombis];
11088 if ( domainbis.count(elem)) ok = true ; // neighbor is in a correct domain : face is kept
11090 if ( ok || onAllBoundaries ) // the characteristics of the face is stored
11092 DownIdType face(downIds[n], downTypes[n]);
11093 if (!faceDomains[face].count(idom))
11095 faceDomains[face][idom] = vtkId; // volume associated to face in this domain
11096 celldom[vtkId] = idom;
11097 //MESSAGE(" cell with a border " << vtkId << " domain " << idom);
11101 theRestDomElems.insert( elem );
11102 faceDomains[face][iRestDom] = neighborsVtkIds[n];
11103 celldom[neighborsVtkIds[n]] = iRestDom;
11111 //MESSAGE("Number of shared faces " << faceDomains.size());
11112 std::map<DownIdType, std::map<int, int>, DownIdCompare>::iterator itface;
11114 // --- explore the shared faces domain by domain,
11115 // explore the nodes of the face and see if they belong to a cell in the domain,
11116 // which has only a node or an edge on the border (not a shared face)
11118 for (int idomain = idom0; idomain < nbDomains; idomain++)
11120 //MESSAGE("Domain " << idomain);
11121 const TIDSortedElemSet& domain = (idomain == iRestDom) ? theRestDomElems : theElems[idomain];
11122 itface = faceDomains.begin();
11123 for (; itface != faceDomains.end(); ++itface)
11125 const std::map<int, int>& domvol = itface->second;
11126 if (!domvol.count(idomain))
11128 DownIdType face = itface->first;
11129 //MESSAGE(" --- face " << face.cellId);
11130 std::set<int> oldNodes;
11132 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11133 std::set<int>::iterator itn = oldNodes.begin();
11134 for (; itn != oldNodes.end(); ++itn)
11137 //MESSAGE(" node " << oldId);
11138 vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
11139 for (int i=0; i<l.ncells; i++)
11141 int vtkId = l.cells[i];
11142 const SMDS_MeshElement* anElem = GetMeshDS()->FindElement(GetMeshDS()->fromVtkToSmds(vtkId));
11143 if (!domain.count(anElem))
11145 int vtkType = grid->GetCellType(vtkId);
11146 int downId = grid->CellIdToDownId(vtkId);
11149 MESSAGE("doubleNodesOnGroupBoundaries: internal algorithm problem");
11150 continue; // not OK at this stage of the algorithm:
11151 //no cells created after BuildDownWardConnectivity
11153 DownIdType aCell(downId, vtkType);
11154 cellDomains[aCell][idomain] = vtkId;
11155 celldom[vtkId] = idomain;
11156 //MESSAGE(" cell " << vtkId << " domain " << idomain);
11162 // --- explore the shared faces domain by domain, to duplicate the nodes in a coherent way
11163 // for each shared face, get the nodes
11164 // for each node, for each domain of the face, create a clone of the node
11166 // --- edges at the intersection of 3 or 4 domains, with the order of domains to build
11167 // junction elements of type prism or hexa. the key is the pair of nodesId (lower first)
11168 // the value is the ordered domain ids. (more than 4 domains not taken into account)
11170 std::map<std::vector<int>, std::vector<int> > edgesMultiDomains; // nodes of edge --> ordered domains
11171 std::map<int, std::vector<int> > mutipleNodes; // nodes multi domains with domain order
11172 std::map<int, std::vector<int> > mutipleNodesToFace; // nodes multi domains with domain order to transform in Face (junction between 3 or more 2D domains)
11174 MESSAGE(".. Duplication of the nodes");
11175 for (int idomain = idom0; idomain < nbDomains; idomain++)
11177 itface = faceDomains.begin();
11178 for (; itface != faceDomains.end(); ++itface)
11180 const std::map<int, int>& domvol = itface->second;
11181 if (!domvol.count(idomain))
11183 DownIdType face = itface->first;
11184 //MESSAGE(" --- face " << face.cellId);
11185 std::set<int> oldNodes;
11187 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11188 std::set<int>::iterator itn = oldNodes.begin();
11189 for (; itn != oldNodes.end(); ++itn)
11192 if (nodeDomains[oldId].empty())
11194 nodeDomains[oldId][idomain] = oldId; // keep the old node in the first domain
11195 //MESSAGE("-+-+-b oldNode " << oldId << " domain " << idomain);
11197 std::map<int, int>::const_iterator itdom = domvol.begin();
11198 for (; itdom != domvol.end(); ++itdom)
11200 int idom = itdom->first;
11201 //MESSAGE(" domain " << idom);
11202 if (!nodeDomains[oldId].count(idom)) // --- node to clone
11204 if (nodeDomains[oldId].size() >= 2) // a multiple node
11206 vector<int> orderedDoms;
11207 //MESSAGE("multiple node " << oldId);
11208 if (mutipleNodes.count(oldId))
11209 orderedDoms = mutipleNodes[oldId];
11212 map<int,int>::iterator it = nodeDomains[oldId].begin();
11213 for (; it != nodeDomains[oldId].end(); ++it)
11214 orderedDoms.push_back(it->first);
11216 orderedDoms.push_back(idom); // TODO order ==> push_front or back
11217 //stringstream txt;
11218 //for (int i=0; i<orderedDoms.size(); i++)
11219 // txt << orderedDoms[i] << " ";
11220 //MESSAGE("orderedDoms " << txt.str());
11221 mutipleNodes[oldId] = orderedDoms;
11223 double *coords = grid->GetPoint(oldId);
11224 SMDS_MeshNode *newNode = meshDS->AddNode(coords[0], coords[1], coords[2]);
11225 copyPosition( meshDS->FindNodeVtk( oldId ), newNode );
11226 int newId = newNode->getVtkId();
11227 nodeDomains[oldId][idom] = newId; // cloned node for other domains
11228 //MESSAGE("-+-+-c oldNode " << oldId << " domain " << idomain << " newNode " << newId << " domain " << idom << " size=" <<nodeDomains[oldId].size());
11235 MESSAGE(".. Creation of elements");
11236 for (int idomain = idom0; idomain < nbDomains; idomain++)
11238 itface = faceDomains.begin();
11239 for (; itface != faceDomains.end(); ++itface)
11241 std::map<int, int> domvol = itface->second;
11242 if (!domvol.count(idomain))
11244 DownIdType face = itface->first;
11245 //MESSAGE(" --- face " << face.cellId);
11246 std::set<int> oldNodes;
11248 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11249 int nbMultipleNodes = 0;
11250 std::set<int>::iterator itn = oldNodes.begin();
11251 for (; itn != oldNodes.end(); ++itn)
11254 if (mutipleNodes.count(oldId))
11257 if (nbMultipleNodes > 1) // check if an edge of the face is shared between 3 or more domains
11259 //MESSAGE("multiple Nodes detected on a shared face");
11260 int downId = itface->first.cellId;
11261 unsigned char cellType = itface->first.cellType;
11262 // --- shared edge or shared face ?
11263 if ((cellType == VTK_LINE) || (cellType == VTK_QUADRATIC_EDGE)) // shared edge (between two faces)
11266 int nbNodes = grid->getDownArray(cellType)->getNodes(downId, nodes);
11267 for (int i=0; i< nbNodes; i=i+nbNodes-1) // i=0 , i=nbNodes-1
11268 if (mutipleNodes.count(nodes[i]))
11269 if (!mutipleNodesToFace.count(nodes[i]))
11270 mutipleNodesToFace[nodes[i]] = mutipleNodes[nodes[i]];
11272 else // shared face (between two volumes)
11274 int nbEdges = grid->getDownArray(cellType)->getNumberOfDownCells(downId);
11275 const int* downEdgeIds = grid->getDownArray(cellType)->getDownCells(downId);
11276 const unsigned char* edgeType = grid->getDownArray(cellType)->getDownTypes(downId);
11277 for (int ie =0; ie < nbEdges; ie++)
11280 int nbNodes = grid->getDownArray(edgeType[ie])->getNodes(downEdgeIds[ie], nodes);
11281 if (mutipleNodes.count(nodes[0]) && mutipleNodes.count(nodes[nbNodes-1]))
11283 vector<int> vn0 = mutipleNodes[nodes[0]];
11284 vector<int> vn1 = mutipleNodes[nodes[nbNodes - 1]];
11286 for (int i0 = 0; i0 < vn0.size(); i0++)
11287 for (int i1 = 0; i1 < vn1.size(); i1++)
11288 if (vn0[i0] == vn1[i1])
11289 doms.push_back(vn0[i0]);
11290 if (doms.size() >2)
11292 //MESSAGE(" detect edgesMultiDomains " << nodes[0] << " " << nodes[nbNodes - 1]);
11293 double *coords = grid->GetPoint(nodes[0]);
11294 gp_Pnt p0(coords[0], coords[1], coords[2]);
11295 coords = grid->GetPoint(nodes[nbNodes - 1]);
11296 gp_Pnt p1(coords[0], coords[1], coords[2]);
11298 int vtkVolIds[1000]; // an edge can belong to a lot of volumes
11299 map<int, SMDS_VtkVolume*> domvol; // domain --> a volume with the edge
11300 map<int, double> angleDom; // oriented angles between planes defined by edge and volume centers
11301 int nbvol = grid->GetParentVolumes(vtkVolIds, downEdgeIds[ie], edgeType[ie]);
11302 for (int id=0; id < doms.size(); id++)
11304 int idom = doms[id];
11305 const TIDSortedElemSet& domain = (idom == iRestDom) ? theRestDomElems : theElems[idom];
11306 for (int ivol=0; ivol<nbvol; ivol++)
11308 int smdsId = meshDS->fromVtkToSmds(vtkVolIds[ivol]);
11309 SMDS_MeshElement* elem = (SMDS_MeshElement*)meshDS->FindElement(smdsId);
11310 if (domain.count(elem))
11312 SMDS_VtkVolume* svol = dynamic_cast<SMDS_VtkVolume*>(elem);
11313 domvol[idom] = svol;
11314 //MESSAGE(" domain " << idom << " volume " << elem->GetID());
11316 vtkIdType npts = 0;
11317 vtkIdType* pts = 0;
11318 grid->GetCellPoints(vtkVolIds[ivol], npts, pts);
11319 SMDS_VtkVolume::gravityCenter(grid, pts, npts, values);
11322 gref.SetXYZ(gp_XYZ(values[0], values[1], values[2]));
11323 angleDom[idom] = 0;
11327 gp_Pnt g(values[0], values[1], values[2]);
11328 angleDom[idom] = OrientedAngle(p0, p1, gref, g); // -pi<angle<+pi
11329 //MESSAGE(" angle=" << angleDom[idom]);
11335 map<double, int> sortedDom; // sort domains by angle
11336 for (map<int, double>::iterator ia = angleDom.begin(); ia != angleDom.end(); ++ia)
11337 sortedDom[ia->second] = ia->first;
11338 vector<int> vnodes;
11340 for (map<double, int>::iterator ib = sortedDom.begin(); ib != sortedDom.end(); ++ib)
11342 vdom.push_back(ib->second);
11343 //MESSAGE(" ordered domain " << ib->second << " angle " << ib->first);
11345 for (int ino = 0; ino < nbNodes; ino++)
11346 vnodes.push_back(nodes[ino]);
11347 edgesMultiDomains[vnodes] = vdom; // nodes vector --> ordered domains
11356 // --- iterate on shared faces (volumes to modify, face to extrude)
11357 // get node id's of the face (id SMDS = id VTK)
11358 // create flat element with old and new nodes if requested
11360 // --- new quad nodes on flat quad elements: oldId --> ((domain1 X domain2) --> newId)
11361 // (domain1 X domain2) = domain1 + MAXINT*domain2
11363 std::map<int, std::map<long,int> > nodeQuadDomains;
11364 std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11366 MESSAGE(".. Creation of elements: simple junction");
11367 if (createJointElems)
11370 string joints2DName = "joints2D";
11371 mapOfJunctionGroups[joints2DName] = this->myMesh->AddGroup(SMDSAbs_Face, joints2DName.c_str(), idg);
11372 SMESHDS_Group *joints2DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints2DName]->GetGroupDS());
11373 string joints3DName = "joints3D";
11374 mapOfJunctionGroups[joints3DName] = this->myMesh->AddGroup(SMDSAbs_Volume, joints3DName.c_str(), idg);
11375 SMESHDS_Group *joints3DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints3DName]->GetGroupDS());
11377 itface = faceDomains.begin();
11378 for (; itface != faceDomains.end(); ++itface)
11380 DownIdType face = itface->first;
11381 std::set<int> oldNodes;
11382 std::set<int>::iterator itn;
11384 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11386 std::map<int, int> domvol = itface->second;
11387 std::map<int, int>::iterator itdom = domvol.begin();
11388 int dom1 = itdom->first;
11389 int vtkVolId = itdom->second;
11391 int dom2 = itdom->first;
11392 SMDS_MeshCell *vol = grid->extrudeVolumeFromFace(vtkVolId, dom1, dom2, oldNodes, nodeDomains,
11394 stringstream grpname;
11397 grpname << dom1 << "_" << dom2;
11399 grpname << dom2 << "_" << dom1;
11400 string namegrp = grpname.str();
11401 if (!mapOfJunctionGroups.count(namegrp))
11402 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(vol->GetType(), namegrp.c_str(), idg);
11403 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11405 sgrp->Add(vol->GetID());
11406 if (vol->GetType() == SMDSAbs_Volume)
11407 joints3DGrp->Add(vol->GetID());
11408 else if (vol->GetType() == SMDSAbs_Face)
11409 joints2DGrp->Add(vol->GetID());
11413 // --- create volumes on multiple domain intersection if requested
11414 // iterate on mutipleNodesToFace
11415 // iterate on edgesMultiDomains
11417 MESSAGE(".. Creation of elements: multiple junction");
11418 if (createJointElems)
11420 // --- iterate on mutipleNodesToFace
11422 std::map<int, std::vector<int> >::iterator itn = mutipleNodesToFace.begin();
11423 for (; itn != mutipleNodesToFace.end(); ++itn)
11425 int node = itn->first;
11426 vector<int> orderDom = itn->second;
11427 vector<vtkIdType> orderedNodes;
11428 for (int idom = 0; idom <orderDom.size(); idom++)
11429 orderedNodes.push_back( nodeDomains[node][orderDom[idom]] );
11430 SMDS_MeshFace* face = this->GetMeshDS()->AddFaceFromVtkIds(orderedNodes);
11432 stringstream grpname;
11434 grpname << 0 << "_" << 0;
11436 string namegrp = grpname.str();
11437 if (!mapOfJunctionGroups.count(namegrp))
11438 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Face, namegrp.c_str(), idg);
11439 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11441 sgrp->Add(face->GetID());
11444 // --- iterate on edgesMultiDomains
11446 std::map<std::vector<int>, std::vector<int> >::iterator ite = edgesMultiDomains.begin();
11447 for (; ite != edgesMultiDomains.end(); ++ite)
11449 vector<int> nodes = ite->first;
11450 vector<int> orderDom = ite->second;
11451 vector<vtkIdType> orderedNodes;
11452 if (nodes.size() == 2)
11454 //MESSAGE(" use edgesMultiDomains " << nodes[0] << " " << nodes[1]);
11455 for (int ino=0; ino < nodes.size(); ino++)
11456 if (orderDom.size() == 3)
11457 for (int idom = 0; idom <orderDom.size(); idom++)
11458 orderedNodes.push_back( nodeDomains[nodes[ino]][orderDom[idom]] );
11460 for (int idom = orderDom.size()-1; idom >=0; idom--)
11461 orderedNodes.push_back( nodeDomains[nodes[ino]][orderDom[idom]] );
11462 SMDS_MeshVolume* vol = this->GetMeshDS()->AddVolumeFromVtkIds(orderedNodes);
11465 string namegrp = "jointsMultiples";
11466 if (!mapOfJunctionGroups.count(namegrp))
11467 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
11468 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11470 sgrp->Add(vol->GetID());
11474 INFOS("Quadratic multiple joints not implemented");
11475 // TODO quadratic nodes
11480 // --- list the explicit faces and edges of the mesh that need to be modified,
11481 // i.e. faces and edges built with one or more duplicated nodes.
11482 // associate these faces or edges to their corresponding domain.
11483 // only the first domain found is kept when a face or edge is shared
11485 std::map<DownIdType, std::map<int,int>, DownIdCompare> faceOrEdgeDom; // cellToModify --> (id domain --> id cell)
11486 std::map<int,int> feDom; // vtk id of cell to modify --> id domain
11487 faceOrEdgeDom.clear();
11490 MESSAGE(".. Modification of elements");
11491 for (int idomain = idom0; idomain < nbDomains; idomain++)
11493 std::map<int, std::map<int, int> >::const_iterator itnod = nodeDomains.begin();
11494 for (; itnod != nodeDomains.end(); ++itnod)
11496 int oldId = itnod->first;
11497 //MESSAGE(" node " << oldId);
11498 vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
11499 for (int i = 0; i < l.ncells; i++)
11501 int vtkId = l.cells[i];
11502 int vtkType = grid->GetCellType(vtkId);
11503 int downId = grid->CellIdToDownId(vtkId);
11505 continue; // new cells: not to be modified
11506 DownIdType aCell(downId, vtkType);
11507 int volParents[1000];
11508 int nbvol = grid->GetParentVolumes(volParents, vtkId);
11509 for (int j = 0; j < nbvol; j++)
11510 if (celldom.count(volParents[j]) && (celldom[volParents[j]] == idomain))
11511 if (!feDom.count(vtkId))
11513 feDom[vtkId] = idomain;
11514 faceOrEdgeDom[aCell] = emptyMap;
11515 faceOrEdgeDom[aCell][idomain] = vtkId; // affect face or edge to the first domain only
11516 //MESSAGE("affect cell " << this->GetMeshDS()->fromVtkToSmds(vtkId) << " domain " << idomain
11517 // << " type " << vtkType << " downId " << downId);
11523 // --- iterate on shared faces (volumes to modify, face to extrude)
11524 // get node id's of the face
11525 // replace old nodes by new nodes in volumes, and update inverse connectivity
11527 std::map<DownIdType, std::map<int,int>, DownIdCompare>* maps[3] = {&faceDomains, &cellDomains, &faceOrEdgeDom};
11528 for (int m=0; m<3; m++)
11530 std::map<DownIdType, std::map<int,int>, DownIdCompare>* amap = maps[m];
11531 itface = (*amap).begin();
11532 for (; itface != (*amap).end(); ++itface)
11534 DownIdType face = itface->first;
11535 std::set<int> oldNodes;
11536 std::set<int>::iterator itn;
11538 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11539 //MESSAGE("examine cell, downId " << face.cellId << " type " << int(face.cellType));
11540 std::map<int, int> localClonedNodeIds;
11542 std::map<int, int> domvol = itface->second;
11543 std::map<int, int>::iterator itdom = domvol.begin();
11544 for (; itdom != domvol.end(); ++itdom)
11546 int idom = itdom->first;
11547 int vtkVolId = itdom->second;
11548 //MESSAGE("modify nodes of cell " << this->GetMeshDS()->fromVtkToSmds(vtkVolId) << " domain " << idom);
11549 localClonedNodeIds.clear();
11550 for (itn = oldNodes.begin(); itn != oldNodes.end(); ++itn)
11553 if (nodeDomains[oldId].count(idom))
11555 localClonedNodeIds[oldId] = nodeDomains[oldId][idom];
11556 //MESSAGE(" node " << oldId << " --> " << localClonedNodeIds[oldId]);
11559 meshDS->ModifyCellNodes(vtkVolId, localClonedNodeIds);
11564 // Remove empty groups (issue 0022812)
11565 std::map<std::string, SMESH_Group*>::iterator name_group = mapOfJunctionGroups.begin();
11566 for ( ; name_group != mapOfJunctionGroups.end(); ++name_group )
11568 if ( name_group->second && name_group->second->GetGroupDS()->IsEmpty() )
11569 myMesh->RemoveGroup( name_group->second->GetGroupDS()->GetID() );
11572 meshDS->CleanDownWardConnectivity(); // Mesh has been modified, downward connectivity is no more usable, free memory
11573 grid->BuildLinks();
11581 * \brief Double nodes on some external faces and create flat elements.
11582 * Flat elements are mainly used by some types of mechanic calculations.
11584 * Each group of the list must be constituted of faces.
11585 * Triangles are transformed in prisms, and quadrangles in hexahedrons.
11586 * @param theElems - list of groups of faces, where a group of faces is a set of
11587 * SMDS_MeshElements sorted by Id.
11588 * @return TRUE if operation has been completed successfully, FALSE otherwise
11590 bool SMESH_MeshEditor::CreateFlatElementsOnFacesGroups(const std::vector<TIDSortedElemSet>& theElems)
11592 MESSAGE("-------------------------------------------------");
11593 MESSAGE("SMESH_MeshEditor::CreateFlatElementsOnFacesGroups");
11594 MESSAGE("-------------------------------------------------");
11596 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11598 // --- For each group of faces
11599 // duplicate the nodes, create a flat element based on the face
11600 // replace the nodes of the faces by their clones
11602 std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> clonedNodes;
11603 std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> intermediateNodes;
11604 clonedNodes.clear();
11605 intermediateNodes.clear();
11606 std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11607 mapOfJunctionGroups.clear();
11609 for (int idom = 0; idom < theElems.size(); idom++)
11611 const TIDSortedElemSet& domain = theElems[idom];
11612 TIDSortedElemSet::const_iterator elemItr = domain.begin();
11613 for (; elemItr != domain.end(); ++elemItr)
11615 SMDS_MeshElement* anElem = (SMDS_MeshElement*) *elemItr;
11616 SMDS_MeshFace* aFace = dynamic_cast<SMDS_MeshFace*> (anElem);
11619 // MESSAGE("aFace=" << aFace->GetID());
11620 bool isQuad = aFace->IsQuadratic();
11621 vector<const SMDS_MeshNode*> ln0, ln1, ln2, ln3, ln4;
11623 // --- clone the nodes, create intermediate nodes for non medium nodes of a quad face
11625 SMDS_ElemIteratorPtr nodeIt = aFace->nodesIterator();
11626 while (nodeIt->more())
11628 const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*> (nodeIt->next());
11629 bool isMedium = isQuad && (aFace->IsMediumNode(node));
11631 ln2.push_back(node);
11633 ln0.push_back(node);
11635 const SMDS_MeshNode* clone = 0;
11636 if (!clonedNodes.count(node))
11638 clone = meshDS->AddNode(node->X(), node->Y(), node->Z());
11639 copyPosition( node, clone );
11640 clonedNodes[node] = clone;
11643 clone = clonedNodes[node];
11646 ln3.push_back(clone);
11648 ln1.push_back(clone);
11650 const SMDS_MeshNode* inter = 0;
11651 if (isQuad && (!isMedium))
11653 if (!intermediateNodes.count(node))
11655 inter = meshDS->AddNode(node->X(), node->Y(), node->Z());
11656 copyPosition( node, inter );
11657 intermediateNodes[node] = inter;
11660 inter = intermediateNodes[node];
11661 ln4.push_back(inter);
11665 // --- extrude the face
11667 vector<const SMDS_MeshNode*> ln;
11668 SMDS_MeshVolume* vol = 0;
11669 vtkIdType aType = aFace->GetVtkType();
11673 vol = meshDS->AddVolume(ln0[2], ln0[1], ln0[0], ln1[2], ln1[1], ln1[0]);
11674 // MESSAGE("vol prism " << vol->GetID());
11675 ln.push_back(ln1[0]);
11676 ln.push_back(ln1[1]);
11677 ln.push_back(ln1[2]);
11680 vol = meshDS->AddVolume(ln0[3], ln0[2], ln0[1], ln0[0], ln1[3], ln1[2], ln1[1], ln1[0]);
11681 // MESSAGE("vol hexa " << vol->GetID());
11682 ln.push_back(ln1[0]);
11683 ln.push_back(ln1[1]);
11684 ln.push_back(ln1[2]);
11685 ln.push_back(ln1[3]);
11687 case VTK_QUADRATIC_TRIANGLE:
11688 vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln0[0], ln0[1], ln0[2], ln3[0], ln3[1], ln3[2],
11689 ln2[0], ln2[1], ln2[2], ln4[0], ln4[1], ln4[2]);
11690 // MESSAGE("vol quad prism " << vol->GetID());
11691 ln.push_back(ln1[0]);
11692 ln.push_back(ln1[1]);
11693 ln.push_back(ln1[2]);
11694 ln.push_back(ln3[0]);
11695 ln.push_back(ln3[1]);
11696 ln.push_back(ln3[2]);
11698 case VTK_QUADRATIC_QUAD:
11699 // vol = meshDS->AddVolume(ln0[0], ln0[1], ln0[2], ln0[3], ln1[0], ln1[1], ln1[2], ln1[3],
11700 // ln2[0], ln2[1], ln2[2], ln2[3], ln3[0], ln3[1], ln3[2], ln3[3],
11701 // ln4[0], ln4[1], ln4[2], ln4[3]);
11702 vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln1[3], ln0[0], ln0[1], ln0[2], ln0[3],
11703 ln3[0], ln3[1], ln3[2], ln3[3], ln2[0], ln2[1], ln2[2], ln2[3],
11704 ln4[0], ln4[1], ln4[2], ln4[3]);
11705 // MESSAGE("vol quad hexa " << vol->GetID());
11706 ln.push_back(ln1[0]);
11707 ln.push_back(ln1[1]);
11708 ln.push_back(ln1[2]);
11709 ln.push_back(ln1[3]);
11710 ln.push_back(ln3[0]);
11711 ln.push_back(ln3[1]);
11712 ln.push_back(ln3[2]);
11713 ln.push_back(ln3[3]);
11723 stringstream grpname;
11727 string namegrp = grpname.str();
11728 if (!mapOfJunctionGroups.count(namegrp))
11729 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
11730 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11732 sgrp->Add(vol->GetID());
11735 // --- modify the face
11737 aFace->ChangeNodes(&ln[0], ln.size());
11744 * \brief identify all the elements around a geom shape, get the faces delimiting the hole
11745 * Build groups of volume to remove, groups of faces to replace on the skin of the object,
11746 * groups of faces to remove inside the object, (idem edges).
11747 * Build ordered list of nodes at the border of each group of faces to replace (to be used to build a geom subshape)
11749 void SMESH_MeshEditor::CreateHoleSkin(double radius,
11750 const TopoDS_Shape& theShape,
11751 SMESH_NodeSearcher* theNodeSearcher,
11752 const char* groupName,
11753 std::vector<double>& nodesCoords,
11754 std::vector<std::vector<int> >& listOfListOfNodes)
11756 MESSAGE("--------------------------------");
11757 MESSAGE("SMESH_MeshEditor::CreateHoleSkin");
11758 MESSAGE("--------------------------------");
11760 // --- zone of volumes to remove is given :
11761 // 1 either by a geom shape (one or more vertices) and a radius,
11762 // 2 either by a group of nodes (representative of the shape)to use with the radius,
11763 // 3 either by a group of nodes where all the elements build on one of this nodes are to remove,
11764 // In the case 2, the group of nodes is an external group of nodes from another mesh,
11765 // In the case 3, the group of nodes is an internal group of the mesh (obtained for instance by a filter),
11766 // defined by it's name.
11768 SMESHDS_GroupBase* groupDS = 0;
11769 SMESH_Mesh::GroupIteratorPtr groupIt = this->myMesh->GetGroups();
11770 while ( groupIt->more() )
11773 SMESH_Group * group = groupIt->next();
11774 if ( !group ) continue;
11775 groupDS = group->GetGroupDS();
11776 if ( !groupDS || groupDS->IsEmpty() ) continue;
11777 std::string grpName = group->GetName();
11778 //MESSAGE("grpName=" << grpName);
11779 if (grpName == groupName)
11785 bool isNodeGroup = false;
11786 bool isNodeCoords = false;
11789 if (groupDS->GetType() != SMDSAbs_Node)
11791 isNodeGroup = true; // a group of nodes exists and it is in this mesh
11794 if (nodesCoords.size() > 0)
11795 isNodeCoords = true; // a list o nodes given by their coordinates
11796 //MESSAGE("---" << isNodeGroup << " " << isNodeCoords);
11798 // --- define groups to build
11800 int idg; // --- group of SMDS volumes
11801 string grpvName = groupName;
11802 grpvName += "_vol";
11803 SMESH_Group *grp = this->myMesh->AddGroup(SMDSAbs_Volume, grpvName.c_str(), idg);
11806 MESSAGE("group not created " << grpvName);
11809 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(grp->GetGroupDS());
11811 int idgs; // --- group of SMDS faces on the skin
11812 string grpsName = groupName;
11813 grpsName += "_skin";
11814 SMESH_Group *grps = this->myMesh->AddGroup(SMDSAbs_Face, grpsName.c_str(), idgs);
11817 MESSAGE("group not created " << grpsName);
11820 SMESHDS_Group *sgrps = dynamic_cast<SMESHDS_Group*>(grps->GetGroupDS());
11822 int idgi; // --- group of SMDS faces internal (several shapes)
11823 string grpiName = groupName;
11824 grpiName += "_internalFaces";
11825 SMESH_Group *grpi = this->myMesh->AddGroup(SMDSAbs_Face, grpiName.c_str(), idgi);
11828 MESSAGE("group not created " << grpiName);
11831 SMESHDS_Group *sgrpi = dynamic_cast<SMESHDS_Group*>(grpi->GetGroupDS());
11833 int idgei; // --- group of SMDS faces internal (several shapes)
11834 string grpeiName = groupName;
11835 grpeiName += "_internalEdges";
11836 SMESH_Group *grpei = this->myMesh->AddGroup(SMDSAbs_Edge, grpeiName.c_str(), idgei);
11839 MESSAGE("group not created " << grpeiName);
11842 SMESHDS_Group *sgrpei = dynamic_cast<SMESHDS_Group*>(grpei->GetGroupDS());
11844 // --- build downward connectivity
11846 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11847 meshDS->BuildDownWardConnectivity(true);
11848 SMDS_UnstructuredGrid* grid = meshDS->getGrid();
11850 // --- set of volumes detected inside
11852 std::set<int> setOfInsideVol;
11853 std::set<int> setOfVolToCheck;
11855 std::vector<gp_Pnt> gpnts;
11858 if (isNodeGroup) // --- a group of nodes is provided : find all the volumes using one or more of this nodes
11860 MESSAGE("group of nodes provided");
11861 SMDS_ElemIteratorPtr elemIt = groupDS->GetElements();
11862 while ( elemIt->more() )
11864 const SMDS_MeshElement* elem = elemIt->next();
11867 const SMDS_MeshNode* node = dynamic_cast<const SMDS_MeshNode*>(elem);
11870 SMDS_MeshElement* vol = 0;
11871 SMDS_ElemIteratorPtr volItr = node->GetInverseElementIterator(SMDSAbs_Volume);
11872 while (volItr->more())
11874 vol = (SMDS_MeshElement*)volItr->next();
11875 setOfInsideVol.insert(vol->getVtkId());
11876 sgrp->Add(vol->GetID());
11880 else if (isNodeCoords)
11882 MESSAGE("list of nodes coordinates provided");
11885 while (i < nodesCoords.size()-2)
11887 double x = nodesCoords[i++];
11888 double y = nodesCoords[i++];
11889 double z = nodesCoords[i++];
11890 gp_Pnt p = gp_Pnt(x, y ,z);
11891 gpnts.push_back(p);
11892 MESSAGE("TopoDS_Vertex " << k << " " << p.X() << " " << p.Y() << " " << p.Z());
11896 else // --- no group, no coordinates : use the vertices of the geom shape provided, and radius
11898 MESSAGE("no group of nodes provided, using vertices from geom shape, and radius");
11899 TopTools_IndexedMapOfShape vertexMap;
11900 TopExp::MapShapes( theShape, TopAbs_VERTEX, vertexMap );
11901 gp_Pnt p = gp_Pnt(0,0,0);
11902 if (vertexMap.Extent() < 1)
11905 for ( int i = 1; i <= vertexMap.Extent(); ++i )
11907 const TopoDS_Vertex& vertex = TopoDS::Vertex( vertexMap( i ));
11908 p = BRep_Tool::Pnt(vertex);
11909 gpnts.push_back(p);
11910 MESSAGE("TopoDS_Vertex " << i << " " << p.X() << " " << p.Y() << " " << p.Z());
11914 if (gpnts.size() > 0)
11917 const SMDS_MeshNode* startNode = theNodeSearcher->FindClosestTo(gpnts[0]);
11919 nodeId = startNode->GetID();
11920 MESSAGE("nodeId " << nodeId);
11922 double radius2 = radius*radius;
11923 MESSAGE("radius2 " << radius2);
11925 // --- volumes on start node
11927 setOfVolToCheck.clear();
11928 SMDS_MeshElement* startVol = 0;
11929 SMDS_ElemIteratorPtr volItr = startNode->GetInverseElementIterator(SMDSAbs_Volume);
11930 while (volItr->more())
11932 startVol = (SMDS_MeshElement*)volItr->next();
11933 setOfVolToCheck.insert(startVol->getVtkId());
11935 if (setOfVolToCheck.empty())
11937 MESSAGE("No volumes found");
11941 // --- starting with central volumes then their neighbors, check if they are inside
11942 // or outside the domain, until no more new neighbor volume is inside.
11943 // Fill the group of inside volumes
11945 std::map<int, double> mapOfNodeDistance2;
11946 mapOfNodeDistance2.clear();
11947 std::set<int> setOfOutsideVol;
11948 while (!setOfVolToCheck.empty())
11950 std::set<int>::iterator it = setOfVolToCheck.begin();
11952 MESSAGE("volume to check, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
11953 bool volInside = false;
11954 vtkIdType npts = 0;
11955 vtkIdType* pts = 0;
11956 grid->GetCellPoints(vtkId, npts, pts);
11957 for (int i=0; i<npts; i++)
11959 double distance2 = 0;
11960 if (mapOfNodeDistance2.count(pts[i]))
11962 distance2 = mapOfNodeDistance2[pts[i]];
11963 MESSAGE("point " << pts[i] << " distance2 " << distance2);
11967 double *coords = grid->GetPoint(pts[i]);
11968 gp_Pnt aPoint = gp_Pnt(coords[0], coords[1], coords[2]);
11970 for (int j=0; j<gpnts.size(); j++)
11972 double d2 = aPoint.SquareDistance(gpnts[j]);
11973 if (d2 < distance2)
11976 if (distance2 < radius2)
11980 mapOfNodeDistance2[pts[i]] = distance2;
11981 MESSAGE(" point " << pts[i] << " distance2 " << distance2 << " coords " << coords[0] << " " << coords[1] << " " << coords[2]);
11983 if (distance2 < radius2)
11985 volInside = true; // one or more nodes inside the domain
11986 sgrp->Add(meshDS->fromVtkToSmds(vtkId));
11992 setOfInsideVol.insert(vtkId);
11993 MESSAGE(" volume inside, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
11994 int neighborsVtkIds[NBMAXNEIGHBORS];
11995 int downIds[NBMAXNEIGHBORS];
11996 unsigned char downTypes[NBMAXNEIGHBORS];
11997 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
11998 for (int n = 0; n < nbNeighbors; n++)
11999 if (!setOfInsideVol.count(neighborsVtkIds[n]) ||setOfOutsideVol.count(neighborsVtkIds[n]))
12000 setOfVolToCheck.insert(neighborsVtkIds[n]);
12004 setOfOutsideVol.insert(vtkId);
12005 MESSAGE(" volume outside, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12007 setOfVolToCheck.erase(vtkId);
12011 // --- for outside hexahedrons, check if they have more than one neighbor volume inside
12012 // If yes, add the volume to the inside set
12014 bool addedInside = true;
12015 std::set<int> setOfVolToReCheck;
12016 while (addedInside)
12018 MESSAGE(" --------------------------- re check");
12019 addedInside = false;
12020 std::set<int>::iterator itv = setOfInsideVol.begin();
12021 for (; itv != setOfInsideVol.end(); ++itv)
12024 int neighborsVtkIds[NBMAXNEIGHBORS];
12025 int downIds[NBMAXNEIGHBORS];
12026 unsigned char downTypes[NBMAXNEIGHBORS];
12027 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12028 for (int n = 0; n < nbNeighbors; n++)
12029 if (!setOfInsideVol.count(neighborsVtkIds[n]))
12030 setOfVolToReCheck.insert(neighborsVtkIds[n]);
12032 setOfVolToCheck = setOfVolToReCheck;
12033 setOfVolToReCheck.clear();
12034 while (!setOfVolToCheck.empty())
12036 std::set<int>::iterator it = setOfVolToCheck.begin();
12038 if (grid->GetCellType(vtkId) == VTK_HEXAHEDRON)
12040 MESSAGE("volume to recheck, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12041 int countInside = 0;
12042 int neighborsVtkIds[NBMAXNEIGHBORS];
12043 int downIds[NBMAXNEIGHBORS];
12044 unsigned char downTypes[NBMAXNEIGHBORS];
12045 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12046 for (int n = 0; n < nbNeighbors; n++)
12047 if (setOfInsideVol.count(neighborsVtkIds[n]))
12049 MESSAGE("countInside " << countInside);
12050 if (countInside > 1)
12052 MESSAGE(" volume inside, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12053 setOfInsideVol.insert(vtkId);
12054 sgrp->Add(meshDS->fromVtkToSmds(vtkId));
12055 addedInside = true;
12058 setOfVolToReCheck.insert(vtkId);
12060 setOfVolToCheck.erase(vtkId);
12064 // --- map of Downward faces at the boundary, inside the global volume
12065 // map of Downward faces on the skin of the global volume (equivalent to SMDS faces on the skin)
12066 // fill group of SMDS faces inside the volume (when several volume shapes)
12067 // fill group of SMDS faces on the skin of the global volume (if skin)
12069 std::map<DownIdType, int, DownIdCompare> boundaryFaces; // boundary faces inside the volume --> corresponding cell
12070 std::map<DownIdType, int, DownIdCompare> skinFaces; // faces on the skin of the global volume --> corresponding cell
12071 std::set<int>::iterator it = setOfInsideVol.begin();
12072 for (; it != setOfInsideVol.end(); ++it)
12075 //MESSAGE(" vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12076 int neighborsVtkIds[NBMAXNEIGHBORS];
12077 int downIds[NBMAXNEIGHBORS];
12078 unsigned char downTypes[NBMAXNEIGHBORS];
12079 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId, true);
12080 for (int n = 0; n < nbNeighbors; n++)
12082 int neighborDim = SMDS_Downward::getCellDimension(grid->GetCellType(neighborsVtkIds[n]));
12083 if (neighborDim == 3)
12085 if (! setOfInsideVol.count(neighborsVtkIds[n])) // neighbor volume is not inside : face is boundary
12087 DownIdType face(downIds[n], downTypes[n]);
12088 boundaryFaces[face] = vtkId;
12090 // if the face between to volumes is in the mesh, get it (internal face between shapes)
12091 int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12092 if (vtkFaceId >= 0)
12094 sgrpi->Add(meshDS->fromVtkToSmds(vtkFaceId));
12095 // find also the smds edges on this face
12096 int nbEdges = grid->getDownArray(downTypes[n])->getNumberOfDownCells(downIds[n]);
12097 const int* dEdges = grid->getDownArray(downTypes[n])->getDownCells(downIds[n]);
12098 const unsigned char* dTypes = grid->getDownArray(downTypes[n])->getDownTypes(downIds[n]);
12099 for (int i = 0; i < nbEdges; i++)
12101 int vtkEdgeId = grid->getDownArray(dTypes[i])->getVtkCellId(dEdges[i]);
12102 if (vtkEdgeId >= 0)
12103 sgrpei->Add(meshDS->fromVtkToSmds(vtkEdgeId));
12107 else if (neighborDim == 2) // skin of the volume
12109 DownIdType face(downIds[n], downTypes[n]);
12110 skinFaces[face] = vtkId;
12111 int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12112 if (vtkFaceId >= 0)
12113 sgrps->Add(meshDS->fromVtkToSmds(vtkFaceId));
12118 // --- identify the edges constituting the wire of each subshape on the skin
12119 // define polylines with the nodes of edges, equivalent to wires
12120 // project polylines on subshapes, and partition, to get geom faces
12122 std::map<int, std::set<int> > shapeIdToVtkIdSet; // shapeId --> set of vtkId on skin
12123 std::set<int> emptySet;
12125 std::set<int> shapeIds;
12127 SMDS_ElemIteratorPtr itelem = sgrps->GetElements();
12128 while (itelem->more())
12130 const SMDS_MeshElement *elem = itelem->next();
12131 int shapeId = elem->getshapeId();
12132 int vtkId = elem->getVtkId();
12133 if (!shapeIdToVtkIdSet.count(shapeId))
12135 shapeIdToVtkIdSet[shapeId] = emptySet;
12136 shapeIds.insert(shapeId);
12138 shapeIdToVtkIdSet[shapeId].insert(vtkId);
12141 std::map<int, std::set<DownIdType, DownIdCompare> > shapeIdToEdges; // shapeId --> set of downward edges
12142 std::set<DownIdType, DownIdCompare> emptyEdges;
12143 emptyEdges.clear();
12145 std::map<int, std::set<int> >::iterator itShape = shapeIdToVtkIdSet.begin();
12146 for (; itShape != shapeIdToVtkIdSet.end(); ++itShape)
12148 int shapeId = itShape->first;
12149 MESSAGE(" --- Shape ID --- "<< shapeId);
12150 shapeIdToEdges[shapeId] = emptyEdges;
12152 std::vector<int> nodesEdges;
12154 std::set<int>::iterator its = itShape->second.begin();
12155 for (; its != itShape->second.end(); ++its)
12158 MESSAGE(" " << vtkId);
12159 int neighborsVtkIds[NBMAXNEIGHBORS];
12160 int downIds[NBMAXNEIGHBORS];
12161 unsigned char downTypes[NBMAXNEIGHBORS];
12162 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12163 for (int n = 0; n < nbNeighbors; n++)
12165 if (neighborsVtkIds[n]<0) // only smds faces are considered as neighbors here
12167 int smdsId = meshDS->fromVtkToSmds(neighborsVtkIds[n]);
12168 const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
12169 if ( shapeIds.count(elem->getshapeId()) && !sgrps->Contains(elem)) // edge : neighbor in the set of shape, not in the group
12171 DownIdType edge(downIds[n], downTypes[n]);
12172 if (!shapeIdToEdges[shapeId].count(edge))
12174 shapeIdToEdges[shapeId].insert(edge);
12176 int nbNodes = grid->getDownArray(downTypes[n])->getNodes(downIds[n],vtkNodeId);
12177 nodesEdges.push_back(vtkNodeId[0]);
12178 nodesEdges.push_back(vtkNodeId[nbNodes-1]);
12179 MESSAGE(" --- nodes " << vtkNodeId[0]+1 << " " << vtkNodeId[nbNodes-1]+1);
12185 std::list<int> order;
12187 if (nodesEdges.size() > 0)
12189 order.push_back(nodesEdges[0]); MESSAGE(" --- back " << order.back()+1); // SMDS id = VTK id + 1;
12190 nodesEdges[0] = -1;
12191 order.push_back(nodesEdges[1]); MESSAGE(" --- back " << order.back()+1);
12192 nodesEdges[1] = -1; // do not reuse this edge
12196 int nodeTofind = order.back(); // try first to push back
12198 for (i = 0; i<nodesEdges.size(); i++)
12199 if (nodesEdges[i] == nodeTofind)
12201 if (i == nodesEdges.size())
12202 found = false; // no follower found on back
12205 if (i%2) // odd ==> use the previous one
12206 if (nodesEdges[i-1] < 0)
12210 order.push_back(nodesEdges[i-1]); MESSAGE(" --- back " << order.back()+1);
12211 nodesEdges[i-1] = -1;
12213 else // even ==> use the next one
12214 if (nodesEdges[i+1] < 0)
12218 order.push_back(nodesEdges[i+1]); MESSAGE(" --- back " << order.back()+1);
12219 nodesEdges[i+1] = -1;
12224 // try to push front
12226 nodeTofind = order.front(); // try to push front
12227 for (i = 0; i<nodesEdges.size(); i++)
12228 if (nodesEdges[i] == nodeTofind)
12230 if (i == nodesEdges.size())
12232 found = false; // no predecessor found on front
12235 if (i%2) // odd ==> use the previous one
12236 if (nodesEdges[i-1] < 0)
12240 order.push_front(nodesEdges[i-1]); MESSAGE(" --- front " << order.front()+1);
12241 nodesEdges[i-1] = -1;
12243 else // even ==> use the next one
12244 if (nodesEdges[i+1] < 0)
12248 order.push_front(nodesEdges[i+1]); MESSAGE(" --- front " << order.front()+1);
12249 nodesEdges[i+1] = -1;
12255 std::vector<int> nodes;
12256 nodes.push_back(shapeId);
12257 std::list<int>::iterator itl = order.begin();
12258 for (; itl != order.end(); itl++)
12260 nodes.push_back((*itl) + 1); // SMDS id = VTK id + 1;
12261 MESSAGE(" ordered node " << nodes[nodes.size()-1]);
12263 listOfListOfNodes.push_back(nodes);
12266 // partition geom faces with blocFissure
12267 // mesh blocFissure and geom faces of the skin (external wires given, triangle algo to choose)
12268 // mesh volume around blocFissure (skin triangles and quadrangle given, tetra algo to choose)
12274 //================================================================================
12276 * \brief Generates skin mesh (containing 2D cells) from 3D mesh
12277 * The created 2D mesh elements based on nodes of free faces of boundary volumes
12278 * \return TRUE if operation has been completed successfully, FALSE otherwise
12280 //================================================================================
12282 bool SMESH_MeshEditor::Make2DMeshFrom3D()
12284 // iterates on volume elements and detect all free faces on them
12285 SMESHDS_Mesh* aMesh = GetMeshDS();
12288 //bool res = false;
12289 int nbFree = 0, nbExisted = 0, nbCreated = 0;
12290 SMDS_VolumeIteratorPtr vIt = aMesh->volumesIterator();
12293 const SMDS_MeshVolume* volume = vIt->next();
12294 SMDS_VolumeTool vTool( volume, /*ignoreCentralNodes=*/false );
12295 vTool.SetExternalNormal();
12296 //const bool isPoly = volume->IsPoly();
12297 const int iQuad = volume->IsQuadratic();
12298 for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12300 if (!vTool.IsFreeFace(iface))
12303 vector<const SMDS_MeshNode *> nodes;
12304 int nbFaceNodes = vTool.NbFaceNodes(iface);
12305 const SMDS_MeshNode** faceNodes = vTool.GetFaceNodes(iface);
12307 for ( ; inode < nbFaceNodes; inode += iQuad+1)
12308 nodes.push_back(faceNodes[inode]);
12309 if (iQuad) { // add medium nodes
12310 for ( inode = 1; inode < nbFaceNodes; inode += 2)
12311 nodes.push_back(faceNodes[inode]);
12312 if ( nbFaceNodes == 9 ) // bi-quadratic quad
12313 nodes.push_back(faceNodes[8]);
12315 // add new face based on volume nodes
12316 if (aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false) ) {
12318 continue; // face already exsist
12320 AddElement(nodes, SMDSAbs_Face, ( !iQuad && nbFaceNodes/(iQuad+1) > 4 ));
12324 return ( nbFree==(nbExisted+nbCreated) );
12329 inline const SMDS_MeshNode* getNodeWithSameID(SMESHDS_Mesh* mesh, const SMDS_MeshNode* node)
12331 if ( const SMDS_MeshNode* n = mesh->FindNode( node->GetID() ))
12333 return mesh->AddNodeWithID( node->X(),node->Y(),node->Z(), node->GetID() );
12336 //================================================================================
12338 * \brief Creates missing boundary elements
12339 * \param elements - elements whose boundary is to be checked
12340 * \param dimension - defines type of boundary elements to create
12341 * \param group - a group to store created boundary elements in
12342 * \param targetMesh - a mesh to store created boundary elements in
12343 * \param toCopyElements - if true, the checked elements will be copied into the targetMesh
12344 * \param toCopyExistingBoundary - if true, not only new but also pre-existing
12345 * boundary elements will be copied into the targetMesh
12346 * \param toAddExistingBondary - if true, not only new but also pre-existing
12347 * boundary elements will be added into the new group
12348 * \param aroundElements - if true, elements will be created on boundary of given
12349 * elements else, on boundary of the whole mesh.
12350 * \return nb of added boundary elements
12352 //================================================================================
12354 int SMESH_MeshEditor::MakeBoundaryMesh(const TIDSortedElemSet& elements,
12355 Bnd_Dimension dimension,
12356 SMESH_Group* group/*=0*/,
12357 SMESH_Mesh* targetMesh/*=0*/,
12358 bool toCopyElements/*=false*/,
12359 bool toCopyExistingBoundary/*=false*/,
12360 bool toAddExistingBondary/*= false*/,
12361 bool aroundElements/*= false*/)
12363 SMDSAbs_ElementType missType = (dimension == BND_2DFROM3D) ? SMDSAbs_Face : SMDSAbs_Edge;
12364 SMDSAbs_ElementType elemType = (dimension == BND_1DFROM2D) ? SMDSAbs_Face : SMDSAbs_Volume;
12365 // hope that all elements are of the same type, do not check them all
12366 if ( !elements.empty() && (*elements.begin())->GetType() != elemType )
12367 throw SALOME_Exception(LOCALIZED("wrong element type"));
12370 toCopyElements = toCopyExistingBoundary = false;
12372 SMESH_MeshEditor tgtEditor( targetMesh ? targetMesh : myMesh );
12373 SMESHDS_Mesh* aMesh = GetMeshDS(), *tgtMeshDS = tgtEditor.GetMeshDS();
12374 int nbAddedBnd = 0;
12376 // editor adding present bnd elements and optionally holding elements to add to the group
12377 SMESH_MeshEditor* presentEditor;
12378 SMESH_MeshEditor tgtEditor2( tgtEditor.GetMesh() );
12379 presentEditor = toAddExistingBondary ? &tgtEditor : &tgtEditor2;
12381 SMESH_MesherHelper helper( *myMesh );
12382 const TopAbs_ShapeEnum missShapeType = ( missType==SMDSAbs_Face ? TopAbs_FACE : TopAbs_EDGE );
12383 SMDS_VolumeTool vTool;
12384 TIDSortedElemSet avoidSet;
12385 const TIDSortedElemSet emptySet, *elemSet = aroundElements ? &elements : &emptySet;
12388 typedef vector<const SMDS_MeshNode*> TConnectivity;
12390 SMDS_ElemIteratorPtr eIt;
12391 if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
12392 else eIt = elemSetIterator( elements );
12394 while (eIt->more())
12396 const SMDS_MeshElement* elem = eIt->next();
12397 const int iQuad = elem->IsQuadratic();
12399 // ------------------------------------------------------------------------------------
12400 // 1. For an elem, get present bnd elements and connectivities of missing bnd elements
12401 // ------------------------------------------------------------------------------------
12402 vector<const SMDS_MeshElement*> presentBndElems;
12403 vector<TConnectivity> missingBndElems;
12404 TConnectivity nodes, elemNodes;
12405 if ( vTool.Set(elem, /*ignoreCentralNodes=*/true) ) // elem is a volume --------------
12407 vTool.SetExternalNormal();
12408 const SMDS_MeshElement* otherVol = 0;
12409 for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12411 if ( !vTool.IsFreeFace(iface, &otherVol) &&
12412 ( !aroundElements || elements.count( otherVol )))
12414 const SMDS_MeshNode** nn = vTool.GetFaceNodes(iface);
12415 const int nbFaceNodes = vTool.NbFaceNodes (iface);
12416 if ( missType == SMDSAbs_Edge ) // boundary edges
12418 nodes.resize( 2+iQuad );
12419 for ( int i = 0; i < nbFaceNodes; i += 1+iQuad)
12421 for ( int j = 0; j < nodes.size(); ++j )
12423 if ( const SMDS_MeshElement* edge =
12424 aMesh->FindElement(nodes,SMDSAbs_Edge,/*noMedium=*/false))
12425 presentBndElems.push_back( edge );
12427 missingBndElems.push_back( nodes );
12430 else // boundary face
12433 for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12434 nodes.push_back( nn[inode] ); // add corner nodes
12436 for ( inode = 1; inode < nbFaceNodes; inode += 2)
12437 nodes.push_back( nn[inode] ); // add medium nodes
12438 int iCenter = vTool.GetCenterNodeIndex(iface); // for HEX27
12440 nodes.push_back( vTool.GetNodes()[ iCenter ] );
12442 if (const SMDS_MeshElement * f = aMesh->FindElement( nodes,
12443 SMDSAbs_Face, /*noMedium=*/false ))
12444 presentBndElems.push_back( f );
12446 missingBndElems.push_back( nodes );
12448 if ( targetMesh != myMesh )
12450 // add 1D elements on face boundary to be added to a new mesh
12451 const SMDS_MeshElement* edge;
12452 for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12455 edge = aMesh->FindEdge( nn[inode], nn[inode+1], nn[inode+2]);
12457 edge = aMesh->FindEdge( nn[inode], nn[inode+1]);
12458 if ( edge && avoidSet.insert( edge ).second )
12459 presentBndElems.push_back( edge );
12465 else if ( elem->GetType() == SMDSAbs_Face ) // elem is a face ------------------------
12467 avoidSet.clear(), avoidSet.insert( elem );
12468 elemNodes.assign( SMDS_MeshElement::iterator( elem->interlacedNodesElemIterator() ),
12469 SMDS_MeshElement::iterator() );
12470 elemNodes.push_back( elemNodes[0] );
12471 nodes.resize( 2 + iQuad );
12472 const int nbLinks = elem->NbCornerNodes();
12473 for ( int i = 0, iN = 0; i < nbLinks; i++, iN += 1+iQuad )
12475 nodes[0] = elemNodes[iN];
12476 nodes[1] = elemNodes[iN+1+iQuad];
12477 if ( SMESH_MeshAlgos::FindFaceInSet( nodes[0], nodes[1], *elemSet, avoidSet))
12478 continue; // not free link
12480 if ( iQuad ) nodes[2] = elemNodes[iN+1];
12481 if ( const SMDS_MeshElement* edge =
12482 aMesh->FindElement(nodes,SMDSAbs_Edge,/*noMedium=*/false))
12483 presentBndElems.push_back( edge );
12485 missingBndElems.push_back( nodes );
12489 // ---------------------------------
12490 // 2. Add missing boundary elements
12491 // ---------------------------------
12492 if ( targetMesh != myMesh )
12493 // instead of making a map of nodes in this mesh and targetMesh,
12494 // we create nodes with same IDs.
12495 for ( int i = 0; i < missingBndElems.size(); ++i )
12497 TConnectivity& srcNodes = missingBndElems[i];
12498 TConnectivity nodes( srcNodes.size() );
12499 for ( inode = 0; inode < nodes.size(); ++inode )
12500 nodes[inode] = getNodeWithSameID( tgtMeshDS, srcNodes[inode] );
12501 if ( aroundElements && tgtEditor.GetMeshDS()->FindElement( nodes,
12503 /*noMedium=*/false))
12505 tgtEditor.AddElement(nodes, missType, !iQuad && nodes.size()/(iQuad+1)>4);
12509 for ( int i = 0; i < missingBndElems.size(); ++i )
12511 TConnectivity& nodes = missingBndElems[i];
12512 if ( aroundElements && tgtEditor.GetMeshDS()->FindElement( nodes,
12514 /*noMedium=*/false))
12516 SMDS_MeshElement* elem =
12517 tgtEditor.AddElement(nodes, missType, !iQuad && nodes.size()/(iQuad+1)>4);
12520 // try to set a new element to a shape
12521 if ( myMesh->HasShapeToMesh() )
12524 set< pair<TopAbs_ShapeEnum, int > > mediumShapes;
12525 const int nbN = nodes.size() / (iQuad+1 );
12526 for ( inode = 0; inode < nbN && ok; ++inode )
12528 pair<int, TopAbs_ShapeEnum> i_stype =
12529 helper.GetMediumPos( nodes[inode], nodes[(inode+1)%nbN]);
12530 if (( ok = ( i_stype.first > 0 && i_stype.second >= TopAbs_FACE )))
12531 mediumShapes.insert( make_pair ( i_stype.second, i_stype.first ));
12533 if ( ok && mediumShapes.size() > 1 )
12535 set< pair<TopAbs_ShapeEnum, int > >::iterator stype_i = mediumShapes.begin();
12536 pair<TopAbs_ShapeEnum, int> stype_i_0 = *stype_i;
12537 for ( ++stype_i; stype_i != mediumShapes.end() && ok; ++stype_i )
12539 if (( ok = ( stype_i->first != stype_i_0.first )))
12540 ok = helper.IsSubShape( aMesh->IndexToShape( stype_i->second ),
12541 aMesh->IndexToShape( stype_i_0.second ));
12544 if ( ok && mediumShapes.begin()->first == missShapeType )
12545 aMesh->SetMeshElementOnShape( elem, mediumShapes.begin()->second );
12549 // ----------------------------------
12550 // 3. Copy present boundary elements
12551 // ----------------------------------
12552 if ( toCopyExistingBoundary )
12553 for ( int i = 0 ; i < presentBndElems.size(); ++i )
12555 const SMDS_MeshElement* e = presentBndElems[i];
12556 TConnectivity nodes( e->NbNodes() );
12557 for ( inode = 0; inode < nodes.size(); ++inode )
12558 nodes[inode] = getNodeWithSameID( tgtMeshDS, e->GetNode(inode) );
12559 presentEditor->AddElement(nodes, e->GetType(), e->IsPoly());
12561 else // store present elements to add them to a group
12562 for ( int i = 0 ; i < presentBndElems.size(); ++i )
12564 presentEditor->myLastCreatedElems.Append(presentBndElems[i]);
12567 } // loop on given elements
12569 // ---------------------------------------------
12570 // 4. Fill group with boundary elements
12571 // ---------------------------------------------
12574 if ( SMESHDS_Group* g = dynamic_cast<SMESHDS_Group*>( group->GetGroupDS() ))
12575 for ( int i = 0; i < tgtEditor.myLastCreatedElems.Size(); ++i )
12576 g->SMDSGroup().Add( tgtEditor.myLastCreatedElems( i+1 ));
12578 tgtEditor.myLastCreatedElems.Clear();
12579 tgtEditor2.myLastCreatedElems.Clear();
12581 // -----------------------
12582 // 5. Copy given elements
12583 // -----------------------
12584 if ( toCopyElements && targetMesh != myMesh )
12586 if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
12587 else eIt = elemSetIterator( elements );
12588 while (eIt->more())
12590 const SMDS_MeshElement* elem = eIt->next();
12591 TConnectivity nodes( elem->NbNodes() );
12592 for ( inode = 0; inode < nodes.size(); ++inode )
12593 nodes[inode] = getNodeWithSameID( tgtMeshDS, elem->GetNode(inode) );
12594 tgtEditor.AddElement(nodes, elemType, elem->IsPoly());
12596 tgtEditor.myLastCreatedElems.Clear();
12602 //================================================================================
12604 * \brief Copy node position and set \a to node on the same geometry
12606 //================================================================================
12608 void SMESH_MeshEditor::copyPosition( const SMDS_MeshNode* from,
12609 const SMDS_MeshNode* to )
12611 if ( !from || !to ) return;
12613 SMDS_PositionPtr pos = from->GetPosition();
12614 if ( !pos || from->getshapeId() < 1 ) return;
12616 switch ( pos->GetTypeOfPosition() )
12618 case SMDS_TOP_3DSPACE: break;
12620 case SMDS_TOP_FACE:
12622 const SMDS_FacePosition* fPos = static_cast< const SMDS_FacePosition* >( pos );
12623 GetMeshDS()->SetNodeOnFace( to, from->getshapeId(),
12624 fPos->GetUParameter(), fPos->GetVParameter() );
12627 case SMDS_TOP_EDGE:
12629 // WARNING: it is dangerous to set equal nodes on one EDGE!!!!!!!!
12630 const SMDS_EdgePosition* ePos = static_cast< const SMDS_EdgePosition* >( pos );
12631 GetMeshDS()->SetNodeOnEdge( to, from->getshapeId(), ePos->GetUParameter() );
12634 case SMDS_TOP_VERTEX:
12636 GetMeshDS()->SetNodeOnVertex( to, from->getshapeId() );
12639 case SMDS_TOP_UNSPEC: