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;
6310 SMDSAbs_ElementType aTypeE = elem->GetType();
6311 if ( !elem /*|| ( aTypeE != SMDSAbs_Face && aTypeE != SMDSAbs_Edge )*/ )
6314 vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
6315 newNodesItVec.reserve( elem->NbNodes() );
6317 // loop on elem nodes
6319 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6320 while ( itN->more() )
6323 // check if a node has been already processed
6324 const SMDS_MeshNode* node =
6325 static_cast<const SMDS_MeshNode*>( itN->next() );
6326 TNodeOfNodeListMap::iterator nIt = mapNewNodes.find( node );
6327 if ( nIt == mapNewNodes.end() ) {
6328 nIt = mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
6329 list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
6332 Standard_Real aAngle1x, aAngleT1T0, aTolAng;
6333 gp_Pnt aP0x, aP1x, aPN0, aPN1, aV0x, aV1x;
6334 gp_Ax1 anAx1, anAxT1T0;
6335 gp_Dir aDT1x, aDT0x, aDT1T0;
6340 aPN0 = SMESH_TNodeXYZ( node );
6342 const SMESH_MeshEditor_PathPoint& aPP0 = aPPs[0];
6344 aDT0x= aPP0.Tangent();
6345 //cout<<"j = 0 PP: Pnt("<<aP0x.X()<<","<<aP0x.Y()<<","<<aP0x.Z()<<")"<<endl;
6347 for ( int j = 1; j < aNbTP; ++j ) {
6348 const SMESH_MeshEditor_PathPoint& aPP1 = aPPs[j];
6350 aDT1x = aPP1.Tangent();
6351 aAngle1x = aPP1.Angle();
6353 gp_Trsf aTrsf, aTrsfRot, aTrsfRotT1T0;
6355 gp_Vec aV01x( aP0x, aP1x );
6356 aTrsf.SetTranslation( aV01x );
6359 aV1x = aV0x.Transformed( aTrsf );
6360 aPN1 = aPN0.Transformed( aTrsf );
6362 // rotation 1 [ T1,T0 ]
6363 aAngleT1T0=-aDT1x.Angle( aDT0x );
6364 if (fabs(aAngleT1T0) > aTolAng) {
6366 anAxT1T0.SetLocation( aV1x );
6367 anAxT1T0.SetDirection( aDT1T0 );
6368 aTrsfRotT1T0.SetRotation( anAxT1T0, aAngleT1T0 );
6370 aPN1 = aPN1.Transformed( aTrsfRotT1T0 );
6374 if ( theHasAngles ) {
6375 anAx1.SetLocation( aV1x );
6376 anAx1.SetDirection( aDT1x );
6377 aTrsfRot.SetRotation( anAx1, aAngle1x );
6379 aPN1 = aPN1.Transformed( aTrsfRot );
6383 //MESSAGE("elem->IsQuadratic " << elem->IsQuadratic() << " " << elem->IsMediumNode(node));
6384 if( elem->IsQuadratic() && !elem->IsMediumNode(node) ) {
6385 // create additional node
6386 double x = ( aPN1.X() + aPN0.X() )/2.;
6387 double y = ( aPN1.Y() + aPN0.Y() )/2.;
6388 double z = ( aPN1.Z() + aPN0.Z() )/2.;
6389 const SMDS_MeshNode* newNode = aMesh->AddNode(x,y,z);
6390 myLastCreatedNodes.Append(newNode);
6391 srcNodes.Append( node );
6392 listNewNodes.push_back( newNode );
6394 const SMDS_MeshNode* newNode = aMesh->AddNode( aPN1.X(), aPN1.Y(), aPN1.Z() );
6395 myLastCreatedNodes.Append(newNode);
6396 srcNodes.Append( node );
6397 listNewNodes.push_back( newNode );
6407 // if current elem is quadratic and current node is not medium
6408 // we have to check - may be it is needed to insert additional nodes
6409 if( elem->IsQuadratic() && !elem->IsMediumNode(node) ) {
6410 list< const SMDS_MeshNode* > & listNewNodes = nIt->second;
6411 if(listNewNodes.size()==aNbTP-1) {
6412 vector<const SMDS_MeshNode*> aNodes(2*(aNbTP-1));
6413 gp_XYZ P(node->X(), node->Y(), node->Z());
6414 list< const SMDS_MeshNode* >::iterator it = listNewNodes.begin();
6416 for(i=0; i<aNbTP-1; i++) {
6417 const SMDS_MeshNode* N = *it;
6418 double x = ( N->X() + P.X() )/2.;
6419 double y = ( N->Y() + P.Y() )/2.;
6420 double z = ( N->Z() + P.Z() )/2.;
6421 const SMDS_MeshNode* newN = aMesh->AddNode(x,y,z);
6422 srcNodes.Append( node );
6423 myLastCreatedNodes.Append(newN);
6426 P = gp_XYZ(N->X(),N->Y(),N->Z());
6428 listNewNodes.clear();
6429 for(i=0; i<2*(aNbTP-1); i++) {
6430 listNewNodes.push_back(aNodes[i]);
6436 newNodesItVec.push_back( nIt );
6438 // make new elements
6439 //sweepElement( aMesh, elem, newNodesItVec, newElemsMap[elem],
6440 // newNodesItVec[0]->second.size(), myLastCreatedElems );
6441 sweepElement( elem, newNodesItVec, newElemsMap[elem], aNbTP-1, srcElems );
6445 makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElemSets[0], aNbTP-1, srcElems );
6447 if ( theMakeGroups )
6448 generateGroups( srcNodes, srcElems, "extruded");
6454 //=======================================================================
6455 //function : LinearAngleVariation
6456 //purpose : auxilary for ExtrusionAlongTrack
6457 //=======================================================================
6458 void SMESH_MeshEditor::LinearAngleVariation(const int nbSteps,
6459 list<double>& Angles)
6461 int nbAngles = Angles.size();
6462 if( nbSteps > nbAngles ) {
6463 vector<double> theAngles(nbAngles);
6464 list<double>::iterator it = Angles.begin();
6466 for(; it!=Angles.end(); it++) {
6468 theAngles[i] = (*it);
6471 double rAn2St = double( nbAngles ) / double( nbSteps );
6472 double angPrev = 0, angle;
6473 for ( int iSt = 0; iSt < nbSteps; ++iSt ) {
6474 double angCur = rAn2St * ( iSt+1 );
6475 double angCurFloor = floor( angCur );
6476 double angPrevFloor = floor( angPrev );
6477 if ( angPrevFloor == angCurFloor )
6478 angle = rAn2St * theAngles[ int( angCurFloor ) ];
6480 int iP = int( angPrevFloor );
6481 double angPrevCeil = ceil(angPrev);
6482 angle = ( angPrevCeil - angPrev ) * theAngles[ iP ];
6484 int iC = int( angCurFloor );
6485 if ( iC < nbAngles )
6486 angle += ( angCur - angCurFloor ) * theAngles[ iC ];
6488 iP = int( angPrevCeil );
6490 angle += theAngles[ iC ];
6492 res.push_back(angle);
6497 for(; it!=res.end(); it++)
6498 Angles.push_back( *it );
6503 //================================================================================
6505 * \brief Move or copy theElements applying theTrsf to their nodes
6506 * \param theElems - elements to transform, if theElems is empty then apply to all mesh nodes
6507 * \param theTrsf - transformation to apply
6508 * \param theCopy - if true, create translated copies of theElems
6509 * \param theMakeGroups - if true and theCopy, create translated groups
6510 * \param theTargetMesh - mesh to copy translated elements into
6511 * \return SMESH_MeshEditor::PGroupIDs - list of ids of created groups
6513 //================================================================================
6515 SMESH_MeshEditor::PGroupIDs
6516 SMESH_MeshEditor::Transform (TIDSortedElemSet & theElems,
6517 const gp_Trsf& theTrsf,
6519 const bool theMakeGroups,
6520 SMESH_Mesh* theTargetMesh)
6522 myLastCreatedElems.Clear();
6523 myLastCreatedNodes.Clear();
6525 bool needReverse = false;
6526 string groupPostfix;
6527 switch ( theTrsf.Form() ) {
6529 MESSAGE("gp_PntMirror");
6531 groupPostfix = "mirrored";
6534 MESSAGE("gp_Ax1Mirror");
6535 groupPostfix = "mirrored";
6538 MESSAGE("gp_Ax2Mirror");
6540 groupPostfix = "mirrored";
6543 MESSAGE("gp_Rotation");
6544 groupPostfix = "rotated";
6546 case gp_Translation:
6547 MESSAGE("gp_Translation");
6548 groupPostfix = "translated";
6551 MESSAGE("gp_Scale");
6552 groupPostfix = "scaled";
6554 case gp_CompoundTrsf: // different scale by axis
6555 MESSAGE("gp_CompoundTrsf");
6556 groupPostfix = "scaled";
6560 needReverse = false;
6561 groupPostfix = "transformed";
6564 SMESH_MeshEditor targetMeshEditor( theTargetMesh );
6565 SMESHDS_Mesh* aTgtMesh = theTargetMesh ? theTargetMesh->GetMeshDS() : 0;
6566 SMESHDS_Mesh* aMesh = GetMeshDS();
6569 // map old node to new one
6570 TNodeNodeMap nodeMap;
6572 // elements sharing moved nodes; those of them which have all
6573 // nodes mirrored but are not in theElems are to be reversed
6574 TIDSortedElemSet inverseElemSet;
6576 // source elements for each generated one
6577 SMESH_SequenceOfElemPtr srcElems, srcNodes;
6579 // issue 021015: EDF 1578 SMESH: Free nodes are removed when translating a mesh
6580 TIDSortedElemSet orphanNode;
6582 if ( theElems.empty() ) // transform the whole mesh
6585 SMDS_ElemIteratorPtr eIt = aMesh->elementsIterator();
6586 while ( eIt->more() ) theElems.insert( eIt->next() );
6588 SMDS_NodeIteratorPtr nIt = aMesh->nodesIterator();
6589 while ( nIt->more() )
6591 const SMDS_MeshNode* node = nIt->next();
6592 if ( node->NbInverseElements() == 0)
6593 orphanNode.insert( node );
6597 // loop on elements to transform nodes : first orphan nodes then elems
6598 TIDSortedElemSet::iterator itElem;
6599 TIDSortedElemSet *elements[] = {&orphanNode, &theElems };
6600 for (int i=0; i<2; i++)
6601 for ( itElem = elements[i]->begin(); itElem != elements[i]->end(); itElem++ ) {
6602 const SMDS_MeshElement* elem = *itElem;
6606 // loop on elem nodes
6607 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6608 while ( itN->more() ) {
6610 const SMDS_MeshNode* node = cast2Node( itN->next() );
6611 // check if a node has been already transformed
6612 pair<TNodeNodeMap::iterator,bool> n2n_isnew =
6613 nodeMap.insert( make_pair ( node, node ));
6614 if ( !n2n_isnew.second )
6618 coord[0] = node->X();
6619 coord[1] = node->Y();
6620 coord[2] = node->Z();
6621 theTrsf.Transforms( coord[0], coord[1], coord[2] );
6622 if ( theTargetMesh ) {
6623 const SMDS_MeshNode * newNode = aTgtMesh->AddNode( coord[0], coord[1], coord[2] );
6624 n2n_isnew.first->second = newNode;
6625 myLastCreatedNodes.Append(newNode);
6626 srcNodes.Append( node );
6628 else if ( theCopy ) {
6629 const SMDS_MeshNode * newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
6630 n2n_isnew.first->second = newNode;
6631 myLastCreatedNodes.Append(newNode);
6632 srcNodes.Append( node );
6635 aMesh->MoveNode( node, coord[0], coord[1], coord[2] );
6636 // node position on shape becomes invalid
6637 const_cast< SMDS_MeshNode* > ( node )->SetPosition
6638 ( SMDS_SpacePosition::originSpacePosition() );
6641 // keep inverse elements
6642 if ( !theCopy && !theTargetMesh && needReverse ) {
6643 SMDS_ElemIteratorPtr invElemIt = node->GetInverseElementIterator();
6644 while ( invElemIt->more() ) {
6645 const SMDS_MeshElement* iel = invElemIt->next();
6646 inverseElemSet.insert( iel );
6652 // either create new elements or reverse mirrored ones
6653 if ( !theCopy && !needReverse && !theTargetMesh )
6656 TIDSortedElemSet::iterator invElemIt = inverseElemSet.begin();
6657 for ( ; invElemIt != inverseElemSet.end(); invElemIt++ )
6658 theElems.insert( *invElemIt );
6660 // Replicate or reverse elements
6662 std::vector<int> iForw;
6663 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
6665 const SMDS_MeshElement* elem = *itElem;
6666 if ( !elem ) continue;
6668 SMDSAbs_GeometryType geomType = elem->GetGeomType();
6669 int nbNodes = elem->NbNodes();
6670 if ( geomType == SMDSGeom_NONE ) continue; // node
6672 switch ( geomType ) {
6674 case SMDSGeom_POLYGON: // ---------------------- polygon
6676 vector<const SMDS_MeshNode*> poly_nodes (nbNodes);
6678 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6679 while (itN->more()) {
6680 const SMDS_MeshNode* node =
6681 static_cast<const SMDS_MeshNode*>(itN->next());
6682 TNodeNodeMap::iterator nodeMapIt = nodeMap.find(node);
6683 if (nodeMapIt == nodeMap.end())
6684 break; // not all nodes transformed
6686 // reverse mirrored faces and volumes
6687 poly_nodes[nbNodes - iNode - 1] = (*nodeMapIt).second;
6689 poly_nodes[iNode] = (*nodeMapIt).second;
6693 if ( iNode != nbNodes )
6694 continue; // not all nodes transformed
6696 if ( theTargetMesh ) {
6697 myLastCreatedElems.Append(aTgtMesh->AddPolygonalFace(poly_nodes));
6698 srcElems.Append( elem );
6700 else if ( theCopy ) {
6701 myLastCreatedElems.Append(aMesh->AddPolygonalFace(poly_nodes));
6702 srcElems.Append( elem );
6705 aMesh->ChangePolygonNodes(elem, poly_nodes);
6710 case SMDSGeom_POLYHEDRA: // ------------------ polyhedral volume
6712 const SMDS_VtkVolume* aPolyedre =
6713 dynamic_cast<const SMDS_VtkVolume*>( elem );
6715 MESSAGE("Warning: bad volumic element");
6719 vector<const SMDS_MeshNode*> poly_nodes; poly_nodes.reserve( nbNodes );
6720 vector<int> quantities; quantities.reserve( nbNodes );
6722 bool allTransformed = true;
6723 int nbFaces = aPolyedre->NbFaces();
6724 for (int iface = 1; iface <= nbFaces && allTransformed; iface++) {
6725 int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
6726 for (int inode = 1; inode <= nbFaceNodes && allTransformed; inode++) {
6727 const SMDS_MeshNode* node = aPolyedre->GetFaceNode(iface, inode);
6728 TNodeNodeMap::iterator nodeMapIt = nodeMap.find(node);
6729 if (nodeMapIt == nodeMap.end()) {
6730 allTransformed = false; // not all nodes transformed
6732 poly_nodes.push_back((*nodeMapIt).second);
6734 if ( needReverse && allTransformed )
6735 std::reverse( poly_nodes.end() - nbFaceNodes, poly_nodes.end() );
6737 quantities.push_back(nbFaceNodes);
6739 if ( !allTransformed )
6740 continue; // not all nodes transformed
6742 if ( theTargetMesh ) {
6743 myLastCreatedElems.Append(aTgtMesh->AddPolyhedralVolume(poly_nodes, quantities));
6744 srcElems.Append( elem );
6746 else if ( theCopy ) {
6747 myLastCreatedElems.Append(aMesh->AddPolyhedralVolume(poly_nodes, quantities));
6748 srcElems.Append( elem );
6751 aMesh->ChangePolyhedronNodes(elem, poly_nodes, quantities);
6756 case SMDSGeom_BALL: // -------------------- Ball
6758 if ( !theCopy && !theTargetMesh ) continue;
6760 TNodeNodeMap::iterator nodeMapIt = nodeMap.find( elem->GetNode(0) );
6761 if (nodeMapIt == nodeMap.end())
6762 continue; // not all nodes transformed
6764 double diameter = static_cast<const SMDS_BallElement*>(elem)->GetDiameter();
6765 if ( theTargetMesh ) {
6766 myLastCreatedElems.Append(aTgtMesh->AddBall( nodeMapIt->second, diameter ));
6767 srcElems.Append( elem );
6770 myLastCreatedElems.Append(aMesh->AddBall( nodeMapIt->second, diameter ));
6771 srcElems.Append( elem );
6776 default: // ----------------------- Regular elements
6778 while ( iForw.size() < nbNodes ) iForw.push_back( iForw.size() );
6779 const std::vector<int>& iRev = SMDS_MeshCell::reverseSmdsOrder( elem->GetEntityType() );
6780 const std::vector<int>& i = needReverse ? iRev : iForw;
6782 // find transformed nodes
6783 vector<const SMDS_MeshNode*> nodes(nbNodes);
6785 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6786 while ( itN->more() ) {
6787 const SMDS_MeshNode* node =
6788 static_cast<const SMDS_MeshNode*>( itN->next() );
6789 TNodeNodeMap::iterator nodeMapIt = nodeMap.find( node );
6790 if ( nodeMapIt == nodeMap.end() )
6791 break; // not all nodes transformed
6792 nodes[ i [ iNode++ ]] = (*nodeMapIt).second;
6794 if ( iNode != nbNodes )
6795 continue; // not all nodes transformed
6797 if ( theTargetMesh ) {
6798 if ( SMDS_MeshElement* copy =
6799 targetMeshEditor.AddElement( nodes, elem->GetType(), elem->IsPoly() )) {
6800 myLastCreatedElems.Append( copy );
6801 srcElems.Append( elem );
6804 else if ( theCopy ) {
6805 if ( AddElement( nodes, elem->GetType(), elem->IsPoly() ))
6806 srcElems.Append( elem );
6809 // reverse element as it was reversed by transformation
6811 aMesh->ChangeElementNodes( elem, &nodes[0], nbNodes );
6813 } // switch ( geomType )
6815 } // loop on elements
6817 PGroupIDs newGroupIDs;
6819 if ( ( theMakeGroups && theCopy ) ||
6820 ( theMakeGroups && theTargetMesh ) )
6821 newGroupIDs = generateGroups( srcNodes, srcElems, groupPostfix, theTargetMesh, false );
6826 //=======================================================================
6828 * \brief Create groups of elements made during transformation
6829 * \param nodeGens - nodes making corresponding myLastCreatedNodes
6830 * \param elemGens - elements making corresponding myLastCreatedElems
6831 * \param postfix - to append to names of new groups
6832 * \param targetMesh - mesh to create groups in
6833 * \param topPresent - is there "top" elements that are created by sweeping
6835 //=======================================================================
6837 SMESH_MeshEditor::PGroupIDs
6838 SMESH_MeshEditor::generateGroups(const SMESH_SequenceOfElemPtr& nodeGens,
6839 const SMESH_SequenceOfElemPtr& elemGens,
6840 const std::string& postfix,
6841 SMESH_Mesh* targetMesh,
6842 const bool topPresent)
6844 PGroupIDs newGroupIDs( new list<int> );
6845 SMESH_Mesh* mesh = targetMesh ? targetMesh : GetMesh();
6847 // Sort existing groups by types and collect their names
6849 // containers to store an old group and generated new ones;
6850 // 1st new group is for result elems of different type than a source one;
6851 // 2nd new group is for same type result elems ("top" group at extrusion)
6853 using boost::make_tuple;
6854 typedef tuple< SMESHDS_GroupBase*, SMESHDS_Group*, SMESHDS_Group* > TOldNewGroup;
6855 vector< list< TOldNewGroup > > groupsByType( SMDSAbs_NbElementTypes );
6856 vector< TOldNewGroup* > orderedOldNewGroups; // in order of old groups
6858 set< string > groupNames;
6860 SMESH_Mesh::GroupIteratorPtr groupIt = GetMesh()->GetGroups();
6861 if ( !groupIt->more() ) return newGroupIDs;
6863 int newGroupID = mesh->GetGroupIds().back()+1;
6864 while ( groupIt->more() )
6866 SMESH_Group * group = groupIt->next();
6867 if ( !group ) continue;
6868 SMESHDS_GroupBase* groupDS = group->GetGroupDS();
6869 if ( !groupDS || groupDS->IsEmpty() ) continue;
6870 groupNames.insert ( group->GetName() );
6871 groupDS->SetStoreName( group->GetName() );
6872 const SMDSAbs_ElementType type = groupDS->GetType();
6873 SMESHDS_Group* newGroup = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
6874 SMESHDS_Group* newTopGroup = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
6875 groupsByType[ type ].push_back( make_tuple( groupDS, newGroup, newTopGroup ));
6876 orderedOldNewGroups.push_back( & groupsByType[ type ].back() );
6879 // Loop on nodes and elements to add them in new groups
6881 vector< const SMDS_MeshElement* > resultElems;
6882 for ( int isNodes = 0; isNodes < 2; ++isNodes )
6884 const SMESH_SequenceOfElemPtr& gens = isNodes ? nodeGens : elemGens;
6885 const SMESH_SequenceOfElemPtr& elems = isNodes ? myLastCreatedNodes : myLastCreatedElems;
6886 if ( gens.Length() != elems.Length() )
6887 throw SALOME_Exception("SMESH_MeshEditor::generateGroups(): invalid args");
6889 // loop on created elements
6890 for (int iElem = 1; iElem <= elems.Length(); ++iElem )
6892 const SMDS_MeshElement* sourceElem = gens( iElem );
6893 if ( !sourceElem ) {
6894 MESSAGE("generateGroups(): NULL source element");
6897 list< TOldNewGroup > & groupsOldNew = groupsByType[ sourceElem->GetType() ];
6898 if ( groupsOldNew.empty() ) { // no groups of this type at all
6899 while ( iElem < gens.Length() && gens( iElem+1 ) == sourceElem )
6900 ++iElem; // skip all elements made by sourceElem
6903 // collect all elements made by the iElem-th sourceElem
6904 resultElems.clear();
6905 if ( const SMDS_MeshElement* resElem = elems( iElem ))
6906 if ( resElem != sourceElem )
6907 resultElems.push_back( resElem );
6908 while ( iElem < gens.Length() && gens( iElem+1 ) == sourceElem )
6909 if ( const SMDS_MeshElement* resElem = elems( ++iElem ))
6910 if ( resElem != sourceElem )
6911 resultElems.push_back( resElem );
6913 const SMDS_MeshElement* topElem = 0;
6914 if ( isNodes ) // there must be a top element
6916 topElem = resultElems.back();
6917 resultElems.pop_back();
6921 vector< const SMDS_MeshElement* >::reverse_iterator resElemIt = resultElems.rbegin();
6922 for ( ; resElemIt != resultElems.rend() ; ++resElemIt )
6923 if ( (*resElemIt)->GetType() == sourceElem->GetType() )
6925 topElem = *resElemIt;
6926 *resElemIt = 0; // erase *resElemIt
6930 // add resultElems to groups originted from ones the sourceElem belongs to
6931 list< TOldNewGroup >::iterator gOldNew, gLast = groupsOldNew.end();
6932 for ( gOldNew = groupsOldNew.begin(); gOldNew != gLast; ++gOldNew )
6934 SMESHDS_GroupBase* oldGroup = gOldNew->get<0>();
6935 if ( oldGroup->Contains( sourceElem )) // sourceElem is in oldGroup
6937 // fill in a new group
6938 SMDS_MeshGroup & newGroup = gOldNew->get<1>()->SMDSGroup();
6939 vector< const SMDS_MeshElement* >::iterator resLast = resultElems.end(), resElemIt;
6940 for ( resElemIt = resultElems.begin(); resElemIt != resLast; ++resElemIt )
6942 newGroup.Add( *resElemIt );
6944 // fill a "top" group
6947 SMDS_MeshGroup & newTopGroup = gOldNew->get<2>()->SMDSGroup();
6948 newTopGroup.Add( topElem );
6952 } // loop on created elements
6953 }// loop on nodes and elements
6955 // Create new SMESH_Groups from SMESHDS_Groups and remove empty SMESHDS_Groups
6957 list<int> topGrouIds;
6958 for ( size_t i = 0; i < orderedOldNewGroups.size(); ++i )
6960 SMESHDS_GroupBase* oldGroupDS = orderedOldNewGroups[i]->get<0>();
6961 SMESHDS_Group* newGroups[2] = { orderedOldNewGroups[i]->get<1>(),
6962 orderedOldNewGroups[i]->get<2>() };
6963 for ( int is2nd = 0; is2nd < 2; ++is2nd )
6965 SMESHDS_Group* newGroupDS = newGroups[ is2nd ];
6966 if ( newGroupDS->IsEmpty() )
6968 mesh->GetMeshDS()->RemoveGroup( newGroupDS );
6973 newGroupDS->SetType( newGroupDS->GetElements()->next()->GetType() );
6976 const bool isTop = ( topPresent &&
6977 newGroupDS->GetType() == oldGroupDS->GetType() &&
6980 string name = oldGroupDS->GetStoreName();
6981 { // remove trailing whitespaces (issue 22599)
6982 size_t size = name.size();
6983 while ( size > 1 && isspace( name[ size-1 ]))
6985 if ( size != name.size() )
6987 name.resize( size );
6988 oldGroupDS->SetStoreName( name.c_str() );
6991 if ( !targetMesh ) {
6992 string suffix = ( isTop ? "top": postfix.c_str() );
6996 while ( !groupNames.insert( name ).second ) // name exists
6997 name = SMESH_Comment( oldGroupDS->GetStoreName() ) << "_" << suffix << "_" << nb++;
7002 newGroupDS->SetStoreName( name.c_str() );
7004 // make a SMESH_Groups
7005 mesh->AddGroup( newGroupDS );
7007 topGrouIds.push_back( newGroupDS->GetID() );
7009 newGroupIDs->push_back( newGroupDS->GetID() );
7013 newGroupIDs->splice( newGroupIDs->end(), topGrouIds );
7018 //================================================================================
7020 * \brief Return list of group of nodes close to each other within theTolerance
7021 * Search among theNodes or in the whole mesh if theNodes is empty using
7022 * an Octree algorithm
7024 //================================================================================
7026 void SMESH_MeshEditor::FindCoincidentNodes (TIDSortedNodeSet & theNodes,
7027 const double theTolerance,
7028 TListOfListOfNodes & theGroupsOfNodes)
7030 myLastCreatedElems.Clear();
7031 myLastCreatedNodes.Clear();
7033 if ( theNodes.empty() )
7034 { // get all nodes in the mesh
7035 SMDS_NodeIteratorPtr nIt = GetMeshDS()->nodesIterator(/*idInceasingOrder=*/true);
7036 while ( nIt->more() )
7037 theNodes.insert( theNodes.end(),nIt->next());
7040 SMESH_OctreeNode::FindCoincidentNodes ( theNodes, &theGroupsOfNodes, theTolerance);
7043 //=======================================================================
7044 //function : SimplifyFace
7046 //=======================================================================
7048 int SMESH_MeshEditor::SimplifyFace (const vector<const SMDS_MeshNode *>& faceNodes,
7049 vector<const SMDS_MeshNode *>& poly_nodes,
7050 vector<int>& quantities) const
7052 int nbNodes = faceNodes.size();
7057 set<const SMDS_MeshNode*> nodeSet;
7059 // get simple seq of nodes
7060 //const SMDS_MeshNode* simpleNodes[ nbNodes ];
7061 vector<const SMDS_MeshNode*> simpleNodes( nbNodes );
7062 int iSimple = 0, nbUnique = 0;
7064 simpleNodes[iSimple++] = faceNodes[0];
7066 for (int iCur = 1; iCur < nbNodes; iCur++) {
7067 if (faceNodes[iCur] != simpleNodes[iSimple - 1]) {
7068 simpleNodes[iSimple++] = faceNodes[iCur];
7069 if (nodeSet.insert( faceNodes[iCur] ).second)
7073 int nbSimple = iSimple;
7074 if (simpleNodes[nbSimple - 1] == simpleNodes[0]) {
7084 bool foundLoop = (nbSimple > nbUnique);
7087 set<const SMDS_MeshNode*> loopSet;
7088 for (iSimple = 0; iSimple < nbSimple && !foundLoop; iSimple++) {
7089 const SMDS_MeshNode* n = simpleNodes[iSimple];
7090 if (!loopSet.insert( n ).second) {
7094 int iC = 0, curLast = iSimple;
7095 for (; iC < curLast; iC++) {
7096 if (simpleNodes[iC] == n) break;
7098 int loopLen = curLast - iC;
7100 // create sub-element
7102 quantities.push_back(loopLen);
7103 for (; iC < curLast; iC++) {
7104 poly_nodes.push_back(simpleNodes[iC]);
7107 // shift the rest nodes (place from the first loop position)
7108 for (iC = curLast + 1; iC < nbSimple; iC++) {
7109 simpleNodes[iC - loopLen] = simpleNodes[iC];
7111 nbSimple -= loopLen;
7114 } // for (iSimple = 0; iSimple < nbSimple; iSimple++)
7115 } // while (foundLoop)
7119 quantities.push_back(iSimple);
7120 for (int i = 0; i < iSimple; i++)
7121 poly_nodes.push_back(simpleNodes[i]);
7127 //=======================================================================
7128 //function : MergeNodes
7129 //purpose : In each group, the cdr of nodes are substituted by the first one
7131 //=======================================================================
7133 void SMESH_MeshEditor::MergeNodes (TListOfListOfNodes & theGroupsOfNodes)
7135 MESSAGE("MergeNodes");
7136 myLastCreatedElems.Clear();
7137 myLastCreatedNodes.Clear();
7139 SMESHDS_Mesh* aMesh = GetMeshDS();
7141 TNodeNodeMap nodeNodeMap; // node to replace - new node
7142 set<const SMDS_MeshElement*> elems; // all elements with changed nodes
7143 list< int > rmElemIds, rmNodeIds;
7145 // Fill nodeNodeMap and elems
7147 TListOfListOfNodes::iterator grIt = theGroupsOfNodes.begin();
7148 for ( ; grIt != theGroupsOfNodes.end(); grIt++ ) {
7149 list<const SMDS_MeshNode*>& nodes = *grIt;
7150 list<const SMDS_MeshNode*>::iterator nIt = nodes.begin();
7151 const SMDS_MeshNode* nToKeep = *nIt;
7152 //MESSAGE("node to keep " << nToKeep->GetID());
7153 for ( ++nIt; nIt != nodes.end(); nIt++ ) {
7154 const SMDS_MeshNode* nToRemove = *nIt;
7155 nodeNodeMap.insert( TNodeNodeMap::value_type( nToRemove, nToKeep ));
7156 if ( nToRemove != nToKeep ) {
7157 //MESSAGE(" node to remove " << nToRemove->GetID());
7158 rmNodeIds.push_back( nToRemove->GetID() );
7159 AddToSameGroups( nToKeep, nToRemove, aMesh );
7160 // set _alwaysComputed to a sub-mesh of VERTEX to enable mesh computing
7161 // after MergeNodes() w/o creating node in place of merged ones.
7162 const SMDS_PositionPtr& pos = nToRemove->GetPosition();
7163 if ( pos && pos->GetTypeOfPosition() == SMDS_TOP_VERTEX )
7164 if ( SMESH_subMesh* sm = myMesh->GetSubMeshContaining( nToRemove->getshapeId() ))
7165 sm->SetIsAlwaysComputed( true );
7168 SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
7169 while ( invElemIt->more() ) {
7170 const SMDS_MeshElement* elem = invElemIt->next();
7175 // Change element nodes or remove an element
7177 set<const SMDS_MeshElement*>::iterator eIt = elems.begin();
7178 for ( ; eIt != elems.end(); eIt++ ) {
7179 const SMDS_MeshElement* elem = *eIt;
7180 //MESSAGE(" ---- inverse elem on node to remove " << elem->GetID());
7181 int nbNodes = elem->NbNodes();
7182 int aShapeId = FindShape( elem );
7184 set<const SMDS_MeshNode*> nodeSet;
7185 vector< const SMDS_MeshNode*> curNodes( nbNodes ), uniqueNodes( nbNodes );
7186 int iUnique = 0, iCur = 0, nbRepl = 0;
7187 vector<int> iRepl( nbNodes );
7189 // get new seq of nodes
7190 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
7191 while ( itN->more() ) {
7192 const SMDS_MeshNode* n =
7193 static_cast<const SMDS_MeshNode*>( itN->next() );
7195 TNodeNodeMap::iterator nnIt = nodeNodeMap.find( n );
7196 if ( nnIt != nodeNodeMap.end() ) { // n sticks
7198 // BUG 0020185: begin
7200 bool stopRecur = false;
7201 set<const SMDS_MeshNode*> nodesRecur;
7202 nodesRecur.insert(n);
7203 while (!stopRecur) {
7204 TNodeNodeMap::iterator nnIt_i = nodeNodeMap.find( n );
7205 if ( nnIt_i != nodeNodeMap.end() ) { // n sticks
7206 n = (*nnIt_i).second;
7207 if (!nodesRecur.insert(n).second) {
7208 // error: recursive dependancy
7218 curNodes[ iCur ] = n;
7219 bool isUnique = nodeSet.insert( n ).second;
7221 uniqueNodes[ iUnique++ ] = n;
7223 iRepl[ nbRepl++ ] = iCur;
7227 // Analyse element topology after replacement
7230 int nbUniqueNodes = nodeSet.size();
7231 //MESSAGE("nbNodes nbUniqueNodes " << nbNodes << " " << nbUniqueNodes);
7232 if ( nbNodes != nbUniqueNodes ) { // some nodes stick
7233 // Polygons and Polyhedral volumes
7234 if (elem->IsPoly()) {
7236 if (elem->GetType() == SMDSAbs_Face) {
7238 vector<const SMDS_MeshNode *> face_nodes (nbNodes);
7240 for (; inode < nbNodes; inode++) {
7241 face_nodes[inode] = curNodes[inode];
7244 vector<const SMDS_MeshNode *> polygons_nodes;
7245 vector<int> quantities;
7246 int nbNew = SimplifyFace(face_nodes, polygons_nodes, quantities);
7249 for (int iface = 0; iface < nbNew; iface++) {
7250 int nbNodes = quantities[iface];
7251 vector<const SMDS_MeshNode *> poly_nodes (nbNodes);
7252 for (int ii = 0; ii < nbNodes; ii++, inode++) {
7253 poly_nodes[ii] = polygons_nodes[inode];
7255 SMDS_MeshElement* newElem = aMesh->AddPolygonalFace(poly_nodes);
7256 myLastCreatedElems.Append(newElem);
7258 aMesh->SetMeshElementOnShape(newElem, aShapeId);
7261 MESSAGE("ChangeElementNodes MergeNodes Polygon");
7262 //aMesh->ChangeElementNodes(elem, &polygons_nodes[inode], quantities[nbNew - 1]);
7263 vector<const SMDS_MeshNode *> polynodes(polygons_nodes.begin()+inode,polygons_nodes.end());
7265 if (nbNew > 0) quid = nbNew - 1;
7266 vector<int> newquant(quantities.begin()+quid, quantities.end());
7267 const SMDS_MeshElement* newElem = 0;
7268 newElem = aMesh->AddPolyhedralVolume(polynodes, newquant);
7269 myLastCreatedElems.Append(newElem);
7270 if ( aShapeId && newElem )
7271 aMesh->SetMeshElementOnShape( newElem, aShapeId );
7272 rmElemIds.push_back(elem->GetID());
7275 rmElemIds.push_back(elem->GetID());
7279 else if (elem->GetType() == SMDSAbs_Volume) {
7280 // Polyhedral volume
7281 if (nbUniqueNodes < 4) {
7282 rmElemIds.push_back(elem->GetID());
7285 // each face has to be analyzed in order to check volume validity
7286 const SMDS_VtkVolume* aPolyedre =
7287 dynamic_cast<const SMDS_VtkVolume*>( elem );
7289 int nbFaces = aPolyedre->NbFaces();
7291 vector<const SMDS_MeshNode *> poly_nodes;
7292 vector<int> quantities;
7294 for (int iface = 1; iface <= nbFaces; iface++) {
7295 int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
7296 vector<const SMDS_MeshNode *> faceNodes (nbFaceNodes);
7298 for (int inode = 1; inode <= nbFaceNodes; inode++) {
7299 const SMDS_MeshNode * faceNode = aPolyedre->GetFaceNode(iface, inode);
7300 TNodeNodeMap::iterator nnIt = nodeNodeMap.find(faceNode);
7301 if (nnIt != nodeNodeMap.end()) { // faceNode sticks
7302 faceNode = (*nnIt).second;
7304 faceNodes[inode - 1] = faceNode;
7307 SimplifyFace(faceNodes, poly_nodes, quantities);
7310 if (quantities.size() > 3) {
7311 // to be done: remove coincident faces
7314 if (quantities.size() > 3)
7316 MESSAGE("ChangeElementNodes MergeNodes Polyhedron");
7317 //aMesh->ChangePolyhedronNodes(elem, poly_nodes, quantities);
7318 const SMDS_MeshElement* newElem = 0;
7319 newElem = aMesh->AddPolyhedralVolume(poly_nodes, quantities);
7320 myLastCreatedElems.Append(newElem);
7321 if ( aShapeId && newElem )
7322 aMesh->SetMeshElementOnShape( newElem, aShapeId );
7323 rmElemIds.push_back(elem->GetID());
7327 rmElemIds.push_back(elem->GetID());
7338 // TODO not all the possible cases are solved. Find something more generic?
7339 switch ( nbNodes ) {
7340 case 2: ///////////////////////////////////// EDGE
7341 isOk = false; break;
7342 case 3: ///////////////////////////////////// TRIANGLE
7343 isOk = false; break;
7345 if ( elem->GetType() == SMDSAbs_Volume ) // TETRAHEDRON
7347 else { //////////////////////////////////// QUADRANGLE
7348 if ( nbUniqueNodes < 3 )
7350 else if ( nbRepl == 2 && iRepl[ 1 ] - iRepl[ 0 ] == 2 )
7351 isOk = false; // opposite nodes stick
7352 //MESSAGE("isOk " << isOk);
7355 case 6: ///////////////////////////////////// PENTAHEDRON
7356 if ( nbUniqueNodes == 4 ) {
7357 // ---------------------------------> tetrahedron
7359 iRepl[ 0 ] > 2 && iRepl[ 1 ] > 2 && iRepl[ 2 ] > 2 ) {
7360 // all top nodes stick: reverse a bottom
7361 uniqueNodes[ 0 ] = curNodes [ 1 ];
7362 uniqueNodes[ 1 ] = curNodes [ 0 ];
7364 else if (nbRepl == 3 &&
7365 iRepl[ 0 ] < 3 && iRepl[ 1 ] < 3 && iRepl[ 2 ] < 3 ) {
7366 // all bottom nodes stick: set a top before
7367 uniqueNodes[ 3 ] = uniqueNodes [ 0 ];
7368 uniqueNodes[ 0 ] = curNodes [ 3 ];
7369 uniqueNodes[ 1 ] = curNodes [ 4 ];
7370 uniqueNodes[ 2 ] = curNodes [ 5 ];
7372 else if (nbRepl == 4 &&
7373 iRepl[ 2 ] - iRepl [ 0 ] == 3 && iRepl[ 3 ] - iRepl [ 1 ] == 3 ) {
7374 // a lateral face turns into a line: reverse a bottom
7375 uniqueNodes[ 0 ] = curNodes [ 1 ];
7376 uniqueNodes[ 1 ] = curNodes [ 0 ];
7381 else if ( nbUniqueNodes == 5 ) {
7382 // PENTAHEDRON --------------------> 2 tetrahedrons
7383 if ( nbRepl == 2 && iRepl[ 1 ] - iRepl [ 0 ] == 3 ) {
7384 // a bottom node sticks with a linked top one
7386 SMDS_MeshElement* newElem =
7387 aMesh->AddVolume(curNodes[ 3 ],
7390 curNodes[ iRepl[ 0 ] == 2 ? 1 : 2 ]);
7391 myLastCreatedElems.Append(newElem);
7393 aMesh->SetMeshElementOnShape( newElem, aShapeId );
7394 // 2. : reverse a bottom
7395 uniqueNodes[ 0 ] = curNodes [ 1 ];
7396 uniqueNodes[ 1 ] = curNodes [ 0 ];
7406 if(elem->IsQuadratic()) { // Quadratic quadrangle
7418 MESSAGE("nbRepl=2: " << iRepl[0] << " " << iRepl[1]);
7421 MESSAGE("nbRepl=3: " << iRepl[0] << " " << iRepl[1] << " " << iRepl[2]);
7423 if( iRepl[0]==0 && iRepl[1]==1 && iRepl[2]==4 ) {
7424 uniqueNodes[0] = curNodes[0];
7425 uniqueNodes[1] = curNodes[2];
7426 uniqueNodes[2] = curNodes[3];
7427 uniqueNodes[3] = curNodes[5];
7428 uniqueNodes[4] = curNodes[6];
7429 uniqueNodes[5] = curNodes[7];
7432 if( iRepl[0]==0 && iRepl[1]==3 && iRepl[2]==7 ) {
7433 uniqueNodes[0] = curNodes[0];
7434 uniqueNodes[1] = curNodes[1];
7435 uniqueNodes[2] = curNodes[2];
7436 uniqueNodes[3] = curNodes[4];
7437 uniqueNodes[4] = curNodes[5];
7438 uniqueNodes[5] = curNodes[6];
7441 if( iRepl[0]==0 && iRepl[1]==4 && iRepl[2]==7 ) {
7442 uniqueNodes[0] = curNodes[1];
7443 uniqueNodes[1] = curNodes[2];
7444 uniqueNodes[2] = curNodes[3];
7445 uniqueNodes[3] = curNodes[5];
7446 uniqueNodes[4] = curNodes[6];
7447 uniqueNodes[5] = curNodes[0];
7450 if( iRepl[0]==1 && iRepl[1]==2 && iRepl[2]==5 ) {
7451 uniqueNodes[0] = curNodes[0];
7452 uniqueNodes[1] = curNodes[1];
7453 uniqueNodes[2] = curNodes[3];
7454 uniqueNodes[3] = curNodes[4];
7455 uniqueNodes[4] = curNodes[6];
7456 uniqueNodes[5] = curNodes[7];
7459 if( iRepl[0]==1 && iRepl[1]==4 && iRepl[2]==5 ) {
7460 uniqueNodes[0] = curNodes[0];
7461 uniqueNodes[1] = curNodes[2];
7462 uniqueNodes[2] = curNodes[3];
7463 uniqueNodes[3] = curNodes[1];
7464 uniqueNodes[4] = curNodes[6];
7465 uniqueNodes[5] = curNodes[7];
7468 if( iRepl[0]==2 && iRepl[1]==3 && iRepl[2]==6 ) {
7469 uniqueNodes[0] = curNodes[0];
7470 uniqueNodes[1] = curNodes[1];
7471 uniqueNodes[2] = curNodes[2];
7472 uniqueNodes[3] = curNodes[4];
7473 uniqueNodes[4] = curNodes[5];
7474 uniqueNodes[5] = curNodes[7];
7477 if( iRepl[0]==2 && iRepl[1]==5 && iRepl[2]==6 ) {
7478 uniqueNodes[0] = curNodes[0];
7479 uniqueNodes[1] = curNodes[1];
7480 uniqueNodes[2] = curNodes[3];
7481 uniqueNodes[3] = curNodes[4];
7482 uniqueNodes[4] = curNodes[2];
7483 uniqueNodes[5] = curNodes[7];
7486 if( iRepl[0]==3 && iRepl[1]==6 && iRepl[2]==7 ) {
7487 uniqueNodes[0] = curNodes[0];
7488 uniqueNodes[1] = curNodes[1];
7489 uniqueNodes[2] = curNodes[2];
7490 uniqueNodes[3] = curNodes[4];
7491 uniqueNodes[4] = curNodes[5];
7492 uniqueNodes[5] = curNodes[3];
7497 MESSAGE("nbRepl=4: " << iRepl[0] << " " << iRepl[1] << " " << iRepl[2] << " " << iRepl[3]);
7500 MESSAGE("nbRepl=5: " << iRepl[0] << " " << iRepl[1] << " " << iRepl[2] << " " << iRepl[3] << " " << iRepl[4]);
7504 //////////////////////////////////// HEXAHEDRON
7506 SMDS_VolumeTool hexa (elem);
7507 hexa.SetExternalNormal();
7508 if ( nbUniqueNodes == 4 && nbRepl == 4 ) {
7509 //////////////////////// HEX ---> 1 tetrahedron
7510 for ( int iFace = 0; iFace < 6; iFace++ ) {
7511 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7512 if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7513 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7514 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7515 // one face turns into a point ...
7516 int iOppFace = hexa.GetOppFaceIndex( iFace );
7517 ind = hexa.GetFaceNodesIndices( iOppFace );
7519 for ( iCur = 0; iCur < 4 && nbStick < 2; iCur++ ) {
7520 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7523 if ( nbStick == 1 ) {
7524 // ... and the opposite one - into a triangle.
7526 ind = hexa.GetFaceNodesIndices( iFace );
7527 uniqueNodes[ 3 ] = curNodes[ind[ 0 ]];
7534 else if ( nbUniqueNodes == 6 && nbRepl == 2 ) {
7535 //////////////////////// HEX ---> 1 prism
7536 int nbTria = 0, iTria[3];
7537 const int *ind; // indices of face nodes
7538 // look for triangular faces
7539 for ( int iFace = 0; iFace < 6 && nbTria < 3; iFace++ ) {
7540 ind = hexa.GetFaceNodesIndices( iFace );
7541 TIDSortedNodeSet faceNodes;
7542 for ( iCur = 0; iCur < 4; iCur++ )
7543 faceNodes.insert( curNodes[ind[iCur]] );
7544 if ( faceNodes.size() == 3 )
7545 iTria[ nbTria++ ] = iFace;
7547 // check if triangles are opposite
7548 if ( nbTria == 2 && iTria[0] == hexa.GetOppFaceIndex( iTria[1] ))
7551 // set nodes of the bottom triangle
7552 ind = hexa.GetFaceNodesIndices( iTria[ 0 ]);
7554 for ( iCur = 0; iCur < 4; iCur++ )
7555 if ( ind[iCur] != iRepl[0] && ind[iCur] != iRepl[1])
7556 indB.push_back( ind[iCur] );
7557 if ( !hexa.IsForward() )
7558 std::swap( indB[0], indB[2] );
7559 for ( iCur = 0; iCur < 3; iCur++ )
7560 uniqueNodes[ iCur ] = curNodes[indB[iCur]];
7561 // set nodes of the top triangle
7562 const int *indT = hexa.GetFaceNodesIndices( iTria[ 1 ]);
7563 for ( iCur = 0; iCur < 3; ++iCur )
7564 for ( int j = 0; j < 4; ++j )
7565 if ( hexa.IsLinked( indB[ iCur ], indT[ j ] ))
7567 uniqueNodes[ iCur + 3 ] = curNodes[ indT[ j ]];
7573 else if (nbUniqueNodes == 5 && nbRepl == 4 ) {
7574 //////////////////// HEXAHEDRON ---> 2 tetrahedrons
7575 for ( int iFace = 0; iFace < 6; iFace++ ) {
7576 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7577 if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7578 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7579 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7580 // one face turns into a point ...
7581 int iOppFace = hexa.GetOppFaceIndex( iFace );
7582 ind = hexa.GetFaceNodesIndices( iOppFace );
7584 iUnique = 2; // reverse a tetrahedron 1 bottom
7585 for ( iCur = 0; iCur < 4 && nbStick == 0; iCur++ ) {
7586 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7588 else if ( iUnique >= 0 )
7589 uniqueNodes[ iUnique-- ] = curNodes[ind[ iCur ]];
7591 if ( nbStick == 0 ) {
7592 // ... and the opposite one is a quadrangle
7594 const int* indTop = hexa.GetFaceNodesIndices( iFace );
7595 uniqueNodes[ 3 ] = curNodes[indTop[ 0 ]];
7598 SMDS_MeshElement* newElem =
7599 aMesh->AddVolume(curNodes[ind[ 0 ]],
7602 curNodes[indTop[ 0 ]]);
7603 myLastCreatedElems.Append(newElem);
7605 aMesh->SetMeshElementOnShape( newElem, aShapeId );
7612 else if ( nbUniqueNodes == 6 && nbRepl == 4 ) {
7613 ////////////////// HEXAHEDRON ---> 2 tetrahedrons or 1 prism
7614 // find indices of quad and tri faces
7615 int iQuadFace[ 6 ], iTriFace[ 6 ], nbQuad = 0, nbTri = 0, iFace;
7616 for ( iFace = 0; iFace < 6; iFace++ ) {
7617 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7619 for ( iCur = 0; iCur < 4; iCur++ )
7620 nodeSet.insert( curNodes[ind[ iCur ]] );
7621 nbUniqueNodes = nodeSet.size();
7622 if ( nbUniqueNodes == 3 )
7623 iTriFace[ nbTri++ ] = iFace;
7624 else if ( nbUniqueNodes == 4 )
7625 iQuadFace[ nbQuad++ ] = iFace;
7627 if (nbQuad == 2 && nbTri == 4 &&
7628 hexa.GetOppFaceIndex( iQuadFace[ 0 ] ) == iQuadFace[ 1 ]) {
7629 // 2 opposite quadrangles stuck with a diagonal;
7630 // sample groups of merged indices: (0-4)(2-6)
7631 // --------------------------------------------> 2 tetrahedrons
7632 const int *ind1 = hexa.GetFaceNodesIndices( iQuadFace[ 0 ]); // indices of quad1 nodes
7633 const int *ind2 = hexa.GetFaceNodesIndices( iQuadFace[ 1 ]);
7634 int i0, i1d, i2, i3d, i0t, i2t; // d-daigonal, t-top
7635 if (curNodes[ind1[ 0 ]] == curNodes[ind2[ 0 ]] &&
7636 curNodes[ind1[ 2 ]] == curNodes[ind2[ 2 ]]) {
7637 // stuck with 0-2 diagonal
7645 else if (curNodes[ind1[ 1 ]] == curNodes[ind2[ 3 ]] &&
7646 curNodes[ind1[ 3 ]] == curNodes[ind2[ 1 ]]) {
7647 // stuck with 1-3 diagonal
7659 uniqueNodes[ 0 ] = curNodes [ i0 ];
7660 uniqueNodes[ 1 ] = curNodes [ i1d ];
7661 uniqueNodes[ 2 ] = curNodes [ i3d ];
7662 uniqueNodes[ 3 ] = curNodes [ i0t ];
7665 SMDS_MeshElement* newElem = aMesh->AddVolume(curNodes[ i1d ],
7669 myLastCreatedElems.Append(newElem);
7671 aMesh->SetMeshElementOnShape( newElem, aShapeId );
7674 else if (( nbTri == 2 && nbQuad == 3 ) || // merged (0-4)(1-5)
7675 ( nbTri == 4 && nbQuad == 2 )) { // merged (7-4)(1-5)
7676 // --------------------------------------------> prism
7677 // find 2 opposite triangles
7679 for ( iFace = 0; iFace + 1 < nbTri; iFace++ ) {
7680 if ( hexa.GetOppFaceIndex( iTriFace[ iFace ] ) == iTriFace[ iFace + 1 ]) {
7681 // find indices of kept and replaced nodes
7682 // and fill unique nodes of 2 opposite triangles
7683 const int *ind1 = hexa.GetFaceNodesIndices( iTriFace[ iFace ]);
7684 const int *ind2 = hexa.GetFaceNodesIndices( iTriFace[ iFace + 1 ]);
7685 const SMDS_MeshNode** hexanodes = hexa.GetNodes();
7686 // fill unique nodes
7689 for ( iCur = 0; iCur < 4 && isOk; iCur++ ) {
7690 const SMDS_MeshNode* n = curNodes[ind1[ iCur ]];
7691 const SMDS_MeshNode* nInit = hexanodes[ind1[ iCur ]];
7693 // iCur of a linked node of the opposite face (make normals co-directed):
7694 int iCurOpp = ( iCur == 1 || iCur == 3 ) ? 4 - iCur : iCur;
7695 // check that correspondent corners of triangles are linked
7696 if ( !hexa.IsLinked( ind1[ iCur ], ind2[ iCurOpp ] ))
7699 uniqueNodes[ iUnique ] = n;
7700 uniqueNodes[ iUnique + 3 ] = curNodes[ind2[ iCurOpp ]];
7709 } // if ( nbUniqueNodes == 6 && nbRepl == 4 )
7712 MESSAGE("MergeNodes() removes hexahedron "<< elem);
7719 } // switch ( nbNodes )
7721 } // if ( nbNodes != nbUniqueNodes ) // some nodes stick
7723 if ( isOk ) { // the elem remains valid after sticking nodes
7724 if (elem->IsPoly() && elem->GetType() == SMDSAbs_Volume)
7726 // Change nodes of polyedre
7727 const SMDS_VtkVolume* aPolyedre =
7728 dynamic_cast<const SMDS_VtkVolume*>( elem );
7730 int nbFaces = aPolyedre->NbFaces();
7732 vector<const SMDS_MeshNode *> poly_nodes;
7733 vector<int> quantities (nbFaces);
7735 for (int iface = 1; iface <= nbFaces; iface++) {
7736 int inode, nbFaceNodes = aPolyedre->NbFaceNodes(iface);
7737 quantities[iface - 1] = nbFaceNodes;
7739 for (inode = 1; inode <= nbFaceNodes; inode++) {
7740 const SMDS_MeshNode* curNode = aPolyedre->GetFaceNode(iface, inode);
7742 TNodeNodeMap::iterator nnIt = nodeNodeMap.find( curNode );
7743 if (nnIt != nodeNodeMap.end()) { // curNode sticks
7744 curNode = (*nnIt).second;
7746 poly_nodes.push_back(curNode);
7749 aMesh->ChangePolyhedronNodes( elem, poly_nodes, quantities );
7752 else // replace non-polyhedron elements
7754 const SMDSAbs_ElementType etyp = elem->GetType();
7755 const int elemId = elem->GetID();
7756 const bool isPoly = (elem->GetEntityType() == SMDSEntity_Polygon);
7757 uniqueNodes.resize(nbUniqueNodes);
7759 SMESHDS_SubMesh * sm = aShapeId > 0 ? aMesh->MeshElements(aShapeId) : 0;
7761 aMesh->RemoveFreeElement(elem, sm, /*fromGroups=*/false);
7762 SMDS_MeshElement* newElem = this->AddElement(uniqueNodes, etyp, isPoly, elemId);
7763 if ( sm && newElem )
7764 sm->AddElement( newElem );
7765 if ( elem != newElem )
7766 ReplaceElemInGroups( elem, newElem, aMesh );
7770 // Remove invalid regular element or invalid polygon
7771 rmElemIds.push_back( elem->GetID() );
7774 } // loop on elements
7776 // Remove bad elements, then equal nodes (order important)
7778 Remove( rmElemIds, false );
7779 Remove( rmNodeIds, true );
7784 // ========================================================
7785 // class : SortableElement
7786 // purpose : allow sorting elements basing on their nodes
7787 // ========================================================
7788 class SortableElement : public set <const SMDS_MeshElement*>
7792 SortableElement( const SMDS_MeshElement* theElem )
7795 SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator();
7796 while ( nodeIt->more() )
7797 this->insert( nodeIt->next() );
7800 const SMDS_MeshElement* Get() const
7803 void Set(const SMDS_MeshElement* e) const
7808 mutable const SMDS_MeshElement* myElem;
7811 //=======================================================================
7812 //function : FindEqualElements
7813 //purpose : Return list of group of elements built on the same nodes.
7814 // Search among theElements or in the whole mesh if theElements is empty
7815 //=======================================================================
7817 void SMESH_MeshEditor::FindEqualElements(TIDSortedElemSet & theElements,
7818 TListOfListOfElementsID & theGroupsOfElementsID)
7820 myLastCreatedElems.Clear();
7821 myLastCreatedNodes.Clear();
7823 typedef map< SortableElement, int > TMapOfNodeSet;
7824 typedef list<int> TGroupOfElems;
7826 if ( theElements.empty() )
7827 { // get all elements in the mesh
7828 SMDS_ElemIteratorPtr eIt = GetMeshDS()->elementsIterator();
7829 while ( eIt->more() )
7830 theElements.insert( theElements.end(), eIt->next());
7833 vector< TGroupOfElems > arrayOfGroups;
7834 TGroupOfElems groupOfElems;
7835 TMapOfNodeSet mapOfNodeSet;
7837 TIDSortedElemSet::iterator elemIt = theElements.begin();
7838 for ( int i = 0, j=0; elemIt != theElements.end(); ++elemIt, ++j ) {
7839 const SMDS_MeshElement* curElem = *elemIt;
7840 SortableElement SE(curElem);
7843 pair< TMapOfNodeSet::iterator, bool> pp = mapOfNodeSet.insert(make_pair(SE, i));
7844 if( !(pp.second) ) {
7845 TMapOfNodeSet::iterator& itSE = pp.first;
7846 ind = (*itSE).second;
7847 arrayOfGroups[ind].push_back(curElem->GetID());
7850 groupOfElems.clear();
7851 groupOfElems.push_back(curElem->GetID());
7852 arrayOfGroups.push_back(groupOfElems);
7857 vector< TGroupOfElems >::iterator groupIt = arrayOfGroups.begin();
7858 for ( ; groupIt != arrayOfGroups.end(); ++groupIt ) {
7859 groupOfElems = *groupIt;
7860 if ( groupOfElems.size() > 1 ) {
7861 groupOfElems.sort();
7862 theGroupsOfElementsID.push_back(groupOfElems);
7867 //=======================================================================
7868 //function : MergeElements
7869 //purpose : In each given group, substitute all elements by the first one.
7870 //=======================================================================
7872 void SMESH_MeshEditor::MergeElements(TListOfListOfElementsID & theGroupsOfElementsID)
7874 myLastCreatedElems.Clear();
7875 myLastCreatedNodes.Clear();
7877 typedef list<int> TListOfIDs;
7878 TListOfIDs rmElemIds; // IDs of elems to remove
7880 SMESHDS_Mesh* aMesh = GetMeshDS();
7882 TListOfListOfElementsID::iterator groupsIt = theGroupsOfElementsID.begin();
7883 while ( groupsIt != theGroupsOfElementsID.end() ) {
7884 TListOfIDs& aGroupOfElemID = *groupsIt;
7885 aGroupOfElemID.sort();
7886 int elemIDToKeep = aGroupOfElemID.front();
7887 const SMDS_MeshElement* elemToKeep = aMesh->FindElement(elemIDToKeep);
7888 aGroupOfElemID.pop_front();
7889 TListOfIDs::iterator idIt = aGroupOfElemID.begin();
7890 while ( idIt != aGroupOfElemID.end() ) {
7891 int elemIDToRemove = *idIt;
7892 const SMDS_MeshElement* elemToRemove = aMesh->FindElement(elemIDToRemove);
7893 // add the kept element in groups of removed one (PAL15188)
7894 AddToSameGroups( elemToKeep, elemToRemove, aMesh );
7895 rmElemIds.push_back( elemIDToRemove );
7901 Remove( rmElemIds, false );
7904 //=======================================================================
7905 //function : MergeEqualElements
7906 //purpose : Remove all but one of elements built on the same nodes.
7907 //=======================================================================
7909 void SMESH_MeshEditor::MergeEqualElements()
7911 TIDSortedElemSet aMeshElements; /* empty input ==
7912 to merge equal elements in the whole mesh */
7913 TListOfListOfElementsID aGroupsOfElementsID;
7914 FindEqualElements(aMeshElements, aGroupsOfElementsID);
7915 MergeElements(aGroupsOfElementsID);
7918 //=======================================================================
7919 //function : findAdjacentFace
7921 //=======================================================================
7923 static const SMDS_MeshElement* findAdjacentFace(const SMDS_MeshNode* n1,
7924 const SMDS_MeshNode* n2,
7925 const SMDS_MeshElement* elem)
7927 TIDSortedElemSet elemSet, avoidSet;
7929 avoidSet.insert ( elem );
7930 return SMESH_MeshAlgos::FindFaceInSet( n1, n2, elemSet, avoidSet );
7933 //=======================================================================
7934 //function : FindFreeBorder
7936 //=======================================================================
7938 #define ControlFreeBorder SMESH::Controls::FreeEdges::IsFreeEdge
7940 bool SMESH_MeshEditor::FindFreeBorder (const SMDS_MeshNode* theFirstNode,
7941 const SMDS_MeshNode* theSecondNode,
7942 const SMDS_MeshNode* theLastNode,
7943 list< const SMDS_MeshNode* > & theNodes,
7944 list< const SMDS_MeshElement* >& theFaces)
7946 if ( !theFirstNode || !theSecondNode )
7948 // find border face between theFirstNode and theSecondNode
7949 const SMDS_MeshElement* curElem = findAdjacentFace( theFirstNode, theSecondNode, 0 );
7953 theFaces.push_back( curElem );
7954 theNodes.push_back( theFirstNode );
7955 theNodes.push_back( theSecondNode );
7957 //vector<const SMDS_MeshNode*> nodes;
7958 const SMDS_MeshNode *nIgnore = theFirstNode, *nStart = theSecondNode;
7959 TIDSortedElemSet foundElems;
7960 bool needTheLast = ( theLastNode != 0 );
7962 while ( nStart != theLastNode ) {
7963 if ( nStart == theFirstNode )
7964 return !needTheLast;
7966 // find all free border faces sharing form nStart
7968 list< const SMDS_MeshElement* > curElemList;
7969 list< const SMDS_MeshNode* > nStartList;
7970 SMDS_ElemIteratorPtr invElemIt = nStart->GetInverseElementIterator(SMDSAbs_Face);
7971 while ( invElemIt->more() ) {
7972 const SMDS_MeshElement* e = invElemIt->next();
7973 if ( e == curElem || foundElems.insert( e ).second ) {
7975 int iNode = 0, nbNodes = e->NbNodes();
7976 //const SMDS_MeshNode* nodes[nbNodes+1];
7977 vector<const SMDS_MeshNode*> nodes(nbNodes+1);
7979 if(e->IsQuadratic()) {
7980 const SMDS_VtkFace* F =
7981 dynamic_cast<const SMDS_VtkFace*>(e);
7982 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
7983 // use special nodes iterator
7984 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
7985 while( anIter->more() ) {
7986 nodes[ iNode++ ] = cast2Node(anIter->next());
7990 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
7991 while ( nIt->more() )
7992 nodes[ iNode++ ] = static_cast<const SMDS_MeshNode*>( nIt->next() );
7994 nodes[ iNode ] = nodes[ 0 ];
7996 for ( iNode = 0; iNode < nbNodes; iNode++ )
7997 if (((nodes[ iNode ] == nStart && nodes[ iNode + 1] != nIgnore ) ||
7998 (nodes[ iNode + 1] == nStart && nodes[ iNode ] != nIgnore )) &&
7999 ControlFreeBorder( &nodes[ iNode ], e->GetID() ))
8001 nStartList.push_back( nodes[ iNode + ( nodes[ iNode ] == nStart ? 1 : 0 )]);
8002 curElemList.push_back( e );
8006 // analyse the found
8008 int nbNewBorders = curElemList.size();
8009 if ( nbNewBorders == 0 ) {
8010 // no free border furthermore
8011 return !needTheLast;
8013 else if ( nbNewBorders == 1 ) {
8014 // one more element found
8016 nStart = nStartList.front();
8017 curElem = curElemList.front();
8018 theFaces.push_back( curElem );
8019 theNodes.push_back( nStart );
8022 // several continuations found
8023 list< const SMDS_MeshElement* >::iterator curElemIt;
8024 list< const SMDS_MeshNode* >::iterator nStartIt;
8025 // check if one of them reached the last node
8026 if ( needTheLast ) {
8027 for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
8028 curElemIt!= curElemList.end();
8029 curElemIt++, nStartIt++ )
8030 if ( *nStartIt == theLastNode ) {
8031 theFaces.push_back( *curElemIt );
8032 theNodes.push_back( *nStartIt );
8036 // find the best free border by the continuations
8037 list<const SMDS_MeshNode*> contNodes[ 2 ], *cNL;
8038 list<const SMDS_MeshElement*> contFaces[ 2 ], *cFL;
8039 for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
8040 curElemIt!= curElemList.end();
8041 curElemIt++, nStartIt++ )
8043 cNL = & contNodes[ contNodes[0].empty() ? 0 : 1 ];
8044 cFL = & contFaces[ contFaces[0].empty() ? 0 : 1 ];
8045 // find one more free border
8046 if ( ! SMESH_MeshEditor::FindFreeBorder( nStart, *nStartIt, theLastNode, *cNL, *cFL )) {
8050 else if ( !contNodes[0].empty() && !contNodes[1].empty() ) {
8051 // choice: clear a worse one
8052 int iLongest = ( contNodes[0].size() < contNodes[1].size() ? 1 : 0 );
8053 int iWorse = ( needTheLast ? 1 - iLongest : iLongest );
8054 contNodes[ iWorse ].clear();
8055 contFaces[ iWorse ].clear();
8058 if ( contNodes[0].empty() && contNodes[1].empty() )
8061 // append the best free border
8062 cNL = & contNodes[ contNodes[0].empty() ? 1 : 0 ];
8063 cFL = & contFaces[ contFaces[0].empty() ? 1 : 0 ];
8064 theNodes.pop_back(); // remove nIgnore
8065 theNodes.pop_back(); // remove nStart
8066 theFaces.pop_back(); // remove curElem
8067 list< const SMDS_MeshNode* >::iterator nIt = cNL->begin();
8068 list< const SMDS_MeshElement* >::iterator fIt = cFL->begin();
8069 for ( ; nIt != cNL->end(); nIt++ ) theNodes.push_back( *nIt );
8070 for ( ; fIt != cFL->end(); fIt++ ) theFaces.push_back( *fIt );
8073 } // several continuations found
8074 } // while ( nStart != theLastNode )
8079 //=======================================================================
8080 //function : CheckFreeBorderNodes
8081 //purpose : Return true if the tree nodes are on a free border
8082 //=======================================================================
8084 bool SMESH_MeshEditor::CheckFreeBorderNodes(const SMDS_MeshNode* theNode1,
8085 const SMDS_MeshNode* theNode2,
8086 const SMDS_MeshNode* theNode3)
8088 list< const SMDS_MeshNode* > nodes;
8089 list< const SMDS_MeshElement* > faces;
8090 return FindFreeBorder( theNode1, theNode2, theNode3, nodes, faces);
8093 //=======================================================================
8094 //function : SewFreeBorder
8096 //=======================================================================
8098 SMESH_MeshEditor::Sew_Error
8099 SMESH_MeshEditor::SewFreeBorder (const SMDS_MeshNode* theBordFirstNode,
8100 const SMDS_MeshNode* theBordSecondNode,
8101 const SMDS_MeshNode* theBordLastNode,
8102 const SMDS_MeshNode* theSideFirstNode,
8103 const SMDS_MeshNode* theSideSecondNode,
8104 const SMDS_MeshNode* theSideThirdNode,
8105 const bool theSideIsFreeBorder,
8106 const bool toCreatePolygons,
8107 const bool toCreatePolyedrs)
8109 myLastCreatedElems.Clear();
8110 myLastCreatedNodes.Clear();
8112 MESSAGE("::SewFreeBorder()");
8113 Sew_Error aResult = SEW_OK;
8115 // ====================================
8116 // find side nodes and elements
8117 // ====================================
8119 list< const SMDS_MeshNode* > nSide[ 2 ];
8120 list< const SMDS_MeshElement* > eSide[ 2 ];
8121 list< const SMDS_MeshNode* >::iterator nIt[ 2 ];
8122 list< const SMDS_MeshElement* >::iterator eIt[ 2 ];
8126 if (!FindFreeBorder(theBordFirstNode,theBordSecondNode,theBordLastNode,
8127 nSide[0], eSide[0])) {
8128 MESSAGE(" Free Border 1 not found " );
8129 aResult = SEW_BORDER1_NOT_FOUND;
8131 if (theSideIsFreeBorder) {
8134 if (!FindFreeBorder(theSideFirstNode, theSideSecondNode, theSideThirdNode,
8135 nSide[1], eSide[1])) {
8136 MESSAGE(" Free Border 2 not found " );
8137 aResult = ( aResult != SEW_OK ? SEW_BOTH_BORDERS_NOT_FOUND : SEW_BORDER2_NOT_FOUND );
8140 if ( aResult != SEW_OK )
8143 if (!theSideIsFreeBorder) {
8147 // -------------------------------------------------------------------------
8149 // 1. If nodes to merge are not coincident, move nodes of the free border
8150 // from the coord sys defined by the direction from the first to last
8151 // nodes of the border to the correspondent sys of the side 2
8152 // 2. On the side 2, find the links most co-directed with the correspondent
8153 // links of the free border
8154 // -------------------------------------------------------------------------
8156 // 1. Since sewing may break if there are volumes to split on the side 2,
8157 // we wont move nodes but just compute new coordinates for them
8158 typedef map<const SMDS_MeshNode*, gp_XYZ> TNodeXYZMap;
8159 TNodeXYZMap nBordXYZ;
8160 list< const SMDS_MeshNode* >& bordNodes = nSide[ 0 ];
8161 list< const SMDS_MeshNode* >::iterator nBordIt;
8163 gp_XYZ Pb1( theBordFirstNode->X(), theBordFirstNode->Y(), theBordFirstNode->Z() );
8164 gp_XYZ Pb2( theBordLastNode->X(), theBordLastNode->Y(), theBordLastNode->Z() );
8165 gp_XYZ Ps1( theSideFirstNode->X(), theSideFirstNode->Y(), theSideFirstNode->Z() );
8166 gp_XYZ Ps2( theSideSecondNode->X(), theSideSecondNode->Y(), theSideSecondNode->Z() );
8167 double tol2 = 1.e-8;
8168 gp_Vec Vbs1( Pb1 - Ps1 ),Vbs2( Pb2 - Ps2 );
8169 if ( Vbs1.SquareMagnitude() > tol2 || Vbs2.SquareMagnitude() > tol2 ) {
8170 // Need node movement.
8172 // find X and Z axes to create trsf
8173 gp_Vec Zb( Pb1 - Pb2 ), Zs( Ps1 - Ps2 );
8175 if ( X.SquareMagnitude() <= gp::Resolution() * gp::Resolution() )
8177 X = gp_Ax2( gp::Origin(), Zb ).XDirection();
8180 gp_Ax3 toBordAx( Pb1, Zb, X );
8181 gp_Ax3 fromSideAx( Ps1, Zs, X );
8182 gp_Ax3 toGlobalAx( gp::Origin(), gp::DZ(), gp::DX() );
8184 gp_Trsf toBordSys, fromSide2Sys;
8185 toBordSys.SetTransformation( toBordAx );
8186 fromSide2Sys.SetTransformation( fromSideAx, toGlobalAx );
8187 fromSide2Sys.SetScaleFactor( Zs.Magnitude() / Zb.Magnitude() );
8190 for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
8191 const SMDS_MeshNode* n = *nBordIt;
8192 gp_XYZ xyz( n->X(),n->Y(),n->Z() );
8193 toBordSys.Transforms( xyz );
8194 fromSide2Sys.Transforms( xyz );
8195 nBordXYZ.insert( TNodeXYZMap::value_type( n, xyz ));
8199 // just insert nodes XYZ in the nBordXYZ map
8200 for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
8201 const SMDS_MeshNode* n = *nBordIt;
8202 nBordXYZ.insert( TNodeXYZMap::value_type( n, gp_XYZ( n->X(),n->Y(),n->Z() )));
8206 // 2. On the side 2, find the links most co-directed with the correspondent
8207 // links of the free border
8209 list< const SMDS_MeshElement* >& sideElems = eSide[ 1 ];
8210 list< const SMDS_MeshNode* >& sideNodes = nSide[ 1 ];
8211 sideNodes.push_back( theSideFirstNode );
8213 bool hasVolumes = false;
8214 LinkID_Gen aLinkID_Gen( GetMeshDS() );
8215 set<long> foundSideLinkIDs, checkedLinkIDs;
8216 SMDS_VolumeTool volume;
8217 //const SMDS_MeshNode* faceNodes[ 4 ];
8219 const SMDS_MeshNode* sideNode;
8220 const SMDS_MeshElement* sideElem;
8221 const SMDS_MeshNode* prevSideNode = theSideFirstNode;
8222 const SMDS_MeshNode* prevBordNode = theBordFirstNode;
8223 nBordIt = bordNodes.begin();
8225 // border node position and border link direction to compare with
8226 gp_XYZ bordPos = nBordXYZ[ *nBordIt ];
8227 gp_XYZ bordDir = bordPos - nBordXYZ[ prevBordNode ];
8228 // choose next side node by link direction or by closeness to
8229 // the current border node:
8230 bool searchByDir = ( *nBordIt != theBordLastNode );
8232 // find the next node on the Side 2
8234 double maxDot = -DBL_MAX, minDist = DBL_MAX;
8236 checkedLinkIDs.clear();
8237 gp_XYZ prevXYZ( prevSideNode->X(), prevSideNode->Y(), prevSideNode->Z() );
8239 // loop on inverse elements of current node (prevSideNode) on the Side 2
8240 SMDS_ElemIteratorPtr invElemIt = prevSideNode->GetInverseElementIterator();
8241 while ( invElemIt->more() )
8243 const SMDS_MeshElement* elem = invElemIt->next();
8244 // prepare data for a loop on links coming to prevSideNode, of a face or a volume
8245 int iPrevNode, iNode = 0, nbNodes = elem->NbNodes();
8246 vector< const SMDS_MeshNode* > faceNodes( nbNodes, (const SMDS_MeshNode*)0 );
8247 bool isVolume = volume.Set( elem );
8248 const SMDS_MeshNode** nodes = isVolume ? volume.GetNodes() : & faceNodes[0];
8249 if ( isVolume ) // --volume
8251 else if ( elem->GetType()==SMDSAbs_Face ) { // --face
8252 // retrieve all face nodes and find iPrevNode - an index of the prevSideNode
8253 if(elem->IsQuadratic()) {
8254 const SMDS_VtkFace* F =
8255 dynamic_cast<const SMDS_VtkFace*>(elem);
8256 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8257 // use special nodes iterator
8258 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8259 while( anIter->more() ) {
8260 nodes[ iNode ] = cast2Node(anIter->next());
8261 if ( nodes[ iNode++ ] == prevSideNode )
8262 iPrevNode = iNode - 1;
8266 SMDS_ElemIteratorPtr nIt = elem->nodesIterator();
8267 while ( nIt->more() ) {
8268 nodes[ iNode ] = cast2Node( nIt->next() );
8269 if ( nodes[ iNode++ ] == prevSideNode )
8270 iPrevNode = iNode - 1;
8273 // there are 2 links to check
8278 // loop on links, to be precise, on the second node of links
8279 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
8280 const SMDS_MeshNode* n = nodes[ iNode ];
8282 if ( !volume.IsLinked( n, prevSideNode ))
8286 if ( iNode ) // a node before prevSideNode
8287 n = nodes[ iPrevNode == 0 ? elem->NbNodes() - 1 : iPrevNode - 1 ];
8288 else // a node after prevSideNode
8289 n = nodes[ iPrevNode + 1 == elem->NbNodes() ? 0 : iPrevNode + 1 ];
8291 // check if this link was already used
8292 long iLink = aLinkID_Gen.GetLinkID( prevSideNode, n );
8293 bool isJustChecked = !checkedLinkIDs.insert( iLink ).second;
8294 if (!isJustChecked &&
8295 foundSideLinkIDs.find( iLink ) == foundSideLinkIDs.end() )
8297 // test a link geometrically
8298 gp_XYZ nextXYZ ( n->X(), n->Y(), n->Z() );
8299 bool linkIsBetter = false;
8300 double dot = 0.0, dist = 0.0;
8301 if ( searchByDir ) { // choose most co-directed link
8302 dot = bordDir * ( nextXYZ - prevXYZ ).Normalized();
8303 linkIsBetter = ( dot > maxDot );
8305 else { // choose link with the node closest to bordPos
8306 dist = ( nextXYZ - bordPos ).SquareModulus();
8307 linkIsBetter = ( dist < minDist );
8309 if ( linkIsBetter ) {
8318 } // loop on inverse elements of prevSideNode
8321 MESSAGE(" Cant find path by links of the Side 2 ");
8322 return SEW_BAD_SIDE_NODES;
8324 sideNodes.push_back( sideNode );
8325 sideElems.push_back( sideElem );
8326 foundSideLinkIDs.insert ( linkID );
8327 prevSideNode = sideNode;
8329 if ( *nBordIt == theBordLastNode )
8330 searchByDir = false;
8332 // find the next border link to compare with
8333 gp_XYZ sidePos( sideNode->X(), sideNode->Y(), sideNode->Z() );
8334 searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8335 // move to next border node if sideNode is before forward border node (bordPos)
8336 while ( *nBordIt != theBordLastNode && !searchByDir ) {
8337 prevBordNode = *nBordIt;
8339 bordPos = nBordXYZ[ *nBordIt ];
8340 bordDir = bordPos - nBordXYZ[ prevBordNode ];
8341 searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8345 while ( sideNode != theSideSecondNode );
8347 if ( hasVolumes && sideNodes.size () != bordNodes.size() && !toCreatePolyedrs) {
8348 MESSAGE("VOLUME SPLITTING IS FORBIDDEN");
8349 return SEW_VOLUMES_TO_SPLIT; // volume splitting is forbidden
8351 } // end nodes search on the side 2
8353 // ============================
8354 // sew the border to the side 2
8355 // ============================
8357 int nbNodes[] = { nSide[0].size(), nSide[1].size() };
8358 int maxNbNodes = Max( nbNodes[0], nbNodes[1] );
8360 TListOfListOfNodes nodeGroupsToMerge;
8361 if ( nbNodes[0] == nbNodes[1] ||
8362 ( theSideIsFreeBorder && !theSideThirdNode)) {
8364 // all nodes are to be merged
8366 for (nIt[0] = nSide[0].begin(), nIt[1] = nSide[1].begin();
8367 nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end();
8368 nIt[0]++, nIt[1]++ )
8370 nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8371 nodeGroupsToMerge.back().push_back( *nIt[1] ); // to keep
8372 nodeGroupsToMerge.back().push_back( *nIt[0] ); // to remove
8377 // insert new nodes into the border and the side to get equal nb of segments
8379 // get normalized parameters of nodes on the borders
8380 //double param[ 2 ][ maxNbNodes ];
8382 param[0] = new double [ maxNbNodes ];
8383 param[1] = new double [ maxNbNodes ];
8385 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8386 list< const SMDS_MeshNode* >& nodes = nSide[ iBord ];
8387 list< const SMDS_MeshNode* >::iterator nIt = nodes.begin();
8388 const SMDS_MeshNode* nPrev = *nIt;
8389 double bordLength = 0;
8390 for ( iNode = 0; nIt != nodes.end(); nIt++, iNode++ ) { // loop on border nodes
8391 const SMDS_MeshNode* nCur = *nIt;
8392 gp_XYZ segment (nCur->X() - nPrev->X(),
8393 nCur->Y() - nPrev->Y(),
8394 nCur->Z() - nPrev->Z());
8395 double segmentLen = segment.Modulus();
8396 bordLength += segmentLen;
8397 param[ iBord ][ iNode ] = bordLength;
8400 // normalize within [0,1]
8401 for ( iNode = 0; iNode < nbNodes[ iBord ]; iNode++ ) {
8402 param[ iBord ][ iNode ] /= bordLength;
8406 // loop on border segments
8407 const SMDS_MeshNode *nPrev[ 2 ] = { 0, 0 };
8408 int i[ 2 ] = { 0, 0 };
8409 nIt[0] = nSide[0].begin(); eIt[0] = eSide[0].begin();
8410 nIt[1] = nSide[1].begin(); eIt[1] = eSide[1].begin();
8412 TElemOfNodeListMap insertMap;
8413 TElemOfNodeListMap::iterator insertMapIt;
8415 // key: elem to insert nodes into
8416 // value: 2 nodes to insert between + nodes to be inserted
8418 bool next[ 2 ] = { false, false };
8420 // find min adjacent segment length after sewing
8421 double nextParam = 10., prevParam = 0;
8422 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8423 if ( i[ iBord ] + 1 < nbNodes[ iBord ])
8424 nextParam = Min( nextParam, param[iBord][ i[iBord] + 1 ]);
8425 if ( i[ iBord ] > 0 )
8426 prevParam = Max( prevParam, param[iBord][ i[iBord] - 1 ]);
8428 double minParam = Min( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8429 double maxParam = Max( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8430 double minSegLen = Min( nextParam - minParam, maxParam - prevParam );
8432 // choose to insert or to merge nodes
8433 double du = param[ 1 ][ i[1] ] - param[ 0 ][ i[0] ];
8434 if ( Abs( du ) <= minSegLen * 0.2 ) {
8437 nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8438 const SMDS_MeshNode* n0 = *nIt[0];
8439 const SMDS_MeshNode* n1 = *nIt[1];
8440 nodeGroupsToMerge.back().push_back( n1 );
8441 nodeGroupsToMerge.back().push_back( n0 );
8442 // position of node of the border changes due to merge
8443 param[ 0 ][ i[0] ] += du;
8444 // move n1 for the sake of elem shape evaluation during insertion.
8445 // n1 will be removed by MergeNodes() anyway
8446 const_cast<SMDS_MeshNode*>( n0 )->setXYZ( n1->X(), n1->Y(), n1->Z() );
8447 next[0] = next[1] = true;
8452 int intoBord = ( du < 0 ) ? 0 : 1;
8453 const SMDS_MeshElement* elem = *eIt[ intoBord ];
8454 const SMDS_MeshNode* n1 = nPrev[ intoBord ];
8455 const SMDS_MeshNode* n2 = *nIt[ intoBord ];
8456 const SMDS_MeshNode* nIns = *nIt[ 1 - intoBord ];
8457 if ( intoBord == 1 ) {
8458 // move node of the border to be on a link of elem of the side
8459 gp_XYZ p1 (n1->X(), n1->Y(), n1->Z());
8460 gp_XYZ p2 (n2->X(), n2->Y(), n2->Z());
8461 double ratio = du / ( param[ 1 ][ i[1] ] - param[ 1 ][ i[1]-1 ]);
8462 gp_XYZ p = p2 * ( 1 - ratio ) + p1 * ratio;
8463 GetMeshDS()->MoveNode( nIns, p.X(), p.Y(), p.Z() );
8465 insertMapIt = insertMap.find( elem );
8466 bool notFound = ( insertMapIt == insertMap.end() );
8467 bool otherLink = ( !notFound && (*insertMapIt).second.front() != n1 );
8469 // insert into another link of the same element:
8470 // 1. perform insertion into the other link of the elem
8471 list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8472 const SMDS_MeshNode* n12 = nodeList.front(); nodeList.pop_front();
8473 const SMDS_MeshNode* n22 = nodeList.front(); nodeList.pop_front();
8474 InsertNodesIntoLink( elem, n12, n22, nodeList, toCreatePolygons );
8475 // 2. perform insertion into the link of adjacent faces
8477 const SMDS_MeshElement* adjElem = findAdjacentFace( n12, n22, elem );
8479 InsertNodesIntoLink( adjElem, n12, n22, nodeList, toCreatePolygons );
8483 if (toCreatePolyedrs) {
8484 // perform insertion into the links of adjacent volumes
8485 UpdateVolumes(n12, n22, nodeList);
8487 // 3. find an element appeared on n1 and n2 after the insertion
8488 insertMap.erase( elem );
8489 elem = findAdjacentFace( n1, n2, 0 );
8491 if ( notFound || otherLink ) {
8492 // add element and nodes of the side into the insertMap
8493 insertMapIt = insertMap.insert
8494 ( TElemOfNodeListMap::value_type( elem, list<const SMDS_MeshNode*>() )).first;
8495 (*insertMapIt).second.push_back( n1 );
8496 (*insertMapIt).second.push_back( n2 );
8498 // add node to be inserted into elem
8499 (*insertMapIt).second.push_back( nIns );
8500 next[ 1 - intoBord ] = true;
8503 // go to the next segment
8504 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8505 if ( next[ iBord ] ) {
8506 if ( i[ iBord ] != 0 && eIt[ iBord ] != eSide[ iBord ].end())
8508 nPrev[ iBord ] = *nIt[ iBord ];
8509 nIt[ iBord ]++; i[ iBord ]++;
8513 while ( nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end());
8515 // perform insertion of nodes into elements
8517 for (insertMapIt = insertMap.begin();
8518 insertMapIt != insertMap.end();
8521 const SMDS_MeshElement* elem = (*insertMapIt).first;
8522 list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8523 const SMDS_MeshNode* n1 = nodeList.front(); nodeList.pop_front();
8524 const SMDS_MeshNode* n2 = nodeList.front(); nodeList.pop_front();
8526 InsertNodesIntoLink( elem, n1, n2, nodeList, toCreatePolygons );
8528 if ( !theSideIsFreeBorder ) {
8529 // look for and insert nodes into the faces adjacent to elem
8531 const SMDS_MeshElement* adjElem = findAdjacentFace( n1, n2, elem );
8533 InsertNodesIntoLink( adjElem, n1, n2, nodeList, toCreatePolygons );
8538 if (toCreatePolyedrs) {
8539 // perform insertion into the links of adjacent volumes
8540 UpdateVolumes(n1, n2, nodeList);
8546 } // end: insert new nodes
8548 MergeNodes ( nodeGroupsToMerge );
8553 //=======================================================================
8554 //function : InsertNodesIntoLink
8555 //purpose : insert theNodesToInsert into theFace between theBetweenNode1
8556 // and theBetweenNode2 and split theElement
8557 //=======================================================================
8559 void SMESH_MeshEditor::InsertNodesIntoLink(const SMDS_MeshElement* theFace,
8560 const SMDS_MeshNode* theBetweenNode1,
8561 const SMDS_MeshNode* theBetweenNode2,
8562 list<const SMDS_MeshNode*>& theNodesToInsert,
8563 const bool toCreatePoly)
8565 if ( theFace->GetType() != SMDSAbs_Face ) return;
8567 // find indices of 2 link nodes and of the rest nodes
8568 int iNode = 0, il1, il2, i3, i4;
8569 il1 = il2 = i3 = i4 = -1;
8570 //const SMDS_MeshNode* nodes[ theFace->NbNodes() ];
8571 vector<const SMDS_MeshNode*> nodes( theFace->NbNodes() );
8573 if(theFace->IsQuadratic()) {
8574 const SMDS_VtkFace* F =
8575 dynamic_cast<const SMDS_VtkFace*>(theFace);
8576 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8577 // use special nodes iterator
8578 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8579 while( anIter->more() ) {
8580 const SMDS_MeshNode* n = cast2Node(anIter->next());
8581 if ( n == theBetweenNode1 )
8583 else if ( n == theBetweenNode2 )
8589 nodes[ iNode++ ] = n;
8593 SMDS_ElemIteratorPtr nodeIt = theFace->nodesIterator();
8594 while ( nodeIt->more() ) {
8595 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8596 if ( n == theBetweenNode1 )
8598 else if ( n == theBetweenNode2 )
8604 nodes[ iNode++ ] = n;
8607 if ( il1 < 0 || il2 < 0 || i3 < 0 )
8610 // arrange link nodes to go one after another regarding the face orientation
8611 bool reverse = ( Abs( il2 - il1 ) == 1 ? il2 < il1 : il1 < il2 );
8612 list<const SMDS_MeshNode *> aNodesToInsert = theNodesToInsert;
8617 aNodesToInsert.reverse();
8619 // check that not link nodes of a quadrangles are in good order
8620 int nbFaceNodes = theFace->NbNodes();
8621 if ( nbFaceNodes == 4 && i4 - i3 != 1 ) {
8627 if (toCreatePoly || theFace->IsPoly()) {
8630 vector<const SMDS_MeshNode *> poly_nodes (nbFaceNodes + aNodesToInsert.size());
8632 // add nodes of face up to first node of link
8635 if(theFace->IsQuadratic()) {
8636 const SMDS_VtkFace* F =
8637 dynamic_cast<const SMDS_VtkFace*>(theFace);
8638 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8639 // use special nodes iterator
8640 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8641 while( anIter->more() && !isFLN ) {
8642 const SMDS_MeshNode* n = cast2Node(anIter->next());
8643 poly_nodes[iNode++] = n;
8644 if (n == nodes[il1]) {
8648 // add nodes to insert
8649 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8650 for (; nIt != aNodesToInsert.end(); nIt++) {
8651 poly_nodes[iNode++] = *nIt;
8653 // add nodes of face starting from last node of link
8654 while ( anIter->more() ) {
8655 poly_nodes[iNode++] = cast2Node(anIter->next());
8659 SMDS_ElemIteratorPtr nodeIt = theFace->nodesIterator();
8660 while ( nodeIt->more() && !isFLN ) {
8661 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8662 poly_nodes[iNode++] = n;
8663 if (n == nodes[il1]) {
8667 // add nodes to insert
8668 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8669 for (; nIt != aNodesToInsert.end(); nIt++) {
8670 poly_nodes[iNode++] = *nIt;
8672 // add nodes of face starting from last node of link
8673 while ( nodeIt->more() ) {
8674 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8675 poly_nodes[iNode++] = n;
8679 // edit or replace the face
8680 SMESHDS_Mesh *aMesh = GetMeshDS();
8682 if (theFace->IsPoly()) {
8683 aMesh->ChangePolygonNodes(theFace, poly_nodes);
8686 int aShapeId = FindShape( theFace );
8688 SMDS_MeshElement* newElem = aMesh->AddPolygonalFace(poly_nodes);
8689 myLastCreatedElems.Append(newElem);
8690 if ( aShapeId && newElem )
8691 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8693 aMesh->RemoveElement(theFace);
8698 SMESHDS_Mesh *aMesh = GetMeshDS();
8699 if( !theFace->IsQuadratic() ) {
8701 // put aNodesToInsert between theBetweenNode1 and theBetweenNode2
8702 int nbLinkNodes = 2 + aNodesToInsert.size();
8703 //const SMDS_MeshNode* linkNodes[ nbLinkNodes ];
8704 vector<const SMDS_MeshNode*> linkNodes( nbLinkNodes );
8705 linkNodes[ 0 ] = nodes[ il1 ];
8706 linkNodes[ nbLinkNodes - 1 ] = nodes[ il2 ];
8707 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8708 for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
8709 linkNodes[ iNode++ ] = *nIt;
8711 // decide how to split a quadrangle: compare possible variants
8712 // and choose which of splits to be a quadrangle
8713 int i1, i2, iSplit, nbSplits = nbLinkNodes - 1, iBestQuad;
8714 if ( nbFaceNodes == 3 ) {
8715 iBestQuad = nbSplits;
8718 else if ( nbFaceNodes == 4 ) {
8719 SMESH::Controls::NumericalFunctorPtr aCrit( new SMESH::Controls::AspectRatio);
8720 double aBestRate = DBL_MAX;
8721 for ( int iQuad = 0; iQuad < nbSplits; iQuad++ ) {
8723 double aBadRate = 0;
8724 // evaluate elements quality
8725 for ( iSplit = 0; iSplit < nbSplits; iSplit++ ) {
8726 if ( iSplit == iQuad ) {
8727 SMDS_FaceOfNodes quad (linkNodes[ i1++ ],
8731 aBadRate += getBadRate( &quad, aCrit );
8734 SMDS_FaceOfNodes tria (linkNodes[ i1++ ],
8736 nodes[ iSplit < iQuad ? i4 : i3 ]);
8737 aBadRate += getBadRate( &tria, aCrit );
8741 if ( aBadRate < aBestRate ) {
8743 aBestRate = aBadRate;
8748 // create new elements
8749 int aShapeId = FindShape( theFace );
8752 for ( iSplit = 0; iSplit < nbSplits - 1; iSplit++ ) {
8753 SMDS_MeshElement* newElem = 0;
8754 if ( iSplit == iBestQuad )
8755 newElem = aMesh->AddFace (linkNodes[ i1++ ],
8760 newElem = aMesh->AddFace (linkNodes[ i1++ ],
8762 nodes[ iSplit < iBestQuad ? i4 : i3 ]);
8763 myLastCreatedElems.Append(newElem);
8764 if ( aShapeId && newElem )
8765 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8768 // change nodes of theFace
8769 const SMDS_MeshNode* newNodes[ 4 ];
8770 newNodes[ 0 ] = linkNodes[ i1 ];
8771 newNodes[ 1 ] = linkNodes[ i2 ];
8772 newNodes[ 2 ] = nodes[ iSplit >= iBestQuad ? i3 : i4 ];
8773 newNodes[ 3 ] = nodes[ i4 ];
8774 //aMesh->ChangeElementNodes( theFace, newNodes, iSplit == iBestQuad ? 4 : 3 );
8775 const SMDS_MeshElement* newElem = 0;
8776 if (iSplit == iBestQuad)
8777 newElem = aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2], newNodes[3] );
8779 newElem = aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2] );
8780 myLastCreatedElems.Append(newElem);
8781 if ( aShapeId && newElem )
8782 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8783 } // end if(!theFace->IsQuadratic())
8784 else { // theFace is quadratic
8785 // we have to split theFace on simple triangles and one simple quadrangle
8787 int nbshift = tmp*2;
8788 // shift nodes in nodes[] by nbshift
8790 for(i=0; i<nbshift; i++) {
8791 const SMDS_MeshNode* n = nodes[0];
8792 for(j=0; j<nbFaceNodes-1; j++) {
8793 nodes[j] = nodes[j+1];
8795 nodes[nbFaceNodes-1] = n;
8797 il1 = il1 - nbshift;
8798 // now have to insert nodes between n0 and n1 or n1 and n2 (see below)
8799 // n0 n1 n2 n0 n1 n2
8800 // +-----+-----+ +-----+-----+
8809 // create new elements
8810 int aShapeId = FindShape( theFace );
8813 if(nbFaceNodes==6) { // quadratic triangle
8814 SMDS_MeshElement* newElem =
8815 aMesh->AddFace(nodes[3],nodes[4],nodes[5]);
8816 myLastCreatedElems.Append(newElem);
8817 if ( aShapeId && newElem )
8818 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8819 if(theFace->IsMediumNode(nodes[il1])) {
8820 // create quadrangle
8821 newElem = aMesh->AddFace(nodes[0],nodes[1],nodes[3],nodes[5]);
8822 myLastCreatedElems.Append(newElem);
8823 if ( aShapeId && newElem )
8824 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8830 // create quadrangle
8831 newElem = aMesh->AddFace(nodes[1],nodes[2],nodes[3],nodes[5]);
8832 myLastCreatedElems.Append(newElem);
8833 if ( aShapeId && newElem )
8834 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8840 else { // nbFaceNodes==8 - quadratic quadrangle
8841 SMDS_MeshElement* newElem =
8842 aMesh->AddFace(nodes[3],nodes[4],nodes[5]);
8843 myLastCreatedElems.Append(newElem);
8844 if ( aShapeId && newElem )
8845 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8846 newElem = aMesh->AddFace(nodes[5],nodes[6],nodes[7]);
8847 myLastCreatedElems.Append(newElem);
8848 if ( aShapeId && newElem )
8849 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8850 newElem = aMesh->AddFace(nodes[5],nodes[7],nodes[3]);
8851 myLastCreatedElems.Append(newElem);
8852 if ( aShapeId && newElem )
8853 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8854 if(theFace->IsMediumNode(nodes[il1])) {
8855 // create quadrangle
8856 newElem = aMesh->AddFace(nodes[0],nodes[1],nodes[3],nodes[7]);
8857 myLastCreatedElems.Append(newElem);
8858 if ( aShapeId && newElem )
8859 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8865 // create quadrangle
8866 newElem = aMesh->AddFace(nodes[1],nodes[2],nodes[3],nodes[7]);
8867 myLastCreatedElems.Append(newElem);
8868 if ( aShapeId && newElem )
8869 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8875 // create needed triangles using n1,n2,n3 and inserted nodes
8876 int nbn = 2 + aNodesToInsert.size();
8877 //const SMDS_MeshNode* aNodes[nbn];
8878 vector<const SMDS_MeshNode*> aNodes(nbn);
8879 aNodes[0] = nodes[n1];
8880 aNodes[nbn-1] = nodes[n2];
8881 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8882 for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
8883 aNodes[iNode++] = *nIt;
8885 for(i=1; i<nbn; i++) {
8886 SMDS_MeshElement* newElem =
8887 aMesh->AddFace(aNodes[i-1],aNodes[i],nodes[n3]);
8888 myLastCreatedElems.Append(newElem);
8889 if ( aShapeId && newElem )
8890 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8894 aMesh->RemoveElement(theFace);
8897 //=======================================================================
8898 //function : UpdateVolumes
8900 //=======================================================================
8901 void SMESH_MeshEditor::UpdateVolumes (const SMDS_MeshNode* theBetweenNode1,
8902 const SMDS_MeshNode* theBetweenNode2,
8903 list<const SMDS_MeshNode*>& theNodesToInsert)
8905 myLastCreatedElems.Clear();
8906 myLastCreatedNodes.Clear();
8908 SMDS_ElemIteratorPtr invElemIt = theBetweenNode1->GetInverseElementIterator(SMDSAbs_Volume);
8909 while (invElemIt->more()) { // loop on inverse elements of theBetweenNode1
8910 const SMDS_MeshElement* elem = invElemIt->next();
8912 // check, if current volume has link theBetweenNode1 - theBetweenNode2
8913 SMDS_VolumeTool aVolume (elem);
8914 if (!aVolume.IsLinked(theBetweenNode1, theBetweenNode2))
8917 // insert new nodes in all faces of the volume, sharing link theBetweenNode1 - theBetweenNode2
8918 int iface, nbFaces = aVolume.NbFaces();
8919 vector<const SMDS_MeshNode *> poly_nodes;
8920 vector<int> quantities (nbFaces);
8922 for (iface = 0; iface < nbFaces; iface++) {
8923 int nbFaceNodes = aVolume.NbFaceNodes(iface), nbInserted = 0;
8924 // faceNodes will contain (nbFaceNodes + 1) nodes, last = first
8925 const SMDS_MeshNode** faceNodes = aVolume.GetFaceNodes(iface);
8927 for (int inode = 0; inode < nbFaceNodes; inode++) {
8928 poly_nodes.push_back(faceNodes[inode]);
8930 if (nbInserted == 0) {
8931 if (faceNodes[inode] == theBetweenNode1) {
8932 if (faceNodes[inode + 1] == theBetweenNode2) {
8933 nbInserted = theNodesToInsert.size();
8935 // add nodes to insert
8936 list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.begin();
8937 for (; nIt != theNodesToInsert.end(); nIt++) {
8938 poly_nodes.push_back(*nIt);
8942 else if (faceNodes[inode] == theBetweenNode2) {
8943 if (faceNodes[inode + 1] == theBetweenNode1) {
8944 nbInserted = theNodesToInsert.size();
8946 // add nodes to insert in reversed order
8947 list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.end();
8949 for (; nIt != theNodesToInsert.begin(); nIt--) {
8950 poly_nodes.push_back(*nIt);
8952 poly_nodes.push_back(*nIt);
8959 quantities[iface] = nbFaceNodes + nbInserted;
8962 // Replace or update the volume
8963 SMESHDS_Mesh *aMesh = GetMeshDS();
8965 if (elem->IsPoly()) {
8966 aMesh->ChangePolyhedronNodes(elem, poly_nodes, quantities);
8970 int aShapeId = FindShape( elem );
8972 SMDS_MeshElement* newElem =
8973 aMesh->AddPolyhedralVolume(poly_nodes, quantities);
8974 myLastCreatedElems.Append(newElem);
8975 if (aShapeId && newElem)
8976 aMesh->SetMeshElementOnShape(newElem, aShapeId);
8978 aMesh->RemoveElement(elem);
8985 //================================================================================
8987 * \brief Transform any volume into data of SMDSEntity_Polyhedra
8989 //================================================================================
8991 void volumeToPolyhedron( const SMDS_MeshElement* elem,
8992 vector<const SMDS_MeshNode *> & nodes,
8993 vector<int> & nbNodeInFaces )
8996 nbNodeInFaces.clear();
8997 SMDS_VolumeTool vTool ( elem );
8998 for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
9000 const SMDS_MeshNode** fNodes = vTool.GetFaceNodes( iF );
9001 nodes.insert( nodes.end(), fNodes, fNodes + vTool.NbFaceNodes( iF ));
9002 nbNodeInFaces.push_back( vTool.NbFaceNodes( iF ));
9007 //=======================================================================
9009 * \brief Convert elements contained in a submesh to quadratic
9010 * \return int - nb of checked elements
9012 //=======================================================================
9014 int SMESH_MeshEditor::convertElemToQuadratic(SMESHDS_SubMesh * theSm,
9015 SMESH_MesherHelper& theHelper,
9016 const bool theForce3d)
9019 if( !theSm ) return nbElem;
9021 vector<int> nbNodeInFaces;
9022 vector<const SMDS_MeshNode *> nodes;
9023 SMDS_ElemIteratorPtr ElemItr = theSm->GetElements();
9024 while(ElemItr->more())
9027 const SMDS_MeshElement* elem = ElemItr->next();
9028 if( !elem ) continue;
9030 // analyse a necessity of conversion
9031 const SMDSAbs_ElementType aType = elem->GetType();
9032 if ( aType < SMDSAbs_Edge || aType > SMDSAbs_Volume )
9034 const SMDSAbs_EntityType aGeomType = elem->GetEntityType();
9035 bool hasCentralNodes = false;
9036 if ( elem->IsQuadratic() )
9039 switch ( aGeomType ) {
9040 case SMDSEntity_Quad_Triangle:
9041 case SMDSEntity_Quad_Quadrangle:
9042 case SMDSEntity_Quad_Hexa:
9043 alreadyOK = !theHelper.GetIsBiQuadratic(); break;
9045 case SMDSEntity_BiQuad_Triangle:
9046 case SMDSEntity_BiQuad_Quadrangle:
9047 case SMDSEntity_TriQuad_Hexa:
9048 alreadyOK = theHelper.GetIsBiQuadratic();
9049 hasCentralNodes = true;
9054 // take into account already present modium nodes
9056 case SMDSAbs_Volume:
9057 theHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( elem )); break;
9059 theHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( elem )); break;
9061 theHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( elem )); break;
9067 // get elem data needed to re-create it
9069 const int id = elem->GetID();
9070 const int nbNodes = elem->NbCornerNodes();
9071 nodes.assign(elem->begin_nodes(), elem->end_nodes());
9072 if ( aGeomType == SMDSEntity_Polyhedra )
9073 nbNodeInFaces = static_cast<const SMDS_VtkVolume* >( elem )->GetQuantities();
9074 else if ( aGeomType == SMDSEntity_Hexagonal_Prism )
9075 volumeToPolyhedron( elem, nodes, nbNodeInFaces );
9077 // remove a linear element
9078 GetMeshDS()->RemoveFreeElement(elem, theSm, /*fromGroups=*/false);
9080 // remove central nodes of biquadratic elements (biquad->quad convertion)
9081 if ( hasCentralNodes )
9082 for ( size_t i = nbNodes * 2; i < nodes.size(); ++i )
9083 if ( nodes[i]->NbInverseElements() == 0 )
9084 GetMeshDS()->RemoveFreeNode( nodes[i], theSm, /*fromGroups=*/true );
9086 const SMDS_MeshElement* NewElem = 0;
9092 NewElem = theHelper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9100 NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
9103 NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9106 NewElem = theHelper.AddPolygonalFace(nodes, id, theForce3d);
9110 case SMDSAbs_Volume :
9114 case SMDSEntity_Tetra:
9115 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9117 case SMDSEntity_Pyramid:
9118 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], id, theForce3d);
9120 case SMDSEntity_Penta:
9121 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], nodes[5], id, theForce3d);
9123 case SMDSEntity_Hexa:
9124 case SMDSEntity_Quad_Hexa:
9125 case SMDSEntity_TriQuad_Hexa:
9126 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9127 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9129 case SMDSEntity_Hexagonal_Prism:
9131 NewElem = theHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9138 ReplaceElemInGroups( elem, NewElem, GetMeshDS());
9139 if( NewElem && NewElem->getshapeId() < 1 )
9140 theSm->AddElement( NewElem );
9144 //=======================================================================
9145 //function : ConvertToQuadratic
9147 //=======================================================================
9149 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d, const bool theToBiQuad)
9151 SMESHDS_Mesh* meshDS = GetMeshDS();
9153 SMESH_MesherHelper aHelper(*myMesh);
9155 aHelper.SetIsQuadratic( true );
9156 aHelper.SetIsBiQuadratic( theToBiQuad );
9157 aHelper.SetElementsOnShape(true);
9158 aHelper.ToFixNodeParameters( true );
9160 // convert elements assigned to sub-meshes
9161 int nbCheckedElems = 0;
9162 if ( myMesh->HasShapeToMesh() )
9164 if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9166 SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9167 while ( smIt->more() ) {
9168 SMESH_subMesh* sm = smIt->next();
9169 if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() ) {
9170 aHelper.SetSubShape( sm->GetSubShape() );
9171 nbCheckedElems += convertElemToQuadratic(smDS, aHelper, theForce3d);
9177 // convert elements NOT assigned to sub-meshes
9178 int totalNbElems = meshDS->NbEdges() + meshDS->NbFaces() + meshDS->NbVolumes();
9179 if ( nbCheckedElems < totalNbElems ) // not all elements are in sub-meshes
9181 aHelper.SetElementsOnShape(false);
9182 SMESHDS_SubMesh *smDS = 0;
9185 SMDS_EdgeIteratorPtr aEdgeItr = meshDS->edgesIterator();
9186 while( aEdgeItr->more() )
9188 const SMDS_MeshEdge* edge = aEdgeItr->next();
9189 if ( !edge->IsQuadratic() )
9191 int id = edge->GetID();
9192 const SMDS_MeshNode* n1 = edge->GetNode(0);
9193 const SMDS_MeshNode* n2 = edge->GetNode(1);
9195 meshDS->RemoveFreeElement(edge, smDS, /*fromGroups=*/false);
9197 const SMDS_MeshEdge* NewEdge = aHelper.AddEdge(n1, n2, id, theForce3d);
9198 ReplaceElemInGroups( edge, NewEdge, GetMeshDS());
9202 aHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( edge ));
9207 SMDS_FaceIteratorPtr aFaceItr = meshDS->facesIterator();
9208 while( aFaceItr->more() )
9210 const SMDS_MeshFace* face = aFaceItr->next();
9211 if ( !face ) continue;
9213 const SMDSAbs_EntityType type = face->GetEntityType();
9217 case SMDSEntity_Quad_Triangle:
9218 case SMDSEntity_Quad_Quadrangle:
9219 alreadyOK = !theToBiQuad;
9220 aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
9222 case SMDSEntity_BiQuad_Triangle:
9223 case SMDSEntity_BiQuad_Quadrangle:
9224 alreadyOK = theToBiQuad;
9225 aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
9227 default: alreadyOK = false;
9232 const int id = face->GetID();
9233 vector<const SMDS_MeshNode *> nodes ( face->begin_nodes(), face->end_nodes());
9235 meshDS->RemoveFreeElement(face, smDS, /*fromGroups=*/false);
9237 SMDS_MeshFace * NewFace = 0;
9240 case SMDSEntity_Triangle:
9241 case SMDSEntity_Quad_Triangle:
9242 case SMDSEntity_BiQuad_Triangle:
9243 NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
9244 if ( nodes.size() == 7 && nodes[6]->NbInverseElements() == 0 ) // rm a central node
9245 GetMeshDS()->RemoveFreeNode( nodes[6], /*sm=*/0, /*fromGroups=*/true );
9248 case SMDSEntity_Quadrangle:
9249 case SMDSEntity_Quad_Quadrangle:
9250 case SMDSEntity_BiQuad_Quadrangle:
9251 NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9252 if ( nodes.size() == 9 && nodes[8]->NbInverseElements() == 0 ) // rm a central node
9253 GetMeshDS()->RemoveFreeNode( nodes[8], /*sm=*/0, /*fromGroups=*/true );
9257 NewFace = aHelper.AddPolygonalFace(nodes, id, theForce3d);
9259 ReplaceElemInGroups( face, NewFace, GetMeshDS());
9263 vector<int> nbNodeInFaces;
9264 SMDS_VolumeIteratorPtr aVolumeItr = meshDS->volumesIterator();
9265 while(aVolumeItr->more())
9267 const SMDS_MeshVolume* volume = aVolumeItr->next();
9268 if ( !volume ) continue;
9270 const SMDSAbs_EntityType type = volume->GetEntityType();
9271 if ( volume->IsQuadratic() )
9276 case SMDSEntity_Quad_Hexa: alreadyOK = !theToBiQuad; break;
9277 case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; break;
9278 default: alreadyOK = true;
9282 aHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( volume ));
9286 const int id = volume->GetID();
9287 vector<const SMDS_MeshNode *> nodes (volume->begin_nodes(), volume->end_nodes());
9288 if ( type == SMDSEntity_Polyhedra )
9289 nbNodeInFaces = static_cast<const SMDS_VtkVolume* >(volume)->GetQuantities();
9290 else if ( type == SMDSEntity_Hexagonal_Prism )
9291 volumeToPolyhedron( volume, nodes, nbNodeInFaces );
9293 meshDS->RemoveFreeElement(volume, smDS, /*fromGroups=*/false);
9295 SMDS_MeshVolume * NewVolume = 0;
9298 case SMDSEntity_Tetra:
9299 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d );
9301 case SMDSEntity_Hexa:
9302 case SMDSEntity_Quad_Hexa:
9303 case SMDSEntity_TriQuad_Hexa:
9304 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9305 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9306 for ( size_t i = 20; i < nodes.size(); ++i ) // rm central nodes
9307 if ( nodes[i]->NbInverseElements() == 0 )
9308 GetMeshDS()->RemoveFreeNode( nodes[i], /*sm=*/0, /*fromGroups=*/true );
9310 case SMDSEntity_Pyramid:
9311 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9312 nodes[3], nodes[4], id, theForce3d);
9314 case SMDSEntity_Penta:
9315 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9316 nodes[3], nodes[4], nodes[5], id, theForce3d);
9318 case SMDSEntity_Hexagonal_Prism:
9320 NewVolume = aHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9322 ReplaceElemInGroups(volume, NewVolume, meshDS);
9327 { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9328 // aHelper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9329 // aHelper.FixQuadraticElements(myError);
9330 SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
9334 //================================================================================
9336 * \brief Makes given elements quadratic
9337 * \param theForce3d - if true, the medium nodes will be placed in the middle of link
9338 * \param theElements - elements to make quadratic
9340 //================================================================================
9342 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d,
9343 TIDSortedElemSet& theElements,
9344 const bool theToBiQuad)
9346 if ( theElements.empty() ) return;
9348 // we believe that all theElements are of the same type
9349 const SMDSAbs_ElementType elemType = (*theElements.begin())->GetType();
9351 // get all nodes shared by theElements
9352 TIDSortedNodeSet allNodes;
9353 TIDSortedElemSet::iterator eIt = theElements.begin();
9354 for ( ; eIt != theElements.end(); ++eIt )
9355 allNodes.insert( (*eIt)->begin_nodes(), (*eIt)->end_nodes() );
9357 // complete theElements with elements of lower dim whose all nodes are in allNodes
9359 TIDSortedElemSet quadAdjacentElems [ SMDSAbs_NbElementTypes ]; // quadratic adjacent elements
9360 TIDSortedElemSet checkedAdjacentElems [ SMDSAbs_NbElementTypes ];
9361 TIDSortedNodeSet::iterator nIt = allNodes.begin();
9362 for ( ; nIt != allNodes.end(); ++nIt )
9364 const SMDS_MeshNode* n = *nIt;
9365 SMDS_ElemIteratorPtr invIt = n->GetInverseElementIterator();
9366 while ( invIt->more() )
9368 const SMDS_MeshElement* e = invIt->next();
9369 const SMDSAbs_ElementType type = e->GetType();
9370 if ( e->IsQuadratic() )
9372 quadAdjacentElems[ type ].insert( e );
9375 switch ( e->GetEntityType() ) {
9376 case SMDSEntity_Quad_Triangle:
9377 case SMDSEntity_Quad_Quadrangle:
9378 case SMDSEntity_Quad_Hexa: alreadyOK = !theToBiQuad; break;
9379 case SMDSEntity_BiQuad_Triangle:
9380 case SMDSEntity_BiQuad_Quadrangle:
9381 case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; break;
9382 default: alreadyOK = true;
9387 if ( type >= elemType )
9388 continue; // same type or more complex linear element
9390 if ( !checkedAdjacentElems[ type ].insert( e ).second )
9391 continue; // e is already checked
9395 SMDS_NodeIteratorPtr nodeIt = e->nodeIterator();
9396 while ( nodeIt->more() && allIn )
9397 allIn = allNodes.count( nodeIt->next() );
9399 theElements.insert(e );
9403 SMESH_MesherHelper helper(*myMesh);
9404 helper.SetIsQuadratic( true );
9405 helper.SetIsBiQuadratic( theToBiQuad );
9407 // add links of quadratic adjacent elements to the helper
9409 if ( !quadAdjacentElems[SMDSAbs_Edge].empty() )
9410 for ( eIt = quadAdjacentElems[SMDSAbs_Edge].begin();
9411 eIt != quadAdjacentElems[SMDSAbs_Edge].end(); ++eIt )
9413 helper.AddTLinks( static_cast< const SMDS_MeshEdge*> (*eIt) );
9415 if ( !quadAdjacentElems[SMDSAbs_Face].empty() )
9416 for ( eIt = quadAdjacentElems[SMDSAbs_Face].begin();
9417 eIt != quadAdjacentElems[SMDSAbs_Face].end(); ++eIt )
9419 helper.AddTLinks( static_cast< const SMDS_MeshFace*> (*eIt) );
9421 if ( !quadAdjacentElems[SMDSAbs_Volume].empty() )
9422 for ( eIt = quadAdjacentElems[SMDSAbs_Volume].begin();
9423 eIt != quadAdjacentElems[SMDSAbs_Volume].end(); ++eIt )
9425 helper.AddTLinks( static_cast< const SMDS_MeshVolume*> (*eIt) );
9428 // make quadratic (or bi-tri-quadratic) elements instead of linear ones
9430 SMESHDS_Mesh* meshDS = GetMeshDS();
9431 SMESHDS_SubMesh* smDS = 0;
9432 for ( eIt = theElements.begin(); eIt != theElements.end(); ++eIt )
9434 const SMDS_MeshElement* elem = *eIt;
9437 int nbCentralNodes = 0;
9438 switch ( elem->GetEntityType() ) {
9439 // linear convertible
9440 case SMDSEntity_Edge:
9441 case SMDSEntity_Triangle:
9442 case SMDSEntity_Quadrangle:
9443 case SMDSEntity_Tetra:
9444 case SMDSEntity_Pyramid:
9445 case SMDSEntity_Hexa:
9446 case SMDSEntity_Penta: alreadyOK = false; nbCentralNodes = 0; break;
9447 // quadratic that can become bi-quadratic
9448 case SMDSEntity_Quad_Triangle:
9449 case SMDSEntity_Quad_Quadrangle:
9450 case SMDSEntity_Quad_Hexa: alreadyOK =!theToBiQuad; nbCentralNodes = 0; break;
9452 case SMDSEntity_BiQuad_Triangle:
9453 case SMDSEntity_BiQuad_Quadrangle: alreadyOK = theToBiQuad; nbCentralNodes = 1; break;
9454 case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; nbCentralNodes = 7; break;
9456 default: alreadyOK = true;
9458 if ( alreadyOK ) continue;
9460 const SMDSAbs_ElementType type = elem->GetType();
9461 const int id = elem->GetID();
9462 const int nbNodes = elem->NbCornerNodes();
9463 vector<const SMDS_MeshNode *> nodes ( elem->begin_nodes(), elem->end_nodes());
9465 helper.SetSubShape( elem->getshapeId() );
9467 if ( !smDS || !smDS->Contains( elem ))
9468 smDS = meshDS->MeshElements( elem->getshapeId() );
9469 meshDS->RemoveFreeElement(elem, smDS, /*fromGroups=*/false);
9471 SMDS_MeshElement * newElem = 0;
9474 case 4: // cases for most frequently used element types go first (for optimization)
9475 if ( type == SMDSAbs_Volume )
9476 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9478 newElem = helper.AddFace (nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9481 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9482 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9485 newElem = helper.AddFace (nodes[0], nodes[1], nodes[2], id, theForce3d);
9488 newElem = helper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9491 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9492 nodes[4], id, theForce3d);
9495 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9496 nodes[4], nodes[5], id, theForce3d);
9500 ReplaceElemInGroups( elem, newElem, meshDS);
9501 if( newElem && smDS )
9502 smDS->AddElement( newElem );
9504 // remove central nodes
9505 for ( size_t i = nodes.size() - nbCentralNodes; i < nodes.size(); ++i )
9506 if ( nodes[i]->NbInverseElements() == 0 )
9507 meshDS->RemoveFreeNode( nodes[i], smDS, /*fromGroups=*/true );
9509 } // loop on theElements
9512 { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9513 // helper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9514 // helper.FixQuadraticElements( myError );
9515 SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
9519 //=======================================================================
9521 * \brief Convert quadratic elements to linear ones and remove quadratic nodes
9522 * \return int - nb of checked elements
9524 //=======================================================================
9526 int SMESH_MeshEditor::removeQuadElem(SMESHDS_SubMesh * theSm,
9527 SMDS_ElemIteratorPtr theItr,
9528 const int theShapeID)
9531 SMESHDS_Mesh* meshDS = GetMeshDS();
9533 while( theItr->more() )
9535 const SMDS_MeshElement* elem = theItr->next();
9537 if( elem && elem->IsQuadratic())
9539 int id = elem->GetID();
9540 int nbCornerNodes = elem->NbCornerNodes();
9541 SMDSAbs_ElementType aType = elem->GetType();
9543 vector<const SMDS_MeshNode *> nodes( elem->begin_nodes(), elem->end_nodes() );
9545 //remove a quadratic element
9546 if ( !theSm || !theSm->Contains( elem ))
9547 theSm = meshDS->MeshElements( elem->getshapeId() );
9548 meshDS->RemoveFreeElement( elem, theSm, /*fromGroups=*/false );
9550 // remove medium nodes
9551 for ( unsigned i = nbCornerNodes; i < nodes.size(); ++i )
9552 if ( nodes[i]->NbInverseElements() == 0 )
9553 meshDS->RemoveFreeNode( nodes[i], theSm );
9555 // add a linear element
9556 nodes.resize( nbCornerNodes );
9557 SMDS_MeshElement * newElem = AddElement( nodes, aType, false, id );
9558 ReplaceElemInGroups(elem, newElem, meshDS);
9559 if( theSm && newElem )
9560 theSm->AddElement( newElem );
9566 //=======================================================================
9567 //function : ConvertFromQuadratic
9569 //=======================================================================
9571 bool SMESH_MeshEditor::ConvertFromQuadratic()
9573 int nbCheckedElems = 0;
9574 if ( myMesh->HasShapeToMesh() )
9576 if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9578 SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9579 while ( smIt->more() ) {
9580 SMESH_subMesh* sm = smIt->next();
9581 if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() )
9582 nbCheckedElems += removeQuadElem( smDS, smDS->GetElements(), sm->GetId() );
9588 GetMeshDS()->NbEdges() + GetMeshDS()->NbFaces() + GetMeshDS()->NbVolumes();
9589 if ( nbCheckedElems < totalNbElems ) // not all elements are in submeshes
9591 SMESHDS_SubMesh *aSM = 0;
9592 removeQuadElem( aSM, GetMeshDS()->elementsIterator(), 0 );
9600 //================================================================================
9602 * \brief Return true if all medium nodes of the element are in the node set
9604 //================================================================================
9606 bool allMediumNodesIn(const SMDS_MeshElement* elem, TIDSortedNodeSet& nodeSet )
9608 for ( int i = elem->NbCornerNodes(); i < elem->NbNodes(); ++i )
9609 if ( !nodeSet.count( elem->GetNode(i) ))
9615 //================================================================================
9617 * \brief Makes given elements linear
9619 //================================================================================
9621 void SMESH_MeshEditor::ConvertFromQuadratic(TIDSortedElemSet& theElements)
9623 if ( theElements.empty() ) return;
9625 // collect IDs of medium nodes of theElements; some of these nodes will be removed
9626 set<int> mediumNodeIDs;
9627 TIDSortedElemSet::iterator eIt = theElements.begin();
9628 for ( ; eIt != theElements.end(); ++eIt )
9630 const SMDS_MeshElement* e = *eIt;
9631 for ( int i = e->NbCornerNodes(); i < e->NbNodes(); ++i )
9632 mediumNodeIDs.insert( e->GetNode(i)->GetID() );
9635 // replace given elements by linear ones
9636 SMDS_ElemIteratorPtr elemIt = elemSetIterator( theElements );
9637 removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9639 // we need to convert remaining elements whose all medium nodes are in mediumNodeIDs
9640 // except those elements sharing medium nodes of quadratic element whose medium nodes
9641 // are not all in mediumNodeIDs
9643 // get remaining medium nodes
9644 TIDSortedNodeSet mediumNodes;
9645 set<int>::iterator nIdsIt = mediumNodeIDs.begin();
9646 for ( ; nIdsIt != mediumNodeIDs.end(); ++nIdsIt )
9647 if ( const SMDS_MeshNode* n = GetMeshDS()->FindNode( *nIdsIt ))
9648 mediumNodes.insert( mediumNodes.end(), n );
9650 // find more quadratic elements to convert
9651 TIDSortedElemSet moreElemsToConvert;
9652 TIDSortedNodeSet::iterator nIt = mediumNodes.begin();
9653 for ( ; nIt != mediumNodes.end(); ++nIt )
9655 SMDS_ElemIteratorPtr invIt = (*nIt)->GetInverseElementIterator();
9656 while ( invIt->more() )
9658 const SMDS_MeshElement* e = invIt->next();
9659 if ( e->IsQuadratic() && allMediumNodesIn( e, mediumNodes ))
9661 // find a more complex element including e and
9662 // whose medium nodes are not in mediumNodes
9663 bool complexFound = false;
9664 for ( int type = e->GetType() + 1; type < SMDSAbs_0DElement; ++type )
9666 SMDS_ElemIteratorPtr invIt2 =
9667 (*nIt)->GetInverseElementIterator( SMDSAbs_ElementType( type ));
9668 while ( invIt2->more() )
9670 const SMDS_MeshElement* eComplex = invIt2->next();
9671 if ( eComplex->IsQuadratic() && !allMediumNodesIn( eComplex, mediumNodes))
9673 int nbCommonNodes = SMESH_MeshAlgos::GetCommonNodes( e, eComplex ).size();
9674 if ( nbCommonNodes == e->NbNodes())
9676 complexFound = true;
9677 type = SMDSAbs_NbElementTypes; // to quit from the outer loop
9683 if ( !complexFound )
9684 moreElemsToConvert.insert( e );
9688 elemIt = elemSetIterator( moreElemsToConvert );
9689 removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9692 //=======================================================================
9693 //function : SewSideElements
9695 //=======================================================================
9697 SMESH_MeshEditor::Sew_Error
9698 SMESH_MeshEditor::SewSideElements (TIDSortedElemSet& theSide1,
9699 TIDSortedElemSet& theSide2,
9700 const SMDS_MeshNode* theFirstNode1,
9701 const SMDS_MeshNode* theFirstNode2,
9702 const SMDS_MeshNode* theSecondNode1,
9703 const SMDS_MeshNode* theSecondNode2)
9705 myLastCreatedElems.Clear();
9706 myLastCreatedNodes.Clear();
9708 MESSAGE ("::::SewSideElements()");
9709 if ( theSide1.size() != theSide2.size() )
9710 return SEW_DIFF_NB_OF_ELEMENTS;
9712 Sew_Error aResult = SEW_OK;
9714 // 1. Build set of faces representing each side
9715 // 2. Find which nodes of the side 1 to merge with ones on the side 2
9716 // 3. Replace nodes in elements of the side 1 and remove replaced nodes
9718 // =======================================================================
9719 // 1. Build set of faces representing each side:
9720 // =======================================================================
9721 // a. build set of nodes belonging to faces
9722 // b. complete set of faces: find missing faces whose nodes are in set of nodes
9723 // c. create temporary faces representing side of volumes if correspondent
9724 // face does not exist
9726 SMESHDS_Mesh* aMesh = GetMeshDS();
9727 // TODO algoritm not OK with vtkUnstructuredGrid: 2 meshes can't share nodes
9728 //SMDS_Mesh aTmpFacesMesh; // try to use the same mesh
9729 TIDSortedElemSet faceSet1, faceSet2;
9730 set<const SMDS_MeshElement*> volSet1, volSet2;
9731 set<const SMDS_MeshNode*> nodeSet1, nodeSet2;
9732 TIDSortedElemSet * faceSetPtr[] = { &faceSet1, &faceSet2 };
9733 set<const SMDS_MeshElement*> * volSetPtr[] = { &volSet1, &volSet2 };
9734 set<const SMDS_MeshNode*> * nodeSetPtr[] = { &nodeSet1, &nodeSet2 };
9735 TIDSortedElemSet * elemSetPtr[] = { &theSide1, &theSide2 };
9736 int iSide, iFace, iNode;
9738 list<const SMDS_MeshElement* > tempFaceList;
9739 for ( iSide = 0; iSide < 2; iSide++ ) {
9740 set<const SMDS_MeshNode*> * nodeSet = nodeSetPtr[ iSide ];
9741 TIDSortedElemSet * elemSet = elemSetPtr[ iSide ];
9742 TIDSortedElemSet * faceSet = faceSetPtr[ iSide ];
9743 set<const SMDS_MeshElement*> * volSet = volSetPtr [ iSide ];
9744 set<const SMDS_MeshElement*>::iterator vIt;
9745 TIDSortedElemSet::iterator eIt;
9746 set<const SMDS_MeshNode*>::iterator nIt;
9748 // check that given nodes belong to given elements
9749 const SMDS_MeshNode* n1 = ( iSide == 0 ) ? theFirstNode1 : theFirstNode2;
9750 const SMDS_MeshNode* n2 = ( iSide == 0 ) ? theSecondNode1 : theSecondNode2;
9751 int firstIndex = -1, secondIndex = -1;
9752 for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9753 const SMDS_MeshElement* elem = *eIt;
9754 if ( firstIndex < 0 ) firstIndex = elem->GetNodeIndex( n1 );
9755 if ( secondIndex < 0 ) secondIndex = elem->GetNodeIndex( n2 );
9756 if ( firstIndex > -1 && secondIndex > -1 ) break;
9758 if ( firstIndex < 0 || secondIndex < 0 ) {
9759 // we can simply return until temporary faces created
9760 return (iSide == 0 ) ? SEW_BAD_SIDE1_NODES : SEW_BAD_SIDE2_NODES;
9763 // -----------------------------------------------------------
9764 // 1a. Collect nodes of existing faces
9765 // and build set of face nodes in order to detect missing
9766 // faces corresponding to sides of volumes
9767 // -----------------------------------------------------------
9769 set< set <const SMDS_MeshNode*> > setOfFaceNodeSet;
9771 // loop on the given element of a side
9772 for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9773 //const SMDS_MeshElement* elem = *eIt;
9774 const SMDS_MeshElement* elem = *eIt;
9775 if ( elem->GetType() == SMDSAbs_Face ) {
9776 faceSet->insert( elem );
9777 set <const SMDS_MeshNode*> faceNodeSet;
9778 SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
9779 while ( nodeIt->more() ) {
9780 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9781 nodeSet->insert( n );
9782 faceNodeSet.insert( n );
9784 setOfFaceNodeSet.insert( faceNodeSet );
9786 else if ( elem->GetType() == SMDSAbs_Volume )
9787 volSet->insert( elem );
9789 // ------------------------------------------------------------------------------
9790 // 1b. Complete set of faces: find missing faces whose nodes are in set of nodes
9791 // ------------------------------------------------------------------------------
9793 for ( nIt = nodeSet->begin(); nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
9794 SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
9795 while ( fIt->more() ) { // loop on faces sharing a node
9796 const SMDS_MeshElement* f = fIt->next();
9797 if ( faceSet->find( f ) == faceSet->end() ) {
9798 // check if all nodes are in nodeSet and
9799 // complete setOfFaceNodeSet if they are
9800 set <const SMDS_MeshNode*> faceNodeSet;
9801 SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
9802 bool allInSet = true;
9803 while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
9804 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9805 if ( nodeSet->find( n ) == nodeSet->end() )
9808 faceNodeSet.insert( n );
9811 faceSet->insert( f );
9812 setOfFaceNodeSet.insert( faceNodeSet );
9818 // -------------------------------------------------------------------------
9819 // 1c. Create temporary faces representing sides of volumes if correspondent
9820 // face does not exist
9821 // -------------------------------------------------------------------------
9823 if ( !volSet->empty() ) {
9824 //int nodeSetSize = nodeSet->size();
9826 // loop on given volumes
9827 for ( vIt = volSet->begin(); vIt != volSet->end(); vIt++ ) {
9828 SMDS_VolumeTool vol (*vIt);
9829 // loop on volume faces: find free faces
9830 // --------------------------------------
9831 list<const SMDS_MeshElement* > freeFaceList;
9832 for ( iFace = 0; iFace < vol.NbFaces(); iFace++ ) {
9833 if ( !vol.IsFreeFace( iFace ))
9835 // check if there is already a face with same nodes in a face set
9836 const SMDS_MeshElement* aFreeFace = 0;
9837 const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iFace );
9838 int nbNodes = vol.NbFaceNodes( iFace );
9839 set <const SMDS_MeshNode*> faceNodeSet;
9840 vol.GetFaceNodes( iFace, faceNodeSet );
9841 bool isNewFace = setOfFaceNodeSet.insert( faceNodeSet ).second;
9843 // no such a face is given but it still can exist, check it
9844 vector<const SMDS_MeshNode *> nodes ( fNodes, fNodes + nbNodes);
9845 aFreeFace = aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false );
9848 // create a temporary face
9849 if ( nbNodes == 3 ) {
9850 //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2] );
9851 aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2] );
9853 else if ( nbNodes == 4 ) {
9854 //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
9855 aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
9858 vector<const SMDS_MeshNode *> poly_nodes ( fNodes, & fNodes[nbNodes]);
9859 //aFreeFace = aTmpFacesMesh.AddPolygonalFace(poly_nodes);
9860 aFreeFace = aMesh->AddPolygonalFace(poly_nodes);
9863 tempFaceList.push_back( aFreeFace );
9867 freeFaceList.push_back( aFreeFace );
9869 } // loop on faces of a volume
9871 // choose one of several free faces of a volume
9872 // --------------------------------------------
9873 if ( freeFaceList.size() > 1 ) {
9874 // choose a face having max nb of nodes shared by other elems of a side
9875 int maxNbNodes = -1;
9876 list<const SMDS_MeshElement* >::iterator fIt = freeFaceList.begin();
9877 while ( fIt != freeFaceList.end() ) { // loop on free faces
9878 int nbSharedNodes = 0;
9879 SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
9880 while ( nodeIt->more() ) { // loop on free face nodes
9881 const SMDS_MeshNode* n =
9882 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9883 SMDS_ElemIteratorPtr invElemIt = n->GetInverseElementIterator();
9884 while ( invElemIt->more() ) {
9885 const SMDS_MeshElement* e = invElemIt->next();
9886 nbSharedNodes += faceSet->count( e );
9887 nbSharedNodes += elemSet->count( e );
9890 if ( nbSharedNodes > maxNbNodes ) {
9891 maxNbNodes = nbSharedNodes;
9892 freeFaceList.erase( freeFaceList.begin(), fIt++ );
9894 else if ( nbSharedNodes == maxNbNodes ) {
9898 freeFaceList.erase( fIt++ ); // here fIt++ occurs before erase
9901 if ( freeFaceList.size() > 1 )
9903 // could not choose one face, use another way
9904 // choose a face most close to the bary center of the opposite side
9905 gp_XYZ aBC( 0., 0., 0. );
9906 set <const SMDS_MeshNode*> addedNodes;
9907 TIDSortedElemSet * elemSet2 = elemSetPtr[ 1 - iSide ];
9908 eIt = elemSet2->begin();
9909 for ( eIt = elemSet2->begin(); eIt != elemSet2->end(); eIt++ ) {
9910 SMDS_ElemIteratorPtr nodeIt = (*eIt)->nodesIterator();
9911 while ( nodeIt->more() ) { // loop on free face nodes
9912 const SMDS_MeshNode* n =
9913 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9914 if ( addedNodes.insert( n ).second )
9915 aBC += gp_XYZ( n->X(),n->Y(),n->Z() );
9918 aBC /= addedNodes.size();
9919 double minDist = DBL_MAX;
9920 fIt = freeFaceList.begin();
9921 while ( fIt != freeFaceList.end() ) { // loop on free faces
9923 SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
9924 while ( nodeIt->more() ) { // loop on free face nodes
9925 const SMDS_MeshNode* n =
9926 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9927 gp_XYZ p( n->X(),n->Y(),n->Z() );
9928 dist += ( aBC - p ).SquareModulus();
9930 if ( dist < minDist ) {
9932 freeFaceList.erase( freeFaceList.begin(), fIt++ );
9935 fIt = freeFaceList.erase( fIt++ );
9938 } // choose one of several free faces of a volume
9940 if ( freeFaceList.size() == 1 ) {
9941 const SMDS_MeshElement* aFreeFace = freeFaceList.front();
9942 faceSet->insert( aFreeFace );
9943 // complete a node set with nodes of a found free face
9944 // for ( iNode = 0; iNode < ; iNode++ )
9945 // nodeSet->insert( fNodes[ iNode ] );
9948 } // loop on volumes of a side
9950 // // complete a set of faces if new nodes in a nodeSet appeared
9951 // // ----------------------------------------------------------
9952 // if ( nodeSetSize != nodeSet->size() ) {
9953 // for ( ; nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
9954 // SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
9955 // while ( fIt->more() ) { // loop on faces sharing a node
9956 // const SMDS_MeshElement* f = fIt->next();
9957 // if ( faceSet->find( f ) == faceSet->end() ) {
9958 // // check if all nodes are in nodeSet and
9959 // // complete setOfFaceNodeSet if they are
9960 // set <const SMDS_MeshNode*> faceNodeSet;
9961 // SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
9962 // bool allInSet = true;
9963 // while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
9964 // const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9965 // if ( nodeSet->find( n ) == nodeSet->end() )
9966 // allInSet = false;
9968 // faceNodeSet.insert( n );
9970 // if ( allInSet ) {
9971 // faceSet->insert( f );
9972 // setOfFaceNodeSet.insert( faceNodeSet );
9978 } // Create temporary faces, if there are volumes given
9981 if ( faceSet1.size() != faceSet2.size() ) {
9982 // delete temporary faces: they are in reverseElements of actual nodes
9983 // SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
9984 // while ( tmpFaceIt->more() )
9985 // aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
9986 // list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
9987 // for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
9988 // aMesh->RemoveElement(*tmpFaceIt);
9989 MESSAGE("Diff nb of faces");
9990 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9993 // ============================================================
9994 // 2. Find nodes to merge:
9995 // bind a node to remove to a node to put instead
9996 // ============================================================
9998 TNodeNodeMap nReplaceMap; // bind a node to remove to a node to put instead
9999 if ( theFirstNode1 != theFirstNode2 )
10000 nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
10001 if ( theSecondNode1 != theSecondNode2 )
10002 nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
10004 LinkID_Gen aLinkID_Gen( GetMeshDS() );
10005 set< long > linkIdSet; // links to process
10006 linkIdSet.insert( aLinkID_Gen.GetLinkID( theFirstNode1, theSecondNode1 ));
10008 typedef pair< const SMDS_MeshNode*, const SMDS_MeshNode* > NLink;
10009 list< NLink > linkList[2];
10010 linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
10011 linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
10012 // loop on links in linkList; find faces by links and append links
10013 // of the found faces to linkList
10014 list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
10015 for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ )
10017 NLink link[] = { *linkIt[0], *linkIt[1] };
10018 long linkID = aLinkID_Gen.GetLinkID( link[0].first, link[0].second );
10019 if ( !linkIdSet.count( linkID ) )
10022 // by links, find faces in the face sets,
10023 // and find indices of link nodes in the found faces;
10024 // in a face set, there is only one or no face sharing a link
10025 // ---------------------------------------------------------------
10027 const SMDS_MeshElement* face[] = { 0, 0 };
10028 vector<const SMDS_MeshNode*> fnodes[2];
10029 int iLinkNode[2][2];
10030 TIDSortedElemSet avoidSet;
10031 for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
10032 const SMDS_MeshNode* n1 = link[iSide].first;
10033 const SMDS_MeshNode* n2 = link[iSide].second;
10034 //cout << "Side " << iSide << " ";
10035 //cout << "L( " << n1->GetID() << ", " << n2->GetID() << " ) " << endl;
10036 // find a face by two link nodes
10037 face[ iSide ] = SMESH_MeshAlgos::FindFaceInSet( n1, n2,
10038 *faceSetPtr[ iSide ], avoidSet,
10039 &iLinkNode[iSide][0],
10040 &iLinkNode[iSide][1] );
10041 if ( face[ iSide ])
10043 //cout << " F " << face[ iSide]->GetID() <<endl;
10044 faceSetPtr[ iSide ]->erase( face[ iSide ]);
10045 // put face nodes to fnodes
10046 if ( face[ iSide ]->IsQuadratic() )
10048 // use interlaced nodes iterator
10049 const SMDS_VtkFace* F = dynamic_cast<const SMDS_VtkFace*>( face[ iSide ]);
10050 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
10051 SMDS_ElemIteratorPtr nIter = F->interlacedNodesElemIterator();
10052 while ( nIter->more() )
10053 fnodes[ iSide ].push_back( cast2Node( nIter->next() ));
10057 fnodes[ iSide ].assign( face[ iSide ]->begin_nodes(),
10058 face[ iSide ]->end_nodes() );
10060 fnodes[ iSide ].push_back( fnodes[ iSide ].front());
10064 // check similarity of elements of the sides
10065 if (aResult == SEW_OK && (( face[0] && !face[1] ) || ( !face[0] && face[1] ))) {
10066 MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
10067 if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
10068 aResult = ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10071 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10073 break; // do not return because it's necessary to remove tmp faces
10076 // set nodes to merge
10077 // -------------------
10079 if ( face[0] && face[1] ) {
10080 const int nbNodes = face[0]->NbNodes();
10081 if ( nbNodes != face[1]->NbNodes() ) {
10082 MESSAGE("Diff nb of face nodes");
10083 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10084 break; // do not return because it s necessary to remove tmp faces
10086 bool reverse[] = { false, false }; // order of nodes in the link
10087 for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
10088 // analyse link orientation in faces
10089 int i1 = iLinkNode[ iSide ][ 0 ];
10090 int i2 = iLinkNode[ iSide ][ 1 ];
10091 reverse[ iSide ] = Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1;
10093 int di1 = reverse[0] ? -1 : +1, i1 = iLinkNode[0][1] + di1;
10094 int di2 = reverse[1] ? -1 : +1, i2 = iLinkNode[1][1] + di2;
10095 for ( int i = nbNodes - 2; i > 0; --i, i1 += di1, i2 += di2 )
10097 nReplaceMap.insert ( make_pair ( fnodes[0][ ( i1 + nbNodes ) % nbNodes ],
10098 fnodes[1][ ( i2 + nbNodes ) % nbNodes ]));
10101 // add other links of the faces to linkList
10102 // -----------------------------------------
10104 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
10105 linkID = aLinkID_Gen.GetLinkID( fnodes[0][iNode], fnodes[0][iNode+1] );
10106 pair< set<long>::iterator, bool > iter_isnew = linkIdSet.insert( linkID );
10107 if ( !iter_isnew.second ) { // already in a set: no need to process
10108 linkIdSet.erase( iter_isnew.first );
10110 else // new in set == encountered for the first time: add
10112 const SMDS_MeshNode* n1 = fnodes[0][ iNode ];
10113 const SMDS_MeshNode* n2 = fnodes[0][ iNode + 1];
10114 linkList[0].push_back ( NLink( n1, n2 ));
10115 linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10120 if ( faceSetPtr[0]->empty() || faceSetPtr[1]->empty() )
10123 } // loop on link lists
10125 if ( aResult == SEW_OK &&
10126 ( //linkIt[0] != linkList[0].end() ||
10127 !faceSetPtr[0]->empty() || !faceSetPtr[1]->empty() )) {
10128 MESSAGE( (linkIt[0] != linkList[0].end()) <<" "<< (faceSetPtr[0]->empty()) <<
10129 " " << (faceSetPtr[1]->empty()));
10130 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10133 // ====================================================================
10134 // 3. Replace nodes in elements of the side 1 and remove replaced nodes
10135 // ====================================================================
10137 // delete temporary faces
10138 // SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
10139 // while ( tmpFaceIt->more() )
10140 // aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
10141 list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
10142 for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
10143 aMesh->RemoveElement(*tmpFaceIt);
10145 if ( aResult != SEW_OK)
10148 list< int > nodeIDsToRemove/*, elemIDsToRemove*/;
10149 // loop on nodes replacement map
10150 TNodeNodeMap::iterator nReplaceMapIt = nReplaceMap.begin(), nnIt;
10151 for ( ; nReplaceMapIt != nReplaceMap.end(); nReplaceMapIt++ )
10152 if ( (*nReplaceMapIt).first != (*nReplaceMapIt).second ) {
10153 const SMDS_MeshNode* nToRemove = (*nReplaceMapIt).first;
10154 nodeIDsToRemove.push_back( nToRemove->GetID() );
10155 // loop on elements sharing nToRemove
10156 SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
10157 while ( invElemIt->more() ) {
10158 const SMDS_MeshElement* e = invElemIt->next();
10159 // get a new suite of nodes: make replacement
10160 int nbReplaced = 0, i = 0, nbNodes = e->NbNodes();
10161 vector< const SMDS_MeshNode*> nodes( nbNodes );
10162 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
10163 while ( nIt->more() ) {
10164 const SMDS_MeshNode* n =
10165 static_cast<const SMDS_MeshNode*>( nIt->next() );
10166 nnIt = nReplaceMap.find( n );
10167 if ( nnIt != nReplaceMap.end() ) {
10169 n = (*nnIt).second;
10173 // if ( nbReplaced == nbNodes && e->GetType() == SMDSAbs_Face )
10174 // elemIDsToRemove.push_back( e->GetID() );
10178 SMDSAbs_ElementType etyp = e->GetType();
10179 SMDS_MeshElement* newElem = this->AddElement(nodes, etyp, false);
10182 myLastCreatedElems.Append(newElem);
10183 AddToSameGroups(newElem, e, aMesh);
10184 int aShapeId = e->getshapeId();
10187 aMesh->SetMeshElementOnShape( newElem, aShapeId );
10190 aMesh->RemoveElement(e);
10195 Remove( nodeIDsToRemove, true );
10200 //================================================================================
10202 * \brief Find corresponding nodes in two sets of faces
10203 * \param theSide1 - first face set
10204 * \param theSide2 - second first face
10205 * \param theFirstNode1 - a boundary node of set 1
10206 * \param theFirstNode2 - a node of set 2 corresponding to theFirstNode1
10207 * \param theSecondNode1 - a boundary node of set 1 linked with theFirstNode1
10208 * \param theSecondNode2 - a node of set 2 corresponding to theSecondNode1
10209 * \param nReplaceMap - output map of corresponding nodes
10210 * \return bool - is a success or not
10212 //================================================================================
10215 //#define DEBUG_MATCHING_NODES
10218 SMESH_MeshEditor::Sew_Error
10219 SMESH_MeshEditor::FindMatchingNodes(set<const SMDS_MeshElement*>& theSide1,
10220 set<const SMDS_MeshElement*>& theSide2,
10221 const SMDS_MeshNode* theFirstNode1,
10222 const SMDS_MeshNode* theFirstNode2,
10223 const SMDS_MeshNode* theSecondNode1,
10224 const SMDS_MeshNode* theSecondNode2,
10225 TNodeNodeMap & nReplaceMap)
10227 set<const SMDS_MeshElement*> * faceSetPtr[] = { &theSide1, &theSide2 };
10229 nReplaceMap.clear();
10230 if ( theFirstNode1 != theFirstNode2 )
10231 nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
10232 if ( theSecondNode1 != theSecondNode2 )
10233 nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
10235 set< SMESH_TLink > linkSet; // set of nodes where order of nodes is ignored
10236 linkSet.insert( SMESH_TLink( theFirstNode1, theSecondNode1 ));
10238 list< NLink > linkList[2];
10239 linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
10240 linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
10242 // loop on links in linkList; find faces by links and append links
10243 // of the found faces to linkList
10244 list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
10245 for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ ) {
10246 NLink link[] = { *linkIt[0], *linkIt[1] };
10247 if ( linkSet.find( link[0] ) == linkSet.end() )
10250 // by links, find faces in the face sets,
10251 // and find indices of link nodes in the found faces;
10252 // in a face set, there is only one or no face sharing a link
10253 // ---------------------------------------------------------------
10255 const SMDS_MeshElement* face[] = { 0, 0 };
10256 list<const SMDS_MeshNode*> notLinkNodes[2];
10257 //bool reverse[] = { false, false }; // order of notLinkNodes
10259 for ( int iSide = 0; iSide < 2; iSide++ ) // loop on 2 sides
10261 const SMDS_MeshNode* n1 = link[iSide].first;
10262 const SMDS_MeshNode* n2 = link[iSide].second;
10263 set<const SMDS_MeshElement*> * faceSet = faceSetPtr[ iSide ];
10264 set< const SMDS_MeshElement* > facesOfNode1;
10265 for ( int iNode = 0; iNode < 2; iNode++ ) // loop on 2 nodes of a link
10267 // during a loop of the first node, we find all faces around n1,
10268 // during a loop of the second node, we find one face sharing both n1 and n2
10269 const SMDS_MeshNode* n = iNode ? n1 : n2; // a node of a link
10270 SMDS_ElemIteratorPtr fIt = n->GetInverseElementIterator(SMDSAbs_Face);
10271 while ( fIt->more() ) { // loop on faces sharing a node
10272 const SMDS_MeshElement* f = fIt->next();
10273 if (faceSet->find( f ) != faceSet->end() && // f is in face set
10274 ! facesOfNode1.insert( f ).second ) // f encounters twice
10276 if ( face[ iSide ] ) {
10277 MESSAGE( "2 faces per link " );
10278 return ( iSide ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10281 faceSet->erase( f );
10283 // get not link nodes
10284 int nbN = f->NbNodes();
10285 if ( f->IsQuadratic() )
10287 nbNodes[ iSide ] = nbN;
10288 list< const SMDS_MeshNode* > & nodes = notLinkNodes[ iSide ];
10289 int i1 = f->GetNodeIndex( n1 );
10290 int i2 = f->GetNodeIndex( n2 );
10291 int iEnd = nbN, iBeg = -1, iDelta = 1;
10292 bool reverse = ( Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1 );
10294 std::swap( iEnd, iBeg ); iDelta = -1;
10299 if ( i == iEnd ) i = iBeg + iDelta;
10300 if ( i == i1 ) break;
10301 nodes.push_back ( f->GetNode( i ) );
10307 // check similarity of elements of the sides
10308 if (( face[0] && !face[1] ) || ( !face[0] && face[1] )) {
10309 MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
10310 if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
10311 return ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10314 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10318 // set nodes to merge
10319 // -------------------
10321 if ( face[0] && face[1] ) {
10322 if ( nbNodes[0] != nbNodes[1] ) {
10323 MESSAGE("Diff nb of face nodes");
10324 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10326 #ifdef DEBUG_MATCHING_NODES
10327 MESSAGE ( " Link 1: " << link[0].first->GetID() <<" "<< link[0].second->GetID()
10328 << " F 1: " << face[0] << "| Link 2: " << link[1].first->GetID() <<" "
10329 << link[1].second->GetID() << " F 2: " << face[1] << " | Bind: " ) ;
10331 int nbN = nbNodes[0];
10333 list<const SMDS_MeshNode*>::iterator n1 = notLinkNodes[0].begin();
10334 list<const SMDS_MeshNode*>::iterator n2 = notLinkNodes[1].begin();
10335 for ( int i = 0 ; i < nbN - 2; ++i ) {
10336 #ifdef DEBUG_MATCHING_NODES
10337 MESSAGE ( (*n1)->GetID() << " to " << (*n2)->GetID() );
10339 nReplaceMap.insert( make_pair( *(n1++), *(n2++) ));
10343 // add other links of the face 1 to linkList
10344 // -----------------------------------------
10346 const SMDS_MeshElement* f0 = face[0];
10347 const SMDS_MeshNode* n1 = f0->GetNode( nbN - 1 );
10348 for ( int i = 0; i < nbN; i++ )
10350 const SMDS_MeshNode* n2 = f0->GetNode( i );
10351 pair< set< SMESH_TLink >::iterator, bool > iter_isnew =
10352 linkSet.insert( SMESH_TLink( n1, n2 ));
10353 if ( !iter_isnew.second ) { // already in a set: no need to process
10354 linkSet.erase( iter_isnew.first );
10356 else // new in set == encountered for the first time: add
10358 #ifdef DEBUG_MATCHING_NODES
10359 MESSAGE ( "Add link 1: " << n1->GetID() << " " << n2->GetID() << " "
10360 << " | link 2: " << nReplaceMap[n1]->GetID() << " " << nReplaceMap[n2]->GetID() << " " );
10362 linkList[0].push_back ( NLink( n1, n2 ));
10363 linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10368 } // loop on link lists
10373 //================================================================================
10375 * \brief Create elements equal (on same nodes) to given ones
10376 * \param [in] theElements - a set of elems to duplicate. If it is empty, all
10377 * elements of the uppest dimension are duplicated.
10379 //================================================================================
10381 void SMESH_MeshEditor::DoubleElements( const TIDSortedElemSet& theElements )
10383 ClearLastCreated();
10384 SMESHDS_Mesh* mesh = GetMeshDS();
10386 // get an element type and an iterator over elements
10388 SMDSAbs_ElementType type;
10389 SMDS_ElemIteratorPtr elemIt;
10390 vector< const SMDS_MeshElement* > allElems;
10391 if ( theElements.empty() )
10393 if ( mesh->NbNodes() == 0 )
10395 // get most complex type
10396 SMDSAbs_ElementType types[SMDSAbs_NbElementTypes] = {
10397 SMDSAbs_Volume, SMDSAbs_Face, SMDSAbs_Edge,
10398 SMDSAbs_0DElement, SMDSAbs_Ball, SMDSAbs_Node
10400 for ( int i = 0; i < SMDSAbs_NbElementTypes; ++i )
10401 if ( mesh->GetMeshInfo().NbElements( types[i] ))
10406 // put all elements in the vector <allElems>
10407 allElems.reserve( mesh->GetMeshInfo().NbElements( type ));
10408 elemIt = mesh->elementsIterator( type );
10409 while ( elemIt->more() )
10410 allElems.push_back( elemIt->next());
10411 elemIt = elemSetIterator( allElems );
10415 type = (*theElements.begin())->GetType();
10416 elemIt = elemSetIterator( theElements );
10419 // duplicate elements
10421 if ( type == SMDSAbs_Ball )
10423 SMDS_UnstructuredGrid* vtkGrid = mesh->getGrid();
10424 while ( elemIt->more() )
10426 const SMDS_MeshElement* elem = elemIt->next();
10427 if ( elem->GetType() != SMDSAbs_Ball )
10429 if (( elem = mesh->AddBall( elem->GetNode(0),
10430 vtkGrid->GetBallDiameter( elem->getVtkId() ))))
10431 myLastCreatedElems.Append( elem );
10436 vector< const SMDS_MeshNode* > nodes;
10437 while ( elemIt->more() )
10439 const SMDS_MeshElement* elem = elemIt->next();
10440 if ( elem->GetType() != type )
10443 nodes.assign( elem->begin_nodes(), elem->end_nodes() );
10445 if ( type == SMDSAbs_Volume && elem->GetVtkType() == VTK_POLYHEDRON )
10447 std::vector<int> quantities =
10448 static_cast< const SMDS_VtkVolume* >( elem )->GetQuantities();
10449 elem = mesh->AddPolyhedralVolume( nodes, quantities );
10453 AddElement( nodes, type, elem->IsPoly() );
10454 elem = 0; // myLastCreatedElems is already filled
10457 myLastCreatedElems.Append( elem );
10462 //================================================================================
10464 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10465 \param theElems - the list of elements (edges or faces) to be replicated
10466 The nodes for duplication could be found from these elements
10467 \param theNodesNot - list of nodes to NOT replicate
10468 \param theAffectedElems - the list of elements (cells and edges) to which the
10469 replicated nodes should be associated to.
10470 \return TRUE if operation has been completed successfully, FALSE otherwise
10472 //================================================================================
10474 bool SMESH_MeshEditor::DoubleNodes( const TIDSortedElemSet& theElems,
10475 const TIDSortedElemSet& theNodesNot,
10476 const TIDSortedElemSet& theAffectedElems )
10478 myLastCreatedElems.Clear();
10479 myLastCreatedNodes.Clear();
10481 if ( theElems.size() == 0 )
10484 SMESHDS_Mesh* aMeshDS = GetMeshDS();
10489 std::map< const SMDS_MeshNode*, const SMDS_MeshNode* > anOldNodeToNewNode;
10490 // duplicate elements and nodes
10491 res = doubleNodes( aMeshDS, theElems, theNodesNot, anOldNodeToNewNode, true );
10492 // replce nodes by duplications
10493 res = doubleNodes( aMeshDS, theAffectedElems, theNodesNot, anOldNodeToNewNode, false );
10497 //================================================================================
10499 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10500 \param theMeshDS - mesh instance
10501 \param theElems - the elements replicated or modified (nodes should be changed)
10502 \param theNodesNot - nodes to NOT replicate
10503 \param theNodeNodeMap - relation of old node to new created node
10504 \param theIsDoubleElem - flag os to replicate element or modify
10505 \return TRUE if operation has been completed successfully, FALSE otherwise
10507 //================================================================================
10509 bool SMESH_MeshEditor::doubleNodes( SMESHDS_Mesh* theMeshDS,
10510 const TIDSortedElemSet& theElems,
10511 const TIDSortedElemSet& theNodesNot,
10512 std::map< const SMDS_MeshNode*,
10513 const SMDS_MeshNode* >& theNodeNodeMap,
10514 const bool theIsDoubleElem )
10516 MESSAGE("doubleNodes");
10517 // iterate on through element and duplicate them (by nodes duplication)
10519 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10520 for ( ; elemItr != theElems.end(); ++elemItr )
10522 const SMDS_MeshElement* anElem = *elemItr;
10526 bool isDuplicate = false;
10527 // duplicate nodes to duplicate element
10528 std::vector<const SMDS_MeshNode*> newNodes( anElem->NbNodes() );
10529 SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
10531 while ( anIter->more() )
10534 SMDS_MeshNode* aCurrNode = (SMDS_MeshNode*)anIter->next();
10535 SMDS_MeshNode* aNewNode = aCurrNode;
10536 if ( theNodeNodeMap.find( aCurrNode ) != theNodeNodeMap.end() )
10537 aNewNode = (SMDS_MeshNode*)theNodeNodeMap[ aCurrNode ];
10538 else if ( theIsDoubleElem && theNodesNot.find( aCurrNode ) == theNodesNot.end() )
10541 aNewNode = theMeshDS->AddNode( aCurrNode->X(), aCurrNode->Y(), aCurrNode->Z() );
10542 copyPosition( aCurrNode, aNewNode );
10543 theNodeNodeMap[ aCurrNode ] = aNewNode;
10544 myLastCreatedNodes.Append( aNewNode );
10546 isDuplicate |= (aCurrNode != aNewNode);
10547 newNodes[ ind++ ] = aNewNode;
10549 if ( !isDuplicate )
10552 if ( theIsDoubleElem )
10553 AddElement(newNodes, anElem->GetType(), anElem->IsPoly());
10555 theMeshDS->ChangeElementNodes( anElem, &newNodes[ 0 ], anElem->NbNodes() );
10562 //================================================================================
10564 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10565 \param theNodes - identifiers of nodes to be doubled
10566 \param theModifiedElems - identifiers of elements to be updated by the new (doubled)
10567 nodes. If list of element identifiers is empty then nodes are doubled but
10568 they not assigned to elements
10569 \return TRUE if operation has been completed successfully, FALSE otherwise
10571 //================================================================================
10573 bool SMESH_MeshEditor::DoubleNodes( const std::list< int >& theListOfNodes,
10574 const std::list< int >& theListOfModifiedElems )
10576 MESSAGE("DoubleNodes");
10577 myLastCreatedElems.Clear();
10578 myLastCreatedNodes.Clear();
10580 if ( theListOfNodes.size() == 0 )
10583 SMESHDS_Mesh* aMeshDS = GetMeshDS();
10587 // iterate through nodes and duplicate them
10589 std::map< const SMDS_MeshNode*, const SMDS_MeshNode* > anOldNodeToNewNode;
10591 std::list< int >::const_iterator aNodeIter;
10592 for ( aNodeIter = theListOfNodes.begin(); aNodeIter != theListOfNodes.end(); ++aNodeIter )
10594 int aCurr = *aNodeIter;
10595 SMDS_MeshNode* aNode = (SMDS_MeshNode*)aMeshDS->FindNode( aCurr );
10601 const SMDS_MeshNode* aNewNode = aMeshDS->AddNode( aNode->X(), aNode->Y(), aNode->Z() );
10604 copyPosition( aNode, aNewNode );
10605 anOldNodeToNewNode[ aNode ] = aNewNode;
10606 myLastCreatedNodes.Append( aNewNode );
10610 // Create map of new nodes for modified elements
10612 std::map< SMDS_MeshElement*, vector<const SMDS_MeshNode*> > anElemToNodes;
10614 std::list< int >::const_iterator anElemIter;
10615 for ( anElemIter = theListOfModifiedElems.begin();
10616 anElemIter != theListOfModifiedElems.end(); ++anElemIter )
10618 int aCurr = *anElemIter;
10619 SMDS_MeshElement* anElem = (SMDS_MeshElement*)aMeshDS->FindElement( aCurr );
10623 vector<const SMDS_MeshNode*> aNodeArr( anElem->NbNodes() );
10625 SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
10627 while ( anIter->more() )
10629 SMDS_MeshNode* aCurrNode = (SMDS_MeshNode*)anIter->next();
10630 if ( aCurr && anOldNodeToNewNode.find( aCurrNode ) != anOldNodeToNewNode.end() )
10632 const SMDS_MeshNode* aNewNode = anOldNodeToNewNode[ aCurrNode ];
10633 aNodeArr[ ind++ ] = aNewNode;
10636 aNodeArr[ ind++ ] = aCurrNode;
10638 anElemToNodes[ anElem ] = aNodeArr;
10641 // Change nodes of elements
10643 std::map< SMDS_MeshElement*, vector<const SMDS_MeshNode*> >::iterator
10644 anElemToNodesIter = anElemToNodes.begin();
10645 for ( ; anElemToNodesIter != anElemToNodes.end(); ++anElemToNodesIter )
10647 const SMDS_MeshElement* anElem = anElemToNodesIter->first;
10648 vector<const SMDS_MeshNode*> aNodeArr = anElemToNodesIter->second;
10651 MESSAGE("ChangeElementNodes");
10652 aMeshDS->ChangeElementNodes( anElem, &aNodeArr[ 0 ], anElem->NbNodes() );
10661 //================================================================================
10663 \brief Check if element located inside shape
10664 \return TRUE if IN or ON shape, FALSE otherwise
10666 //================================================================================
10668 template<class Classifier>
10669 bool isInside(const SMDS_MeshElement* theElem,
10670 Classifier& theClassifier,
10671 const double theTol)
10673 gp_XYZ centerXYZ (0, 0, 0);
10674 SMDS_ElemIteratorPtr aNodeItr = theElem->nodesIterator();
10675 while (aNodeItr->more())
10676 centerXYZ += SMESH_TNodeXYZ(cast2Node( aNodeItr->next()));
10678 gp_Pnt aPnt = centerXYZ / theElem->NbNodes();
10679 theClassifier.Perform(aPnt, theTol);
10680 TopAbs_State aState = theClassifier.State();
10681 return (aState == TopAbs_IN || aState == TopAbs_ON );
10684 //================================================================================
10686 * \brief Classifier of the 3D point on the TopoDS_Face
10687 * with interaface suitable for isInside()
10689 //================================================================================
10691 struct _FaceClassifier
10693 Extrema_ExtPS _extremum;
10694 BRepAdaptor_Surface _surface;
10695 TopAbs_State _state;
10697 _FaceClassifier(const TopoDS_Face& face):_extremum(),_surface(face),_state(TopAbs_OUT)
10699 _extremum.Initialize( _surface,
10700 _surface.FirstUParameter(), _surface.LastUParameter(),
10701 _surface.FirstVParameter(), _surface.LastVParameter(),
10702 _surface.Tolerance(), _surface.Tolerance() );
10704 void Perform(const gp_Pnt& aPnt, double theTol)
10707 _state = TopAbs_OUT;
10708 _extremum.Perform(aPnt);
10709 if ( _extremum.IsDone() )
10710 for ( int iSol = 1; iSol <= _extremum.NbExt() && _state == TopAbs_OUT; ++iSol)
10711 _state = ( _extremum.SquareDistance(iSol) <= theTol ? TopAbs_IN : TopAbs_OUT );
10713 TopAbs_State State() const
10720 //================================================================================
10722 \brief Identify the elements that will be affected by node duplication (actual duplication is not performed).
10723 This method is the first step of DoubleNodeElemGroupsInRegion.
10724 \param theElems - list of groups of elements (edges or faces) to be replicated
10725 \param theNodesNot - list of groups of nodes not to replicated
10726 \param theShape - shape to detect affected elements (element which geometric center
10727 located on or inside shape). If the shape is null, detection is done on faces orientations
10728 (select elements with a gravity center on the side given by faces normals).
10729 This mode (null shape) is faster, but works only when theElems are faces, with coherents orientations.
10730 The replicated nodes should be associated to affected elements.
10731 \return groups of affected elements
10732 \sa DoubleNodeElemGroupsInRegion()
10734 //================================================================================
10736 bool SMESH_MeshEditor::AffectedElemGroupsInRegion( const TIDSortedElemSet& theElems,
10737 const TIDSortedElemSet& theNodesNot,
10738 const TopoDS_Shape& theShape,
10739 TIDSortedElemSet& theAffectedElems)
10741 if ( theShape.IsNull() )
10743 std::set<const SMDS_MeshNode*> alreadyCheckedNodes;
10744 std::set<const SMDS_MeshElement*> alreadyCheckedElems;
10745 std::set<const SMDS_MeshElement*> edgesToCheck;
10746 alreadyCheckedNodes.clear();
10747 alreadyCheckedElems.clear();
10748 edgesToCheck.clear();
10750 // --- iterates on elements to be replicated and get elements by back references from their nodes
10752 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10754 for ( ielem=1; elemItr != theElems.end(); ++elemItr )
10756 SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
10757 if (!anElem || (anElem->GetType() != SMDSAbs_Face))
10760 SMESH_MeshAlgos::FaceNormal( anElem, normal, /*normalized=*/true );
10761 MESSAGE("element " << ielem++ << " normal " << normal.X() << " " << normal.Y() << " " << normal.Z());
10762 std::set<const SMDS_MeshNode*> nodesElem;
10764 SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
10765 while ( nodeItr->more() )
10767 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10768 nodesElem.insert(aNode);
10770 std::set<const SMDS_MeshNode*>::iterator nodit = nodesElem.begin();
10771 for (; nodit != nodesElem.end(); nodit++)
10773 MESSAGE(" noeud ");
10774 const SMDS_MeshNode* aNode = *nodit;
10775 if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
10777 if (alreadyCheckedNodes.find(aNode) != alreadyCheckedNodes.end())
10779 alreadyCheckedNodes.insert(aNode);
10780 SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
10781 while ( backElemItr->more() )
10783 MESSAGE(" backelem ");
10784 const SMDS_MeshElement* curElem = backElemItr->next();
10785 if (alreadyCheckedElems.find(curElem) != alreadyCheckedElems.end())
10787 if (theElems.find(curElem) != theElems.end())
10789 alreadyCheckedElems.insert(curElem);
10790 double x=0, y=0, z=0;
10792 SMDS_ElemIteratorPtr nodeItr2 = curElem->nodesIterator();
10793 while ( nodeItr2->more() )
10795 const SMDS_MeshNode* anotherNode = cast2Node(nodeItr2->next());
10796 x += anotherNode->X();
10797 y += anotherNode->Y();
10798 z += anotherNode->Z();
10802 p.SetCoord( x/nb -aNode->X(),
10804 z/nb -aNode->Z() );
10805 MESSAGE(" check " << p.X() << " " << p.Y() << " " << p.Z());
10808 MESSAGE(" --- inserted")
10809 theAffectedElems.insert( curElem );
10811 else if (curElem->GetType() == SMDSAbs_Edge)
10812 edgesToCheck.insert(curElem);
10816 // --- add also edges lying on the set of faces (all nodes in alreadyCheckedNodes)
10817 std::set<const SMDS_MeshElement*>::iterator eit = edgesToCheck.begin();
10818 for( ; eit != edgesToCheck.end(); eit++)
10820 bool onside = true;
10821 const SMDS_MeshElement* anEdge = *eit;
10822 SMDS_ElemIteratorPtr nodeItr = anEdge->nodesIterator();
10823 while ( nodeItr->more() )
10825 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10826 if (alreadyCheckedNodes.find(aNode) == alreadyCheckedNodes.end())
10834 MESSAGE(" --- edge onside inserted")
10835 theAffectedElems.insert(anEdge);
10841 const double aTol = Precision::Confusion();
10842 auto_ptr< BRepClass3d_SolidClassifier> bsc3d;
10843 auto_ptr<_FaceClassifier> aFaceClassifier;
10844 if ( theShape.ShapeType() == TopAbs_SOLID )
10846 bsc3d.reset( new BRepClass3d_SolidClassifier(theShape));;
10847 bsc3d->PerformInfinitePoint(aTol);
10849 else if (theShape.ShapeType() == TopAbs_FACE )
10851 aFaceClassifier.reset( new _FaceClassifier(TopoDS::Face(theShape)));
10854 // iterates on indicated elements and get elements by back references from their nodes
10855 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10857 for ( ielem = 1; elemItr != theElems.end(); ++elemItr )
10859 MESSAGE("element " << ielem++);
10860 SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
10863 SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
10864 while ( nodeItr->more() )
10866 MESSAGE(" noeud ");
10867 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10868 if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
10870 SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
10871 while ( backElemItr->more() )
10873 MESSAGE(" backelem ");
10874 const SMDS_MeshElement* curElem = backElemItr->next();
10875 if ( curElem && theElems.find(curElem) == theElems.end() &&
10877 isInside( curElem, *bsc3d, aTol ) :
10878 isInside( curElem, *aFaceClassifier, aTol )))
10879 theAffectedElems.insert( curElem );
10887 //================================================================================
10889 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10890 \param theElems - group of of elements (edges or faces) to be replicated
10891 \param theNodesNot - group of nodes not to replicate
10892 \param theShape - shape to detect affected elements (element which geometric center
10893 located on or inside shape).
10894 The replicated nodes should be associated to affected elements.
10895 \return TRUE if operation has been completed successfully, FALSE otherwise
10897 //================================================================================
10899 bool SMESH_MeshEditor::DoubleNodesInRegion( const TIDSortedElemSet& theElems,
10900 const TIDSortedElemSet& theNodesNot,
10901 const TopoDS_Shape& theShape )
10903 if ( theShape.IsNull() )
10906 const double aTol = Precision::Confusion();
10907 auto_ptr< BRepClass3d_SolidClassifier> bsc3d;
10908 auto_ptr<_FaceClassifier> aFaceClassifier;
10909 if ( theShape.ShapeType() == TopAbs_SOLID )
10911 bsc3d.reset( new BRepClass3d_SolidClassifier(theShape));;
10912 bsc3d->PerformInfinitePoint(aTol);
10914 else if (theShape.ShapeType() == TopAbs_FACE )
10916 aFaceClassifier.reset( new _FaceClassifier(TopoDS::Face(theShape)));
10919 // iterates on indicated elements and get elements by back references from their nodes
10920 TIDSortedElemSet anAffected;
10921 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10922 for ( ; elemItr != theElems.end(); ++elemItr )
10924 SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
10928 SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
10929 while ( nodeItr->more() )
10931 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10932 if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
10934 SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
10935 while ( backElemItr->more() )
10937 const SMDS_MeshElement* curElem = backElemItr->next();
10938 if ( curElem && theElems.find(curElem) == theElems.end() &&
10940 isInside( curElem, *bsc3d, aTol ) :
10941 isInside( curElem, *aFaceClassifier, aTol )))
10942 anAffected.insert( curElem );
10946 return DoubleNodes( theElems, theNodesNot, anAffected );
10950 * \brief compute an oriented angle between two planes defined by four points.
10951 * The vector (p0,p1) defines the intersection of the 2 planes (p0,p1,g1) and (p0,p1,g2)
10952 * @param p0 base of the rotation axe
10953 * @param p1 extremity of the rotation axe
10954 * @param g1 belongs to the first plane
10955 * @param g2 belongs to the second plane
10957 double SMESH_MeshEditor::OrientedAngle(const gp_Pnt& p0, const gp_Pnt& p1, const gp_Pnt& g1, const gp_Pnt& g2)
10959 // MESSAGE(" p0: " << p0.X() << " " << p0.Y() << " " << p0.Z());
10960 // MESSAGE(" p1: " << p1.X() << " " << p1.Y() << " " << p1.Z());
10961 // MESSAGE(" g1: " << g1.X() << " " << g1.Y() << " " << g1.Z());
10962 // MESSAGE(" g2: " << g2.X() << " " << g2.Y() << " " << g2.Z());
10963 gp_Vec vref(p0, p1);
10966 gp_Vec n1 = vref.Crossed(v1);
10967 gp_Vec n2 = vref.Crossed(v2);
10969 return n2.AngleWithRef(n1, vref);
10971 catch ( Standard_Failure ) {
10973 return Max( v1.Magnitude(), v2.Magnitude() );
10977 * \brief Double nodes on shared faces between groups of volumes and create flat elements on demand.
10978 * The list of groups must contain at least two groups. The groups have to be disjoint: no common element into two different groups.
10979 * The nodes of the internal faces at the boundaries of the groups are doubled. Optionally, the internal faces are replaced by flat elements.
10980 * Triangles are transformed into prisms, and quadrangles into hexahedrons.
10981 * The flat elements are stored in groups of volumes. These groups are named according to the position of the group in the list:
10982 * 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.
10983 * 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.
10984 * All the flat elements are gathered into the group named "joints3D" (or "joints2D" in 2D situation).
10985 * The flat element of the multiple junctions between the simple junction are stored in a group named "jointsMultiples".
10986 * \param theElems - list of groups of volumes, where a group of volume is a set of
10987 * SMDS_MeshElements sorted by Id.
10988 * \param createJointElems - if TRUE, create the elements
10989 * \param onAllBoundaries - if TRUE, the nodes and elements are also created on
10990 * the boundary between \a theDomains and the rest mesh
10991 * \return TRUE if operation has been completed successfully, FALSE otherwise
10993 bool SMESH_MeshEditor::DoubleNodesOnGroupBoundaries( const std::vector<TIDSortedElemSet>& theElems,
10994 bool createJointElems,
10995 bool onAllBoundaries)
10997 MESSAGE("----------------------------------------------");
10998 MESSAGE("SMESH_MeshEditor::doubleNodesOnGroupBoundaries");
10999 MESSAGE("----------------------------------------------");
11001 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11002 meshDS->BuildDownWardConnectivity(true);
11004 SMDS_UnstructuredGrid *grid = meshDS->getGrid();
11006 // --- build the list of faces shared by 2 domains (group of elements), with their domain and volume indexes
11007 // build the list of cells with only a node or an edge on the border, with their domain and volume indexes
11008 // build the list of nodes shared by 2 or more domains, with their domain indexes
11010 std::map<DownIdType, std::map<int,int>, DownIdCompare> faceDomains; // face --> (id domain --> id volume)
11011 std::map<int,int>celldom; // cell vtkId --> domain
11012 std::map<DownIdType, std::map<int,int>, DownIdCompare> cellDomains; // oldNode --> (id domain --> id cell)
11013 std::map<int, std::map<int,int> > nodeDomains; // oldId --> (domainId --> newId)
11014 faceDomains.clear();
11016 cellDomains.clear();
11017 nodeDomains.clear();
11018 std::map<int,int> emptyMap;
11019 std::set<int> emptySet;
11022 MESSAGE(".. Number of domains :"<<theElems.size());
11024 TIDSortedElemSet theRestDomElems;
11025 const int iRestDom = -1;
11026 const int idom0 = onAllBoundaries ? iRestDom : 0;
11027 const int nbDomains = theElems.size();
11029 // Check if the domains do not share an element
11030 for (int idom = 0; idom < nbDomains-1; idom++)
11032 // MESSAGE("... Check of domain #" << idom);
11033 const TIDSortedElemSet& domain = theElems[idom];
11034 TIDSortedElemSet::const_iterator elemItr = domain.begin();
11035 for (; elemItr != domain.end(); ++elemItr)
11037 const SMDS_MeshElement* anElem = *elemItr;
11038 int idombisdeb = idom + 1 ;
11039 for (int idombis = idombisdeb; idombis < theElems.size(); idombis++) // check if the element belongs to a domain further in the list
11041 const TIDSortedElemSet& domainbis = theElems[idombis];
11042 if ( domainbis.count(anElem) )
11044 MESSAGE(".... Domain #" << idom);
11045 MESSAGE(".... Domain #" << idombis);
11046 throw SALOME_Exception("The domains are not disjoint.");
11053 for (int idom = 0; idom < nbDomains; idom++)
11056 // --- build a map (face to duplicate --> volume to modify)
11057 // with all the faces shared by 2 domains (group of elements)
11058 // and corresponding volume of this domain, for each shared face.
11059 // a volume has a face shared by 2 domains if it has a neighbor which is not in his domain.
11061 MESSAGE("... Neighbors of domain #" << idom);
11062 const TIDSortedElemSet& domain = theElems[idom];
11063 TIDSortedElemSet::const_iterator elemItr = domain.begin();
11064 for (; elemItr != domain.end(); ++elemItr)
11066 const SMDS_MeshElement* anElem = *elemItr;
11069 int vtkId = anElem->getVtkId();
11070 //MESSAGE(" vtkId " << vtkId << " smdsId " << anElem->GetID());
11071 int neighborsVtkIds[NBMAXNEIGHBORS];
11072 int downIds[NBMAXNEIGHBORS];
11073 unsigned char downTypes[NBMAXNEIGHBORS];
11074 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
11075 for (int n = 0; n < nbNeighbors; n++)
11077 int smdsId = meshDS->fromVtkToSmds(neighborsVtkIds[n]);
11078 const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
11079 if (elem && ! domain.count(elem)) // neighbor is in another domain : face is shared
11082 for (int idombis = 0; idombis < theElems.size() && !ok; idombis++) // check if the neighbor belongs to another domain of the list
11084 // MESSAGE("Domain " << idombis);
11085 const TIDSortedElemSet& domainbis = theElems[idombis];
11086 if ( domainbis.count(elem)) ok = true ; // neighbor is in a correct domain : face is kept
11088 if ( ok || onAllBoundaries ) // the characteristics of the face is stored
11090 DownIdType face(downIds[n], downTypes[n]);
11091 if (!faceDomains[face].count(idom))
11093 faceDomains[face][idom] = vtkId; // volume associated to face in this domain
11094 celldom[vtkId] = idom;
11095 //MESSAGE(" cell with a border " << vtkId << " domain " << idom);
11099 theRestDomElems.insert( elem );
11100 faceDomains[face][iRestDom] = neighborsVtkIds[n];
11101 celldom[neighborsVtkIds[n]] = iRestDom;
11109 //MESSAGE("Number of shared faces " << faceDomains.size());
11110 std::map<DownIdType, std::map<int, int>, DownIdCompare>::iterator itface;
11112 // --- explore the shared faces domain by domain,
11113 // explore the nodes of the face and see if they belong to a cell in the domain,
11114 // which has only a node or an edge on the border (not a shared face)
11116 for (int idomain = idom0; idomain < nbDomains; idomain++)
11118 //MESSAGE("Domain " << idomain);
11119 const TIDSortedElemSet& domain = (idomain == iRestDom) ? theRestDomElems : theElems[idomain];
11120 itface = faceDomains.begin();
11121 for (; itface != faceDomains.end(); ++itface)
11123 const std::map<int, int>& domvol = itface->second;
11124 if (!domvol.count(idomain))
11126 DownIdType face = itface->first;
11127 //MESSAGE(" --- face " << face.cellId);
11128 std::set<int> oldNodes;
11130 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11131 std::set<int>::iterator itn = oldNodes.begin();
11132 for (; itn != oldNodes.end(); ++itn)
11135 //MESSAGE(" node " << oldId);
11136 vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
11137 for (int i=0; i<l.ncells; i++)
11139 int vtkId = l.cells[i];
11140 const SMDS_MeshElement* anElem = GetMeshDS()->FindElement(GetMeshDS()->fromVtkToSmds(vtkId));
11141 if (!domain.count(anElem))
11143 int vtkType = grid->GetCellType(vtkId);
11144 int downId = grid->CellIdToDownId(vtkId);
11147 MESSAGE("doubleNodesOnGroupBoundaries: internal algorithm problem");
11148 continue; // not OK at this stage of the algorithm:
11149 //no cells created after BuildDownWardConnectivity
11151 DownIdType aCell(downId, vtkType);
11152 cellDomains[aCell][idomain] = vtkId;
11153 celldom[vtkId] = idomain;
11154 //MESSAGE(" cell " << vtkId << " domain " << idomain);
11160 // --- explore the shared faces domain by domain, to duplicate the nodes in a coherent way
11161 // for each shared face, get the nodes
11162 // for each node, for each domain of the face, create a clone of the node
11164 // --- edges at the intersection of 3 or 4 domains, with the order of domains to build
11165 // junction elements of type prism or hexa. the key is the pair of nodesId (lower first)
11166 // the value is the ordered domain ids. (more than 4 domains not taken into account)
11168 std::map<std::vector<int>, std::vector<int> > edgesMultiDomains; // nodes of edge --> ordered domains
11169 std::map<int, std::vector<int> > mutipleNodes; // nodes multi domains with domain order
11170 std::map<int, std::vector<int> > mutipleNodesToFace; // nodes multi domains with domain order to transform in Face (junction between 3 or more 2D domains)
11172 MESSAGE(".. Duplication of the nodes");
11173 for (int idomain = idom0; idomain < nbDomains; idomain++)
11175 itface = faceDomains.begin();
11176 for (; itface != faceDomains.end(); ++itface)
11178 const std::map<int, int>& domvol = itface->second;
11179 if (!domvol.count(idomain))
11181 DownIdType face = itface->first;
11182 //MESSAGE(" --- face " << face.cellId);
11183 std::set<int> oldNodes;
11185 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11186 std::set<int>::iterator itn = oldNodes.begin();
11187 for (; itn != oldNodes.end(); ++itn)
11190 if (nodeDomains[oldId].empty())
11192 nodeDomains[oldId][idomain] = oldId; // keep the old node in the first domain
11193 //MESSAGE("-+-+-b oldNode " << oldId << " domain " << idomain);
11195 std::map<int, int>::const_iterator itdom = domvol.begin();
11196 for (; itdom != domvol.end(); ++itdom)
11198 int idom = itdom->first;
11199 //MESSAGE(" domain " << idom);
11200 if (!nodeDomains[oldId].count(idom)) // --- node to clone
11202 if (nodeDomains[oldId].size() >= 2) // a multiple node
11204 vector<int> orderedDoms;
11205 //MESSAGE("multiple node " << oldId);
11206 if (mutipleNodes.count(oldId))
11207 orderedDoms = mutipleNodes[oldId];
11210 map<int,int>::iterator it = nodeDomains[oldId].begin();
11211 for (; it != nodeDomains[oldId].end(); ++it)
11212 orderedDoms.push_back(it->first);
11214 orderedDoms.push_back(idom); // TODO order ==> push_front or back
11215 //stringstream txt;
11216 //for (int i=0; i<orderedDoms.size(); i++)
11217 // txt << orderedDoms[i] << " ";
11218 //MESSAGE("orderedDoms " << txt.str());
11219 mutipleNodes[oldId] = orderedDoms;
11221 double *coords = grid->GetPoint(oldId);
11222 SMDS_MeshNode *newNode = meshDS->AddNode(coords[0], coords[1], coords[2]);
11223 copyPosition( meshDS->FindNodeVtk( oldId ), newNode );
11224 int newId = newNode->getVtkId();
11225 nodeDomains[oldId][idom] = newId; // cloned node for other domains
11226 //MESSAGE("-+-+-c oldNode " << oldId << " domain " << idomain << " newNode " << newId << " domain " << idom << " size=" <<nodeDomains[oldId].size());
11233 MESSAGE(".. Creation of elements");
11234 for (int idomain = idom0; idomain < nbDomains; idomain++)
11236 itface = faceDomains.begin();
11237 for (; itface != faceDomains.end(); ++itface)
11239 std::map<int, int> domvol = itface->second;
11240 if (!domvol.count(idomain))
11242 DownIdType face = itface->first;
11243 //MESSAGE(" --- face " << face.cellId);
11244 std::set<int> oldNodes;
11246 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11247 int nbMultipleNodes = 0;
11248 std::set<int>::iterator itn = oldNodes.begin();
11249 for (; itn != oldNodes.end(); ++itn)
11252 if (mutipleNodes.count(oldId))
11255 if (nbMultipleNodes > 1) // check if an edge of the face is shared between 3 or more domains
11257 //MESSAGE("multiple Nodes detected on a shared face");
11258 int downId = itface->first.cellId;
11259 unsigned char cellType = itface->first.cellType;
11260 // --- shared edge or shared face ?
11261 if ((cellType == VTK_LINE) || (cellType == VTK_QUADRATIC_EDGE)) // shared edge (between two faces)
11264 int nbNodes = grid->getDownArray(cellType)->getNodes(downId, nodes);
11265 for (int i=0; i< nbNodes; i=i+nbNodes-1) // i=0 , i=nbNodes-1
11266 if (mutipleNodes.count(nodes[i]))
11267 if (!mutipleNodesToFace.count(nodes[i]))
11268 mutipleNodesToFace[nodes[i]] = mutipleNodes[nodes[i]];
11270 else // shared face (between two volumes)
11272 int nbEdges = grid->getDownArray(cellType)->getNumberOfDownCells(downId);
11273 const int* downEdgeIds = grid->getDownArray(cellType)->getDownCells(downId);
11274 const unsigned char* edgeType = grid->getDownArray(cellType)->getDownTypes(downId);
11275 for (int ie =0; ie < nbEdges; ie++)
11278 int nbNodes = grid->getDownArray(edgeType[ie])->getNodes(downEdgeIds[ie], nodes);
11279 if (mutipleNodes.count(nodes[0]) && mutipleNodes.count(nodes[nbNodes-1]))
11281 vector<int> vn0 = mutipleNodes[nodes[0]];
11282 vector<int> vn1 = mutipleNodes[nodes[nbNodes - 1]];
11284 for (int i0 = 0; i0 < vn0.size(); i0++)
11285 for (int i1 = 0; i1 < vn1.size(); i1++)
11286 if (vn0[i0] == vn1[i1])
11287 doms.push_back(vn0[i0]);
11288 if (doms.size() >2)
11290 //MESSAGE(" detect edgesMultiDomains " << nodes[0] << " " << nodes[nbNodes - 1]);
11291 double *coords = grid->GetPoint(nodes[0]);
11292 gp_Pnt p0(coords[0], coords[1], coords[2]);
11293 coords = grid->GetPoint(nodes[nbNodes - 1]);
11294 gp_Pnt p1(coords[0], coords[1], coords[2]);
11296 int vtkVolIds[1000]; // an edge can belong to a lot of volumes
11297 map<int, SMDS_VtkVolume*> domvol; // domain --> a volume with the edge
11298 map<int, double> angleDom; // oriented angles between planes defined by edge and volume centers
11299 int nbvol = grid->GetParentVolumes(vtkVolIds, downEdgeIds[ie], edgeType[ie]);
11300 for (int id=0; id < doms.size(); id++)
11302 int idom = doms[id];
11303 const TIDSortedElemSet& domain = (idom == iRestDom) ? theRestDomElems : theElems[idom];
11304 for (int ivol=0; ivol<nbvol; ivol++)
11306 int smdsId = meshDS->fromVtkToSmds(vtkVolIds[ivol]);
11307 SMDS_MeshElement* elem = (SMDS_MeshElement*)meshDS->FindElement(smdsId);
11308 if (domain.count(elem))
11310 SMDS_VtkVolume* svol = dynamic_cast<SMDS_VtkVolume*>(elem);
11311 domvol[idom] = svol;
11312 //MESSAGE(" domain " << idom << " volume " << elem->GetID());
11314 vtkIdType npts = 0;
11315 vtkIdType* pts = 0;
11316 grid->GetCellPoints(vtkVolIds[ivol], npts, pts);
11317 SMDS_VtkVolume::gravityCenter(grid, pts, npts, values);
11320 gref.SetXYZ(gp_XYZ(values[0], values[1], values[2]));
11321 angleDom[idom] = 0;
11325 gp_Pnt g(values[0], values[1], values[2]);
11326 angleDom[idom] = OrientedAngle(p0, p1, gref, g); // -pi<angle<+pi
11327 //MESSAGE(" angle=" << angleDom[idom]);
11333 map<double, int> sortedDom; // sort domains by angle
11334 for (map<int, double>::iterator ia = angleDom.begin(); ia != angleDom.end(); ++ia)
11335 sortedDom[ia->second] = ia->first;
11336 vector<int> vnodes;
11338 for (map<double, int>::iterator ib = sortedDom.begin(); ib != sortedDom.end(); ++ib)
11340 vdom.push_back(ib->second);
11341 //MESSAGE(" ordered domain " << ib->second << " angle " << ib->first);
11343 for (int ino = 0; ino < nbNodes; ino++)
11344 vnodes.push_back(nodes[ino]);
11345 edgesMultiDomains[vnodes] = vdom; // nodes vector --> ordered domains
11354 // --- iterate on shared faces (volumes to modify, face to extrude)
11355 // get node id's of the face (id SMDS = id VTK)
11356 // create flat element with old and new nodes if requested
11358 // --- new quad nodes on flat quad elements: oldId --> ((domain1 X domain2) --> newId)
11359 // (domain1 X domain2) = domain1 + MAXINT*domain2
11361 std::map<int, std::map<long,int> > nodeQuadDomains;
11362 std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11364 MESSAGE(".. Creation of elements: simple junction");
11365 if (createJointElems)
11368 string joints2DName = "joints2D";
11369 mapOfJunctionGroups[joints2DName] = this->myMesh->AddGroup(SMDSAbs_Face, joints2DName.c_str(), idg);
11370 SMESHDS_Group *joints2DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints2DName]->GetGroupDS());
11371 string joints3DName = "joints3D";
11372 mapOfJunctionGroups[joints3DName] = this->myMesh->AddGroup(SMDSAbs_Volume, joints3DName.c_str(), idg);
11373 SMESHDS_Group *joints3DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints3DName]->GetGroupDS());
11375 itface = faceDomains.begin();
11376 for (; itface != faceDomains.end(); ++itface)
11378 DownIdType face = itface->first;
11379 std::set<int> oldNodes;
11380 std::set<int>::iterator itn;
11382 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11384 std::map<int, int> domvol = itface->second;
11385 std::map<int, int>::iterator itdom = domvol.begin();
11386 int dom1 = itdom->first;
11387 int vtkVolId = itdom->second;
11389 int dom2 = itdom->first;
11390 SMDS_MeshCell *vol = grid->extrudeVolumeFromFace(vtkVolId, dom1, dom2, oldNodes, nodeDomains,
11392 stringstream grpname;
11395 grpname << dom1 << "_" << dom2;
11397 grpname << dom2 << "_" << dom1;
11398 string namegrp = grpname.str();
11399 if (!mapOfJunctionGroups.count(namegrp))
11400 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(vol->GetType(), namegrp.c_str(), idg);
11401 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11403 sgrp->Add(vol->GetID());
11404 if (vol->GetType() == SMDSAbs_Volume)
11405 joints3DGrp->Add(vol->GetID());
11406 else if (vol->GetType() == SMDSAbs_Face)
11407 joints2DGrp->Add(vol->GetID());
11411 // --- create volumes on multiple domain intersection if requested
11412 // iterate on mutipleNodesToFace
11413 // iterate on edgesMultiDomains
11415 MESSAGE(".. Creation of elements: multiple junction");
11416 if (createJointElems)
11418 // --- iterate on mutipleNodesToFace
11420 std::map<int, std::vector<int> >::iterator itn = mutipleNodesToFace.begin();
11421 for (; itn != mutipleNodesToFace.end(); ++itn)
11423 int node = itn->first;
11424 vector<int> orderDom = itn->second;
11425 vector<vtkIdType> orderedNodes;
11426 for (int idom = 0; idom <orderDom.size(); idom++)
11427 orderedNodes.push_back( nodeDomains[node][orderDom[idom]] );
11428 SMDS_MeshFace* face = this->GetMeshDS()->AddFaceFromVtkIds(orderedNodes);
11430 stringstream grpname;
11432 grpname << 0 << "_" << 0;
11434 string namegrp = grpname.str();
11435 if (!mapOfJunctionGroups.count(namegrp))
11436 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Face, namegrp.c_str(), idg);
11437 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11439 sgrp->Add(face->GetID());
11442 // --- iterate on edgesMultiDomains
11444 std::map<std::vector<int>, std::vector<int> >::iterator ite = edgesMultiDomains.begin();
11445 for (; ite != edgesMultiDomains.end(); ++ite)
11447 vector<int> nodes = ite->first;
11448 vector<int> orderDom = ite->second;
11449 vector<vtkIdType> orderedNodes;
11450 if (nodes.size() == 2)
11452 //MESSAGE(" use edgesMultiDomains " << nodes[0] << " " << nodes[1]);
11453 for (int ino=0; ino < nodes.size(); ino++)
11454 if (orderDom.size() == 3)
11455 for (int idom = 0; idom <orderDom.size(); idom++)
11456 orderedNodes.push_back( nodeDomains[nodes[ino]][orderDom[idom]] );
11458 for (int idom = orderDom.size()-1; idom >=0; idom--)
11459 orderedNodes.push_back( nodeDomains[nodes[ino]][orderDom[idom]] );
11460 SMDS_MeshVolume* vol = this->GetMeshDS()->AddVolumeFromVtkIds(orderedNodes);
11463 string namegrp = "jointsMultiples";
11464 if (!mapOfJunctionGroups.count(namegrp))
11465 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
11466 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11468 sgrp->Add(vol->GetID());
11472 INFOS("Quadratic multiple joints not implemented");
11473 // TODO quadratic nodes
11478 // --- list the explicit faces and edges of the mesh that need to be modified,
11479 // i.e. faces and edges built with one or more duplicated nodes.
11480 // associate these faces or edges to their corresponding domain.
11481 // only the first domain found is kept when a face or edge is shared
11483 std::map<DownIdType, std::map<int,int>, DownIdCompare> faceOrEdgeDom; // cellToModify --> (id domain --> id cell)
11484 std::map<int,int> feDom; // vtk id of cell to modify --> id domain
11485 faceOrEdgeDom.clear();
11488 MESSAGE(".. Modification of elements");
11489 for (int idomain = idom0; idomain < nbDomains; idomain++)
11491 std::map<int, std::map<int, int> >::const_iterator itnod = nodeDomains.begin();
11492 for (; itnod != nodeDomains.end(); ++itnod)
11494 int oldId = itnod->first;
11495 //MESSAGE(" node " << oldId);
11496 vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
11497 for (int i = 0; i < l.ncells; i++)
11499 int vtkId = l.cells[i];
11500 int vtkType = grid->GetCellType(vtkId);
11501 int downId = grid->CellIdToDownId(vtkId);
11503 continue; // new cells: not to be modified
11504 DownIdType aCell(downId, vtkType);
11505 int volParents[1000];
11506 int nbvol = grid->GetParentVolumes(volParents, vtkId);
11507 for (int j = 0; j < nbvol; j++)
11508 if (celldom.count(volParents[j]) && (celldom[volParents[j]] == idomain))
11509 if (!feDom.count(vtkId))
11511 feDom[vtkId] = idomain;
11512 faceOrEdgeDom[aCell] = emptyMap;
11513 faceOrEdgeDom[aCell][idomain] = vtkId; // affect face or edge to the first domain only
11514 //MESSAGE("affect cell " << this->GetMeshDS()->fromVtkToSmds(vtkId) << " domain " << idomain
11515 // << " type " << vtkType << " downId " << downId);
11521 // --- iterate on shared faces (volumes to modify, face to extrude)
11522 // get node id's of the face
11523 // replace old nodes by new nodes in volumes, and update inverse connectivity
11525 std::map<DownIdType, std::map<int,int>, DownIdCompare>* maps[3] = {&faceDomains, &cellDomains, &faceOrEdgeDom};
11526 for (int m=0; m<3; m++)
11528 std::map<DownIdType, std::map<int,int>, DownIdCompare>* amap = maps[m];
11529 itface = (*amap).begin();
11530 for (; itface != (*amap).end(); ++itface)
11532 DownIdType face = itface->first;
11533 std::set<int> oldNodes;
11534 std::set<int>::iterator itn;
11536 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11537 //MESSAGE("examine cell, downId " << face.cellId << " type " << int(face.cellType));
11538 std::map<int, int> localClonedNodeIds;
11540 std::map<int, int> domvol = itface->second;
11541 std::map<int, int>::iterator itdom = domvol.begin();
11542 for (; itdom != domvol.end(); ++itdom)
11544 int idom = itdom->first;
11545 int vtkVolId = itdom->second;
11546 //MESSAGE("modify nodes of cell " << this->GetMeshDS()->fromVtkToSmds(vtkVolId) << " domain " << idom);
11547 localClonedNodeIds.clear();
11548 for (itn = oldNodes.begin(); itn != oldNodes.end(); ++itn)
11551 if (nodeDomains[oldId].count(idom))
11553 localClonedNodeIds[oldId] = nodeDomains[oldId][idom];
11554 //MESSAGE(" node " << oldId << " --> " << localClonedNodeIds[oldId]);
11557 meshDS->ModifyCellNodes(vtkVolId, localClonedNodeIds);
11562 // Remove empty groups (issue 0022812)
11563 std::map<std::string, SMESH_Group*>::iterator name_group = mapOfJunctionGroups.begin();
11564 for ( ; name_group != mapOfJunctionGroups.end(); ++name_group )
11566 if ( name_group->second && name_group->second->GetGroupDS()->IsEmpty() )
11567 myMesh->RemoveGroup( name_group->second->GetGroupDS()->GetID() );
11570 meshDS->CleanDownWardConnectivity(); // Mesh has been modified, downward connectivity is no more usable, free memory
11571 grid->BuildLinks();
11579 * \brief Double nodes on some external faces and create flat elements.
11580 * Flat elements are mainly used by some types of mechanic calculations.
11582 * Each group of the list must be constituted of faces.
11583 * Triangles are transformed in prisms, and quadrangles in hexahedrons.
11584 * @param theElems - list of groups of faces, where a group of faces is a set of
11585 * SMDS_MeshElements sorted by Id.
11586 * @return TRUE if operation has been completed successfully, FALSE otherwise
11588 bool SMESH_MeshEditor::CreateFlatElementsOnFacesGroups(const std::vector<TIDSortedElemSet>& theElems)
11590 MESSAGE("-------------------------------------------------");
11591 MESSAGE("SMESH_MeshEditor::CreateFlatElementsOnFacesGroups");
11592 MESSAGE("-------------------------------------------------");
11594 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11596 // --- For each group of faces
11597 // duplicate the nodes, create a flat element based on the face
11598 // replace the nodes of the faces by their clones
11600 std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> clonedNodes;
11601 std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> intermediateNodes;
11602 clonedNodes.clear();
11603 intermediateNodes.clear();
11604 std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11605 mapOfJunctionGroups.clear();
11607 for (int idom = 0; idom < theElems.size(); idom++)
11609 const TIDSortedElemSet& domain = theElems[idom];
11610 TIDSortedElemSet::const_iterator elemItr = domain.begin();
11611 for (; elemItr != domain.end(); ++elemItr)
11613 SMDS_MeshElement* anElem = (SMDS_MeshElement*) *elemItr;
11614 SMDS_MeshFace* aFace = dynamic_cast<SMDS_MeshFace*> (anElem);
11617 // MESSAGE("aFace=" << aFace->GetID());
11618 bool isQuad = aFace->IsQuadratic();
11619 vector<const SMDS_MeshNode*> ln0, ln1, ln2, ln3, ln4;
11621 // --- clone the nodes, create intermediate nodes for non medium nodes of a quad face
11623 SMDS_ElemIteratorPtr nodeIt = aFace->nodesIterator();
11624 while (nodeIt->more())
11626 const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*> (nodeIt->next());
11627 bool isMedium = isQuad && (aFace->IsMediumNode(node));
11629 ln2.push_back(node);
11631 ln0.push_back(node);
11633 const SMDS_MeshNode* clone = 0;
11634 if (!clonedNodes.count(node))
11636 clone = meshDS->AddNode(node->X(), node->Y(), node->Z());
11637 copyPosition( node, clone );
11638 clonedNodes[node] = clone;
11641 clone = clonedNodes[node];
11644 ln3.push_back(clone);
11646 ln1.push_back(clone);
11648 const SMDS_MeshNode* inter = 0;
11649 if (isQuad && (!isMedium))
11651 if (!intermediateNodes.count(node))
11653 inter = meshDS->AddNode(node->X(), node->Y(), node->Z());
11654 copyPosition( node, inter );
11655 intermediateNodes[node] = inter;
11658 inter = intermediateNodes[node];
11659 ln4.push_back(inter);
11663 // --- extrude the face
11665 vector<const SMDS_MeshNode*> ln;
11666 SMDS_MeshVolume* vol = 0;
11667 vtkIdType aType = aFace->GetVtkType();
11671 vol = meshDS->AddVolume(ln0[2], ln0[1], ln0[0], ln1[2], ln1[1], ln1[0]);
11672 // MESSAGE("vol prism " << vol->GetID());
11673 ln.push_back(ln1[0]);
11674 ln.push_back(ln1[1]);
11675 ln.push_back(ln1[2]);
11678 vol = meshDS->AddVolume(ln0[3], ln0[2], ln0[1], ln0[0], ln1[3], ln1[2], ln1[1], ln1[0]);
11679 // MESSAGE("vol hexa " << vol->GetID());
11680 ln.push_back(ln1[0]);
11681 ln.push_back(ln1[1]);
11682 ln.push_back(ln1[2]);
11683 ln.push_back(ln1[3]);
11685 case VTK_QUADRATIC_TRIANGLE:
11686 vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln0[0], ln0[1], ln0[2], ln3[0], ln3[1], ln3[2],
11687 ln2[0], ln2[1], ln2[2], ln4[0], ln4[1], ln4[2]);
11688 // MESSAGE("vol quad prism " << vol->GetID());
11689 ln.push_back(ln1[0]);
11690 ln.push_back(ln1[1]);
11691 ln.push_back(ln1[2]);
11692 ln.push_back(ln3[0]);
11693 ln.push_back(ln3[1]);
11694 ln.push_back(ln3[2]);
11696 case VTK_QUADRATIC_QUAD:
11697 // vol = meshDS->AddVolume(ln0[0], ln0[1], ln0[2], ln0[3], ln1[0], ln1[1], ln1[2], ln1[3],
11698 // ln2[0], ln2[1], ln2[2], ln2[3], ln3[0], ln3[1], ln3[2], ln3[3],
11699 // ln4[0], ln4[1], ln4[2], ln4[3]);
11700 vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln1[3], ln0[0], ln0[1], ln0[2], ln0[3],
11701 ln3[0], ln3[1], ln3[2], ln3[3], ln2[0], ln2[1], ln2[2], ln2[3],
11702 ln4[0], ln4[1], ln4[2], ln4[3]);
11703 // MESSAGE("vol quad hexa " << vol->GetID());
11704 ln.push_back(ln1[0]);
11705 ln.push_back(ln1[1]);
11706 ln.push_back(ln1[2]);
11707 ln.push_back(ln1[3]);
11708 ln.push_back(ln3[0]);
11709 ln.push_back(ln3[1]);
11710 ln.push_back(ln3[2]);
11711 ln.push_back(ln3[3]);
11721 stringstream grpname;
11725 string namegrp = grpname.str();
11726 if (!mapOfJunctionGroups.count(namegrp))
11727 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
11728 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11730 sgrp->Add(vol->GetID());
11733 // --- modify the face
11735 aFace->ChangeNodes(&ln[0], ln.size());
11742 * \brief identify all the elements around a geom shape, get the faces delimiting the hole
11743 * Build groups of volume to remove, groups of faces to replace on the skin of the object,
11744 * groups of faces to remove inside the object, (idem edges).
11745 * Build ordered list of nodes at the border of each group of faces to replace (to be used to build a geom subshape)
11747 void SMESH_MeshEditor::CreateHoleSkin(double radius,
11748 const TopoDS_Shape& theShape,
11749 SMESH_NodeSearcher* theNodeSearcher,
11750 const char* groupName,
11751 std::vector<double>& nodesCoords,
11752 std::vector<std::vector<int> >& listOfListOfNodes)
11754 MESSAGE("--------------------------------");
11755 MESSAGE("SMESH_MeshEditor::CreateHoleSkin");
11756 MESSAGE("--------------------------------");
11758 // --- zone of volumes to remove is given :
11759 // 1 either by a geom shape (one or more vertices) and a radius,
11760 // 2 either by a group of nodes (representative of the shape)to use with the radius,
11761 // 3 either by a group of nodes where all the elements build on one of this nodes are to remove,
11762 // In the case 2, the group of nodes is an external group of nodes from another mesh,
11763 // In the case 3, the group of nodes is an internal group of the mesh (obtained for instance by a filter),
11764 // defined by it's name.
11766 SMESHDS_GroupBase* groupDS = 0;
11767 SMESH_Mesh::GroupIteratorPtr groupIt = this->myMesh->GetGroups();
11768 while ( groupIt->more() )
11771 SMESH_Group * group = groupIt->next();
11772 if ( !group ) continue;
11773 groupDS = group->GetGroupDS();
11774 if ( !groupDS || groupDS->IsEmpty() ) continue;
11775 std::string grpName = group->GetName();
11776 //MESSAGE("grpName=" << grpName);
11777 if (grpName == groupName)
11783 bool isNodeGroup = false;
11784 bool isNodeCoords = false;
11787 if (groupDS->GetType() != SMDSAbs_Node)
11789 isNodeGroup = true; // a group of nodes exists and it is in this mesh
11792 if (nodesCoords.size() > 0)
11793 isNodeCoords = true; // a list o nodes given by their coordinates
11794 //MESSAGE("---" << isNodeGroup << " " << isNodeCoords);
11796 // --- define groups to build
11798 int idg; // --- group of SMDS volumes
11799 string grpvName = groupName;
11800 grpvName += "_vol";
11801 SMESH_Group *grp = this->myMesh->AddGroup(SMDSAbs_Volume, grpvName.c_str(), idg);
11804 MESSAGE("group not created " << grpvName);
11807 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(grp->GetGroupDS());
11809 int idgs; // --- group of SMDS faces on the skin
11810 string grpsName = groupName;
11811 grpsName += "_skin";
11812 SMESH_Group *grps = this->myMesh->AddGroup(SMDSAbs_Face, grpsName.c_str(), idgs);
11815 MESSAGE("group not created " << grpsName);
11818 SMESHDS_Group *sgrps = dynamic_cast<SMESHDS_Group*>(grps->GetGroupDS());
11820 int idgi; // --- group of SMDS faces internal (several shapes)
11821 string grpiName = groupName;
11822 grpiName += "_internalFaces";
11823 SMESH_Group *grpi = this->myMesh->AddGroup(SMDSAbs_Face, grpiName.c_str(), idgi);
11826 MESSAGE("group not created " << grpiName);
11829 SMESHDS_Group *sgrpi = dynamic_cast<SMESHDS_Group*>(grpi->GetGroupDS());
11831 int idgei; // --- group of SMDS faces internal (several shapes)
11832 string grpeiName = groupName;
11833 grpeiName += "_internalEdges";
11834 SMESH_Group *grpei = this->myMesh->AddGroup(SMDSAbs_Edge, grpeiName.c_str(), idgei);
11837 MESSAGE("group not created " << grpeiName);
11840 SMESHDS_Group *sgrpei = dynamic_cast<SMESHDS_Group*>(grpei->GetGroupDS());
11842 // --- build downward connectivity
11844 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11845 meshDS->BuildDownWardConnectivity(true);
11846 SMDS_UnstructuredGrid* grid = meshDS->getGrid();
11848 // --- set of volumes detected inside
11850 std::set<int> setOfInsideVol;
11851 std::set<int> setOfVolToCheck;
11853 std::vector<gp_Pnt> gpnts;
11856 if (isNodeGroup) // --- a group of nodes is provided : find all the volumes using one or more of this nodes
11858 MESSAGE("group of nodes provided");
11859 SMDS_ElemIteratorPtr elemIt = groupDS->GetElements();
11860 while ( elemIt->more() )
11862 const SMDS_MeshElement* elem = elemIt->next();
11865 const SMDS_MeshNode* node = dynamic_cast<const SMDS_MeshNode*>(elem);
11868 SMDS_MeshElement* vol = 0;
11869 SMDS_ElemIteratorPtr volItr = node->GetInverseElementIterator(SMDSAbs_Volume);
11870 while (volItr->more())
11872 vol = (SMDS_MeshElement*)volItr->next();
11873 setOfInsideVol.insert(vol->getVtkId());
11874 sgrp->Add(vol->GetID());
11878 else if (isNodeCoords)
11880 MESSAGE("list of nodes coordinates provided");
11883 while (i < nodesCoords.size()-2)
11885 double x = nodesCoords[i++];
11886 double y = nodesCoords[i++];
11887 double z = nodesCoords[i++];
11888 gp_Pnt p = gp_Pnt(x, y ,z);
11889 gpnts.push_back(p);
11890 MESSAGE("TopoDS_Vertex " << k << " " << p.X() << " " << p.Y() << " " << p.Z());
11894 else // --- no group, no coordinates : use the vertices of the geom shape provided, and radius
11896 MESSAGE("no group of nodes provided, using vertices from geom shape, and radius");
11897 TopTools_IndexedMapOfShape vertexMap;
11898 TopExp::MapShapes( theShape, TopAbs_VERTEX, vertexMap );
11899 gp_Pnt p = gp_Pnt(0,0,0);
11900 if (vertexMap.Extent() < 1)
11903 for ( int i = 1; i <= vertexMap.Extent(); ++i )
11905 const TopoDS_Vertex& vertex = TopoDS::Vertex( vertexMap( i ));
11906 p = BRep_Tool::Pnt(vertex);
11907 gpnts.push_back(p);
11908 MESSAGE("TopoDS_Vertex " << i << " " << p.X() << " " << p.Y() << " " << p.Z());
11912 if (gpnts.size() > 0)
11915 const SMDS_MeshNode* startNode = theNodeSearcher->FindClosestTo(gpnts[0]);
11917 nodeId = startNode->GetID();
11918 MESSAGE("nodeId " << nodeId);
11920 double radius2 = radius*radius;
11921 MESSAGE("radius2 " << radius2);
11923 // --- volumes on start node
11925 setOfVolToCheck.clear();
11926 SMDS_MeshElement* startVol = 0;
11927 SMDS_ElemIteratorPtr volItr = startNode->GetInverseElementIterator(SMDSAbs_Volume);
11928 while (volItr->more())
11930 startVol = (SMDS_MeshElement*)volItr->next();
11931 setOfVolToCheck.insert(startVol->getVtkId());
11933 if (setOfVolToCheck.empty())
11935 MESSAGE("No volumes found");
11939 // --- starting with central volumes then their neighbors, check if they are inside
11940 // or outside the domain, until no more new neighbor volume is inside.
11941 // Fill the group of inside volumes
11943 std::map<int, double> mapOfNodeDistance2;
11944 mapOfNodeDistance2.clear();
11945 std::set<int> setOfOutsideVol;
11946 while (!setOfVolToCheck.empty())
11948 std::set<int>::iterator it = setOfVolToCheck.begin();
11950 MESSAGE("volume to check, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
11951 bool volInside = false;
11952 vtkIdType npts = 0;
11953 vtkIdType* pts = 0;
11954 grid->GetCellPoints(vtkId, npts, pts);
11955 for (int i=0; i<npts; i++)
11957 double distance2 = 0;
11958 if (mapOfNodeDistance2.count(pts[i]))
11960 distance2 = mapOfNodeDistance2[pts[i]];
11961 MESSAGE("point " << pts[i] << " distance2 " << distance2);
11965 double *coords = grid->GetPoint(pts[i]);
11966 gp_Pnt aPoint = gp_Pnt(coords[0], coords[1], coords[2]);
11968 for (int j=0; j<gpnts.size(); j++)
11970 double d2 = aPoint.SquareDistance(gpnts[j]);
11971 if (d2 < distance2)
11974 if (distance2 < radius2)
11978 mapOfNodeDistance2[pts[i]] = distance2;
11979 MESSAGE(" point " << pts[i] << " distance2 " << distance2 << " coords " << coords[0] << " " << coords[1] << " " << coords[2]);
11981 if (distance2 < radius2)
11983 volInside = true; // one or more nodes inside the domain
11984 sgrp->Add(meshDS->fromVtkToSmds(vtkId));
11990 setOfInsideVol.insert(vtkId);
11991 MESSAGE(" volume inside, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
11992 int neighborsVtkIds[NBMAXNEIGHBORS];
11993 int downIds[NBMAXNEIGHBORS];
11994 unsigned char downTypes[NBMAXNEIGHBORS];
11995 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
11996 for (int n = 0; n < nbNeighbors; n++)
11997 if (!setOfInsideVol.count(neighborsVtkIds[n]) ||setOfOutsideVol.count(neighborsVtkIds[n]))
11998 setOfVolToCheck.insert(neighborsVtkIds[n]);
12002 setOfOutsideVol.insert(vtkId);
12003 MESSAGE(" volume outside, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12005 setOfVolToCheck.erase(vtkId);
12009 // --- for outside hexahedrons, check if they have more than one neighbor volume inside
12010 // If yes, add the volume to the inside set
12012 bool addedInside = true;
12013 std::set<int> setOfVolToReCheck;
12014 while (addedInside)
12016 MESSAGE(" --------------------------- re check");
12017 addedInside = false;
12018 std::set<int>::iterator itv = setOfInsideVol.begin();
12019 for (; itv != setOfInsideVol.end(); ++itv)
12022 int neighborsVtkIds[NBMAXNEIGHBORS];
12023 int downIds[NBMAXNEIGHBORS];
12024 unsigned char downTypes[NBMAXNEIGHBORS];
12025 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12026 for (int n = 0; n < nbNeighbors; n++)
12027 if (!setOfInsideVol.count(neighborsVtkIds[n]))
12028 setOfVolToReCheck.insert(neighborsVtkIds[n]);
12030 setOfVolToCheck = setOfVolToReCheck;
12031 setOfVolToReCheck.clear();
12032 while (!setOfVolToCheck.empty())
12034 std::set<int>::iterator it = setOfVolToCheck.begin();
12036 if (grid->GetCellType(vtkId) == VTK_HEXAHEDRON)
12038 MESSAGE("volume to recheck, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12039 int countInside = 0;
12040 int neighborsVtkIds[NBMAXNEIGHBORS];
12041 int downIds[NBMAXNEIGHBORS];
12042 unsigned char downTypes[NBMAXNEIGHBORS];
12043 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12044 for (int n = 0; n < nbNeighbors; n++)
12045 if (setOfInsideVol.count(neighborsVtkIds[n]))
12047 MESSAGE("countInside " << countInside);
12048 if (countInside > 1)
12050 MESSAGE(" volume inside, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12051 setOfInsideVol.insert(vtkId);
12052 sgrp->Add(meshDS->fromVtkToSmds(vtkId));
12053 addedInside = true;
12056 setOfVolToReCheck.insert(vtkId);
12058 setOfVolToCheck.erase(vtkId);
12062 // --- map of Downward faces at the boundary, inside the global volume
12063 // map of Downward faces on the skin of the global volume (equivalent to SMDS faces on the skin)
12064 // fill group of SMDS faces inside the volume (when several volume shapes)
12065 // fill group of SMDS faces on the skin of the global volume (if skin)
12067 std::map<DownIdType, int, DownIdCompare> boundaryFaces; // boundary faces inside the volume --> corresponding cell
12068 std::map<DownIdType, int, DownIdCompare> skinFaces; // faces on the skin of the global volume --> corresponding cell
12069 std::set<int>::iterator it = setOfInsideVol.begin();
12070 for (; it != setOfInsideVol.end(); ++it)
12073 //MESSAGE(" vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12074 int neighborsVtkIds[NBMAXNEIGHBORS];
12075 int downIds[NBMAXNEIGHBORS];
12076 unsigned char downTypes[NBMAXNEIGHBORS];
12077 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId, true);
12078 for (int n = 0; n < nbNeighbors; n++)
12080 int neighborDim = SMDS_Downward::getCellDimension(grid->GetCellType(neighborsVtkIds[n]));
12081 if (neighborDim == 3)
12083 if (! setOfInsideVol.count(neighborsVtkIds[n])) // neighbor volume is not inside : face is boundary
12085 DownIdType face(downIds[n], downTypes[n]);
12086 boundaryFaces[face] = vtkId;
12088 // if the face between to volumes is in the mesh, get it (internal face between shapes)
12089 int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12090 if (vtkFaceId >= 0)
12092 sgrpi->Add(meshDS->fromVtkToSmds(vtkFaceId));
12093 // find also the smds edges on this face
12094 int nbEdges = grid->getDownArray(downTypes[n])->getNumberOfDownCells(downIds[n]);
12095 const int* dEdges = grid->getDownArray(downTypes[n])->getDownCells(downIds[n]);
12096 const unsigned char* dTypes = grid->getDownArray(downTypes[n])->getDownTypes(downIds[n]);
12097 for (int i = 0; i < nbEdges; i++)
12099 int vtkEdgeId = grid->getDownArray(dTypes[i])->getVtkCellId(dEdges[i]);
12100 if (vtkEdgeId >= 0)
12101 sgrpei->Add(meshDS->fromVtkToSmds(vtkEdgeId));
12105 else if (neighborDim == 2) // skin of the volume
12107 DownIdType face(downIds[n], downTypes[n]);
12108 skinFaces[face] = vtkId;
12109 int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12110 if (vtkFaceId >= 0)
12111 sgrps->Add(meshDS->fromVtkToSmds(vtkFaceId));
12116 // --- identify the edges constituting the wire of each subshape on the skin
12117 // define polylines with the nodes of edges, equivalent to wires
12118 // project polylines on subshapes, and partition, to get geom faces
12120 std::map<int, std::set<int> > shapeIdToVtkIdSet; // shapeId --> set of vtkId on skin
12121 std::set<int> emptySet;
12123 std::set<int> shapeIds;
12125 SMDS_ElemIteratorPtr itelem = sgrps->GetElements();
12126 while (itelem->more())
12128 const SMDS_MeshElement *elem = itelem->next();
12129 int shapeId = elem->getshapeId();
12130 int vtkId = elem->getVtkId();
12131 if (!shapeIdToVtkIdSet.count(shapeId))
12133 shapeIdToVtkIdSet[shapeId] = emptySet;
12134 shapeIds.insert(shapeId);
12136 shapeIdToVtkIdSet[shapeId].insert(vtkId);
12139 std::map<int, std::set<DownIdType, DownIdCompare> > shapeIdToEdges; // shapeId --> set of downward edges
12140 std::set<DownIdType, DownIdCompare> emptyEdges;
12141 emptyEdges.clear();
12143 std::map<int, std::set<int> >::iterator itShape = shapeIdToVtkIdSet.begin();
12144 for (; itShape != shapeIdToVtkIdSet.end(); ++itShape)
12146 int shapeId = itShape->first;
12147 MESSAGE(" --- Shape ID --- "<< shapeId);
12148 shapeIdToEdges[shapeId] = emptyEdges;
12150 std::vector<int> nodesEdges;
12152 std::set<int>::iterator its = itShape->second.begin();
12153 for (; its != itShape->second.end(); ++its)
12156 MESSAGE(" " << vtkId);
12157 int neighborsVtkIds[NBMAXNEIGHBORS];
12158 int downIds[NBMAXNEIGHBORS];
12159 unsigned char downTypes[NBMAXNEIGHBORS];
12160 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12161 for (int n = 0; n < nbNeighbors; n++)
12163 if (neighborsVtkIds[n]<0) // only smds faces are considered as neighbors here
12165 int smdsId = meshDS->fromVtkToSmds(neighborsVtkIds[n]);
12166 const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
12167 if ( shapeIds.count(elem->getshapeId()) && !sgrps->Contains(elem)) // edge : neighbor in the set of shape, not in the group
12169 DownIdType edge(downIds[n], downTypes[n]);
12170 if (!shapeIdToEdges[shapeId].count(edge))
12172 shapeIdToEdges[shapeId].insert(edge);
12174 int nbNodes = grid->getDownArray(downTypes[n])->getNodes(downIds[n],vtkNodeId);
12175 nodesEdges.push_back(vtkNodeId[0]);
12176 nodesEdges.push_back(vtkNodeId[nbNodes-1]);
12177 MESSAGE(" --- nodes " << vtkNodeId[0]+1 << " " << vtkNodeId[nbNodes-1]+1);
12183 std::list<int> order;
12185 if (nodesEdges.size() > 0)
12187 order.push_back(nodesEdges[0]); MESSAGE(" --- back " << order.back()+1); // SMDS id = VTK id + 1;
12188 nodesEdges[0] = -1;
12189 order.push_back(nodesEdges[1]); MESSAGE(" --- back " << order.back()+1);
12190 nodesEdges[1] = -1; // do not reuse this edge
12194 int nodeTofind = order.back(); // try first to push back
12196 for (i = 0; i<nodesEdges.size(); i++)
12197 if (nodesEdges[i] == nodeTofind)
12199 if (i == nodesEdges.size())
12200 found = false; // no follower found on back
12203 if (i%2) // odd ==> use the previous one
12204 if (nodesEdges[i-1] < 0)
12208 order.push_back(nodesEdges[i-1]); MESSAGE(" --- back " << order.back()+1);
12209 nodesEdges[i-1] = -1;
12211 else // even ==> use the next one
12212 if (nodesEdges[i+1] < 0)
12216 order.push_back(nodesEdges[i+1]); MESSAGE(" --- back " << order.back()+1);
12217 nodesEdges[i+1] = -1;
12222 // try to push front
12224 nodeTofind = order.front(); // try to push front
12225 for (i = 0; i<nodesEdges.size(); i++)
12226 if (nodesEdges[i] == nodeTofind)
12228 if (i == nodesEdges.size())
12230 found = false; // no predecessor found on front
12233 if (i%2) // odd ==> use the previous one
12234 if (nodesEdges[i-1] < 0)
12238 order.push_front(nodesEdges[i-1]); MESSAGE(" --- front " << order.front()+1);
12239 nodesEdges[i-1] = -1;
12241 else // even ==> use the next one
12242 if (nodesEdges[i+1] < 0)
12246 order.push_front(nodesEdges[i+1]); MESSAGE(" --- front " << order.front()+1);
12247 nodesEdges[i+1] = -1;
12253 std::vector<int> nodes;
12254 nodes.push_back(shapeId);
12255 std::list<int>::iterator itl = order.begin();
12256 for (; itl != order.end(); itl++)
12258 nodes.push_back((*itl) + 1); // SMDS id = VTK id + 1;
12259 MESSAGE(" ordered node " << nodes[nodes.size()-1]);
12261 listOfListOfNodes.push_back(nodes);
12264 // partition geom faces with blocFissure
12265 // mesh blocFissure and geom faces of the skin (external wires given, triangle algo to choose)
12266 // mesh volume around blocFissure (skin triangles and quadrangle given, tetra algo to choose)
12272 //================================================================================
12274 * \brief Generates skin mesh (containing 2D cells) from 3D mesh
12275 * The created 2D mesh elements based on nodes of free faces of boundary volumes
12276 * \return TRUE if operation has been completed successfully, FALSE otherwise
12278 //================================================================================
12280 bool SMESH_MeshEditor::Make2DMeshFrom3D()
12282 // iterates on volume elements and detect all free faces on them
12283 SMESHDS_Mesh* aMesh = GetMeshDS();
12286 //bool res = false;
12287 int nbFree = 0, nbExisted = 0, nbCreated = 0;
12288 SMDS_VolumeIteratorPtr vIt = aMesh->volumesIterator();
12291 const SMDS_MeshVolume* volume = vIt->next();
12292 SMDS_VolumeTool vTool( volume, /*ignoreCentralNodes=*/false );
12293 vTool.SetExternalNormal();
12294 //const bool isPoly = volume->IsPoly();
12295 const int iQuad = volume->IsQuadratic();
12296 for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12298 if (!vTool.IsFreeFace(iface))
12301 vector<const SMDS_MeshNode *> nodes;
12302 int nbFaceNodes = vTool.NbFaceNodes(iface);
12303 const SMDS_MeshNode** faceNodes = vTool.GetFaceNodes(iface);
12305 for ( ; inode < nbFaceNodes; inode += iQuad+1)
12306 nodes.push_back(faceNodes[inode]);
12307 if (iQuad) { // add medium nodes
12308 for ( inode = 1; inode < nbFaceNodes; inode += 2)
12309 nodes.push_back(faceNodes[inode]);
12310 if ( nbFaceNodes == 9 ) // bi-quadratic quad
12311 nodes.push_back(faceNodes[8]);
12313 // add new face based on volume nodes
12314 if (aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false) ) {
12316 continue; // face already exsist
12318 AddElement(nodes, SMDSAbs_Face, ( !iQuad && nbFaceNodes/(iQuad+1) > 4 ));
12322 return ( nbFree==(nbExisted+nbCreated) );
12327 inline const SMDS_MeshNode* getNodeWithSameID(SMESHDS_Mesh* mesh, const SMDS_MeshNode* node)
12329 if ( const SMDS_MeshNode* n = mesh->FindNode( node->GetID() ))
12331 return mesh->AddNodeWithID( node->X(),node->Y(),node->Z(), node->GetID() );
12334 //================================================================================
12336 * \brief Creates missing boundary elements
12337 * \param elements - elements whose boundary is to be checked
12338 * \param dimension - defines type of boundary elements to create
12339 * \param group - a group to store created boundary elements in
12340 * \param targetMesh - a mesh to store created boundary elements in
12341 * \param toCopyElements - if true, the checked elements will be copied into the targetMesh
12342 * \param toCopyExistingBoundary - if true, not only new but also pre-existing
12343 * boundary elements will be copied into the targetMesh
12344 * \param toAddExistingBondary - if true, not only new but also pre-existing
12345 * boundary elements will be added into the new group
12346 * \param aroundElements - if true, elements will be created on boundary of given
12347 * elements else, on boundary of the whole mesh.
12348 * \return nb of added boundary elements
12350 //================================================================================
12352 int SMESH_MeshEditor::MakeBoundaryMesh(const TIDSortedElemSet& elements,
12353 Bnd_Dimension dimension,
12354 SMESH_Group* group/*=0*/,
12355 SMESH_Mesh* targetMesh/*=0*/,
12356 bool toCopyElements/*=false*/,
12357 bool toCopyExistingBoundary/*=false*/,
12358 bool toAddExistingBondary/*= false*/,
12359 bool aroundElements/*= false*/)
12361 SMDSAbs_ElementType missType = (dimension == BND_2DFROM3D) ? SMDSAbs_Face : SMDSAbs_Edge;
12362 SMDSAbs_ElementType elemType = (dimension == BND_1DFROM2D) ? SMDSAbs_Face : SMDSAbs_Volume;
12363 // hope that all elements are of the same type, do not check them all
12364 if ( !elements.empty() && (*elements.begin())->GetType() != elemType )
12365 throw SALOME_Exception(LOCALIZED("wrong element type"));
12368 toCopyElements = toCopyExistingBoundary = false;
12370 SMESH_MeshEditor tgtEditor( targetMesh ? targetMesh : myMesh );
12371 SMESHDS_Mesh* aMesh = GetMeshDS(), *tgtMeshDS = tgtEditor.GetMeshDS();
12372 int nbAddedBnd = 0;
12374 // editor adding present bnd elements and optionally holding elements to add to the group
12375 SMESH_MeshEditor* presentEditor;
12376 SMESH_MeshEditor tgtEditor2( tgtEditor.GetMesh() );
12377 presentEditor = toAddExistingBondary ? &tgtEditor : &tgtEditor2;
12379 SMESH_MesherHelper helper( *myMesh );
12380 const TopAbs_ShapeEnum missShapeType = ( missType==SMDSAbs_Face ? TopAbs_FACE : TopAbs_EDGE );
12381 SMDS_VolumeTool vTool;
12382 TIDSortedElemSet avoidSet;
12383 const TIDSortedElemSet emptySet, *elemSet = aroundElements ? &elements : &emptySet;
12386 typedef vector<const SMDS_MeshNode*> TConnectivity;
12388 SMDS_ElemIteratorPtr eIt;
12389 if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
12390 else eIt = elemSetIterator( elements );
12392 while (eIt->more())
12394 const SMDS_MeshElement* elem = eIt->next();
12395 const int iQuad = elem->IsQuadratic();
12397 // ------------------------------------------------------------------------------------
12398 // 1. For an elem, get present bnd elements and connectivities of missing bnd elements
12399 // ------------------------------------------------------------------------------------
12400 vector<const SMDS_MeshElement*> presentBndElems;
12401 vector<TConnectivity> missingBndElems;
12402 TConnectivity nodes, elemNodes;
12403 if ( vTool.Set(elem, /*ignoreCentralNodes=*/true) ) // elem is a volume --------------
12405 vTool.SetExternalNormal();
12406 const SMDS_MeshElement* otherVol = 0;
12407 for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12409 if ( !vTool.IsFreeFace(iface, &otherVol) &&
12410 ( !aroundElements || elements.count( otherVol )))
12412 const SMDS_MeshNode** nn = vTool.GetFaceNodes(iface);
12413 const int nbFaceNodes = vTool.NbFaceNodes (iface);
12414 if ( missType == SMDSAbs_Edge ) // boundary edges
12416 nodes.resize( 2+iQuad );
12417 for ( int i = 0; i < nbFaceNodes; i += 1+iQuad)
12419 for ( int j = 0; j < nodes.size(); ++j )
12421 if ( const SMDS_MeshElement* edge =
12422 aMesh->FindElement(nodes,SMDSAbs_Edge,/*noMedium=*/false))
12423 presentBndElems.push_back( edge );
12425 missingBndElems.push_back( nodes );
12428 else // boundary face
12431 for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12432 nodes.push_back( nn[inode] ); // add corner nodes
12434 for ( inode = 1; inode < nbFaceNodes; inode += 2)
12435 nodes.push_back( nn[inode] ); // add medium nodes
12436 int iCenter = vTool.GetCenterNodeIndex(iface); // for HEX27
12438 nodes.push_back( vTool.GetNodes()[ iCenter ] );
12440 if (const SMDS_MeshElement * f = aMesh->FindElement( nodes,
12441 SMDSAbs_Face, /*noMedium=*/false ))
12442 presentBndElems.push_back( f );
12444 missingBndElems.push_back( nodes );
12446 if ( targetMesh != myMesh )
12448 // add 1D elements on face boundary to be added to a new mesh
12449 const SMDS_MeshElement* edge;
12450 for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12453 edge = aMesh->FindEdge( nn[inode], nn[inode+1], nn[inode+2]);
12455 edge = aMesh->FindEdge( nn[inode], nn[inode+1]);
12456 if ( edge && avoidSet.insert( edge ).second )
12457 presentBndElems.push_back( edge );
12463 else if ( elem->GetType() == SMDSAbs_Face ) // elem is a face ------------------------
12465 avoidSet.clear(), avoidSet.insert( elem );
12466 elemNodes.assign( SMDS_MeshElement::iterator( elem->interlacedNodesElemIterator() ),
12467 SMDS_MeshElement::iterator() );
12468 elemNodes.push_back( elemNodes[0] );
12469 nodes.resize( 2 + iQuad );
12470 const int nbLinks = elem->NbCornerNodes();
12471 for ( int i = 0, iN = 0; i < nbLinks; i++, iN += 1+iQuad )
12473 nodes[0] = elemNodes[iN];
12474 nodes[1] = elemNodes[iN+1+iQuad];
12475 if ( SMESH_MeshAlgos::FindFaceInSet( nodes[0], nodes[1], *elemSet, avoidSet))
12476 continue; // not free link
12478 if ( iQuad ) nodes[2] = elemNodes[iN+1];
12479 if ( const SMDS_MeshElement* edge =
12480 aMesh->FindElement(nodes,SMDSAbs_Edge,/*noMedium=*/false))
12481 presentBndElems.push_back( edge );
12483 missingBndElems.push_back( nodes );
12487 // ---------------------------------
12488 // 2. Add missing boundary elements
12489 // ---------------------------------
12490 if ( targetMesh != myMesh )
12491 // instead of making a map of nodes in this mesh and targetMesh,
12492 // we create nodes with same IDs.
12493 for ( int i = 0; i < missingBndElems.size(); ++i )
12495 TConnectivity& srcNodes = missingBndElems[i];
12496 TConnectivity nodes( srcNodes.size() );
12497 for ( inode = 0; inode < nodes.size(); ++inode )
12498 nodes[inode] = getNodeWithSameID( tgtMeshDS, srcNodes[inode] );
12499 if ( aroundElements && tgtEditor.GetMeshDS()->FindElement( nodes,
12501 /*noMedium=*/false))
12503 tgtEditor.AddElement(nodes, missType, !iQuad && nodes.size()/(iQuad+1)>4);
12507 for ( int i = 0; i < missingBndElems.size(); ++i )
12509 TConnectivity& nodes = missingBndElems[i];
12510 if ( aroundElements && tgtEditor.GetMeshDS()->FindElement( nodes,
12512 /*noMedium=*/false))
12514 SMDS_MeshElement* elem =
12515 tgtEditor.AddElement(nodes, missType, !iQuad && nodes.size()/(iQuad+1)>4);
12518 // try to set a new element to a shape
12519 if ( myMesh->HasShapeToMesh() )
12522 set< pair<TopAbs_ShapeEnum, int > > mediumShapes;
12523 const int nbN = nodes.size() / (iQuad+1 );
12524 for ( inode = 0; inode < nbN && ok; ++inode )
12526 pair<int, TopAbs_ShapeEnum> i_stype =
12527 helper.GetMediumPos( nodes[inode], nodes[(inode+1)%nbN]);
12528 if (( ok = ( i_stype.first > 0 && i_stype.second >= TopAbs_FACE )))
12529 mediumShapes.insert( make_pair ( i_stype.second, i_stype.first ));
12531 if ( ok && mediumShapes.size() > 1 )
12533 set< pair<TopAbs_ShapeEnum, int > >::iterator stype_i = mediumShapes.begin();
12534 pair<TopAbs_ShapeEnum, int> stype_i_0 = *stype_i;
12535 for ( ++stype_i; stype_i != mediumShapes.end() && ok; ++stype_i )
12537 if (( ok = ( stype_i->first != stype_i_0.first )))
12538 ok = helper.IsSubShape( aMesh->IndexToShape( stype_i->second ),
12539 aMesh->IndexToShape( stype_i_0.second ));
12542 if ( ok && mediumShapes.begin()->first == missShapeType )
12543 aMesh->SetMeshElementOnShape( elem, mediumShapes.begin()->second );
12547 // ----------------------------------
12548 // 3. Copy present boundary elements
12549 // ----------------------------------
12550 if ( toCopyExistingBoundary )
12551 for ( int i = 0 ; i < presentBndElems.size(); ++i )
12553 const SMDS_MeshElement* e = presentBndElems[i];
12554 TConnectivity nodes( e->NbNodes() );
12555 for ( inode = 0; inode < nodes.size(); ++inode )
12556 nodes[inode] = getNodeWithSameID( tgtMeshDS, e->GetNode(inode) );
12557 presentEditor->AddElement(nodes, e->GetType(), e->IsPoly());
12559 else // store present elements to add them to a group
12560 for ( int i = 0 ; i < presentBndElems.size(); ++i )
12562 presentEditor->myLastCreatedElems.Append(presentBndElems[i]);
12565 } // loop on given elements
12567 // ---------------------------------------------
12568 // 4. Fill group with boundary elements
12569 // ---------------------------------------------
12572 if ( SMESHDS_Group* g = dynamic_cast<SMESHDS_Group*>( group->GetGroupDS() ))
12573 for ( int i = 0; i < tgtEditor.myLastCreatedElems.Size(); ++i )
12574 g->SMDSGroup().Add( tgtEditor.myLastCreatedElems( i+1 ));
12576 tgtEditor.myLastCreatedElems.Clear();
12577 tgtEditor2.myLastCreatedElems.Clear();
12579 // -----------------------
12580 // 5. Copy given elements
12581 // -----------------------
12582 if ( toCopyElements && targetMesh != myMesh )
12584 if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
12585 else eIt = elemSetIterator( elements );
12586 while (eIt->more())
12588 const SMDS_MeshElement* elem = eIt->next();
12589 TConnectivity nodes( elem->NbNodes() );
12590 for ( inode = 0; inode < nodes.size(); ++inode )
12591 nodes[inode] = getNodeWithSameID( tgtMeshDS, elem->GetNode(inode) );
12592 tgtEditor.AddElement(nodes, elemType, elem->IsPoly());
12594 tgtEditor.myLastCreatedElems.Clear();
12600 //================================================================================
12602 * \brief Copy node position and set \a to node on the same geometry
12604 //================================================================================
12606 void SMESH_MeshEditor::copyPosition( const SMDS_MeshNode* from,
12607 const SMDS_MeshNode* to )
12609 if ( !from || !to ) return;
12611 SMDS_PositionPtr pos = from->GetPosition();
12612 if ( !pos || from->getshapeId() < 1 ) return;
12614 switch ( pos->GetTypeOfPosition() )
12616 case SMDS_TOP_3DSPACE: break;
12618 case SMDS_TOP_FACE:
12620 const SMDS_FacePosition* fPos = static_cast< const SMDS_FacePosition* >( pos );
12621 GetMeshDS()->SetNodeOnFace( to, from->getshapeId(),
12622 fPos->GetUParameter(), fPos->GetVParameter() );
12625 case SMDS_TOP_EDGE:
12627 // WARNING: it is dangerous to set equal nodes on one EDGE!!!!!!!!
12628 const SMDS_EdgePosition* ePos = static_cast< const SMDS_EdgePosition* >( pos );
12629 GetMeshDS()->SetNodeOnEdge( to, from->getshapeId(), ePos->GetUParameter() );
12632 case SMDS_TOP_VERTEX:
12634 GetMeshDS()->SetNodeOnVertex( to, from->getshapeId() );
12637 case SMDS_TOP_UNSPEC: