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 SMESH_MesherHelper helper( *GetMesh() );
3773 set< int >::reverse_iterator fId = faceIdSet.rbegin(); // treat 0 fId at the end
3774 for ( ; fId != faceIdSet.rend(); ++fId )
3776 // get face surface and submesh
3777 Handle(Geom_Surface) surface;
3778 SMESHDS_SubMesh* faceSubMesh = 0;
3780 double fToler2 = 0, f,l;
3781 double u1 = 0, u2 = 0, v1 = 0, v2 = 0;
3782 bool isUPeriodic = false, isVPeriodic = false;
3785 face = TopoDS::Face( aMesh->IndexToShape( *fId ));
3786 surface = BRep_Tool::Surface( face );
3787 faceSubMesh = aMesh->MeshElements( *fId );
3788 fToler2 = BRep_Tool::Tolerance( face );
3789 fToler2 *= fToler2 * 10.;
3790 isUPeriodic = surface->IsUPeriodic();
3793 isVPeriodic = surface->IsVPeriodic();
3796 surface->Bounds( u1, u2, v1, v2 );
3797 helper.SetSubShape( face );
3799 // ---------------------------------------------------------
3800 // for elements on a face, find movable and fixed nodes and
3801 // compute UV for them
3802 // ---------------------------------------------------------
3803 bool checkBoundaryNodes = false;
3804 bool isQuadratic = false;
3805 set<const SMDS_MeshNode*> setMovableNodes;
3806 map< const SMDS_MeshNode*, gp_XY* > uvMap, uvMap2;
3807 list< gp_XY > listUV; // uvs the 2 uvMaps refer to
3808 list< const SMDS_MeshElement* > elemsOnFace;
3810 Extrema_GenExtPS projector;
3811 GeomAdaptor_Surface surfAdaptor;
3812 if ( !surface.IsNull() ) {
3813 surfAdaptor.Load( surface );
3814 projector.Initialize( surfAdaptor, 20,20, 1e-5,1e-5 );
3816 int nbElemOnFace = 0;
3817 itElem = theElems.begin();
3818 // loop on not yet smoothed elements: look for elems on a face
3819 while ( itElem != theElems.end() )
3821 if ( faceSubMesh && nbElemOnFace == faceSubMesh->NbElements() )
3822 break; // all elements found
3824 const SMDS_MeshElement* elem = *itElem;
3825 if ( !elem || elem->GetType() != SMDSAbs_Face || elem->NbNodes() < 3 ||
3826 ( faceSubMesh && !faceSubMesh->Contains( elem ))) {
3830 elemsOnFace.push_back( elem );
3831 theElems.erase( itElem++ );
3835 isQuadratic = elem->IsQuadratic();
3837 // get movable nodes of elem
3838 const SMDS_MeshNode* node;
3839 SMDS_TypeOfPosition posType;
3840 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3841 int nn = 0, nbn = elem->NbNodes();
3842 if(elem->IsQuadratic())
3844 while ( nn++ < nbn ) {
3845 node = static_cast<const SMDS_MeshNode*>( itN->next() );
3846 const SMDS_PositionPtr& pos = node->GetPosition();
3847 posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
3848 if (posType != SMDS_TOP_EDGE &&
3849 posType != SMDS_TOP_VERTEX &&
3850 theFixedNodes.find( node ) == theFixedNodes.end())
3852 // check if all faces around the node are on faceSubMesh
3853 // because a node on edge may be bound to face
3854 SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
3856 if ( faceSubMesh ) {
3857 while ( eIt->more() && all ) {
3858 const SMDS_MeshElement* e = eIt->next();
3859 all = faceSubMesh->Contains( e );
3863 setMovableNodes.insert( node );
3865 checkBoundaryNodes = true;
3867 if ( posType == SMDS_TOP_3DSPACE )
3868 checkBoundaryNodes = true;
3871 if ( surface.IsNull() )
3874 // get nodes to check UV
3875 list< const SMDS_MeshNode* > uvCheckNodes;
3876 const SMDS_MeshNode* nodeInFace = 0;
3877 itN = elem->nodesIterator();
3878 nn = 0; nbn = elem->NbNodes();
3879 if(elem->IsQuadratic())
3881 while ( nn++ < nbn ) {
3882 node = static_cast<const SMDS_MeshNode*>( itN->next() );
3883 if ( node->GetPosition()->GetDim() == 2 )
3885 if ( uvMap.find( node ) == uvMap.end() )
3886 uvCheckNodes.push_back( node );
3887 // add nodes of elems sharing node
3888 // SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
3889 // while ( eIt->more() ) {
3890 // const SMDS_MeshElement* e = eIt->next();
3891 // if ( e != elem ) {
3892 // SMDS_ElemIteratorPtr nIt = e->nodesIterator();
3893 // while ( nIt->more() ) {
3894 // const SMDS_MeshNode* n =
3895 // static_cast<const SMDS_MeshNode*>( nIt->next() );
3896 // if ( uvMap.find( n ) == uvMap.end() )
3897 // uvCheckNodes.push_back( n );
3903 list< const SMDS_MeshNode* >::iterator n = uvCheckNodes.begin();
3904 for ( ; n != uvCheckNodes.end(); ++n ) {
3907 const SMDS_PositionPtr& pos = node->GetPosition();
3908 posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
3912 bool toCheck = true;
3913 uv = helper.GetNodeUV( face, node, nodeInFace, &toCheck );
3915 // compute not existing UV
3916 bool project = ( posType == SMDS_TOP_3DSPACE );
3917 // double dist1 = DBL_MAX, dist2 = 0;
3918 // if ( posType != SMDS_TOP_3DSPACE ) {
3919 // dist1 = pNode.SquareDistance( surface->Value( uv.X(), uv.Y() ));
3920 // project = dist1 > fToler2;
3922 if ( project ) { // compute new UV
3924 gp_Pnt pNode = SMESH_TNodeXYZ( node );
3925 if ( !getClosestUV( projector, pNode, newUV )) {
3926 MESSAGE("Node Projection Failed " << node);
3930 newUV.SetX( ElCLib::InPeriod( newUV.X(), u1, u2 ));
3932 newUV.SetY( ElCLib::InPeriod( newUV.Y(), v1, v2 ));
3934 // if ( posType != SMDS_TOP_3DSPACE )
3935 // dist2 = pNode.SquareDistance( surface->Value( newUV.X(), newUV.Y() ));
3936 // if ( dist2 < dist1 )
3940 // store UV in the map
3941 listUV.push_back( uv );
3942 uvMap.insert( make_pair( node, &listUV.back() ));
3944 } // loop on not yet smoothed elements
3946 if ( !faceSubMesh || nbElemOnFace != faceSubMesh->NbElements() )
3947 checkBoundaryNodes = true;
3949 // fix nodes on mesh boundary
3951 if ( checkBoundaryNodes ) {
3952 map< SMESH_TLink, int > linkNbMap; // how many times a link encounters in elemsOnFace
3953 map< SMESH_TLink, int >::iterator link_nb;
3954 // put all elements links to linkNbMap
3955 list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
3956 for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
3957 const SMDS_MeshElement* elem = (*elemIt);
3958 int nbn = elem->NbCornerNodes();
3959 // loop on elem links: insert them in linkNbMap
3960 for ( int iN = 0; iN < nbn; ++iN ) {
3961 const SMDS_MeshNode* n1 = elem->GetNode( iN );
3962 const SMDS_MeshNode* n2 = elem->GetNode(( iN+1 ) % nbn);
3963 SMESH_TLink link( n1, n2 );
3964 link_nb = linkNbMap.insert( make_pair( link, 0 )).first;
3968 // remove nodes that are in links encountered only once from setMovableNodes
3969 for ( link_nb = linkNbMap.begin(); link_nb != linkNbMap.end(); ++link_nb ) {
3970 if ( link_nb->second == 1 ) {
3971 setMovableNodes.erase( link_nb->first.node1() );
3972 setMovableNodes.erase( link_nb->first.node2() );
3977 // -----------------------------------------------------
3978 // for nodes on seam edge, compute one more UV ( uvMap2 );
3979 // find movable nodes linked to nodes on seam and which
3980 // are to be smoothed using the second UV ( uvMap2 )
3981 // -----------------------------------------------------
3983 set<const SMDS_MeshNode*> nodesNearSeam; // to smooth using uvMap2
3984 if ( !surface.IsNull() ) {
3985 TopExp_Explorer eExp( face, TopAbs_EDGE );
3986 for ( ; eExp.More(); eExp.Next() ) {
3987 TopoDS_Edge edge = TopoDS::Edge( eExp.Current() );
3988 if ( !BRep_Tool::IsClosed( edge, face ))
3990 SMESHDS_SubMesh* sm = aMesh->MeshElements( edge );
3991 if ( !sm ) continue;
3992 // find out which parameter varies for a node on seam
3995 Handle(Geom2d_Curve) pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
3996 if ( pcurve.IsNull() ) continue;
3997 uv1 = pcurve->Value( f );
3999 pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
4000 if ( pcurve.IsNull() ) continue;
4001 uv2 = pcurve->Value( f );
4002 int iPar = Abs( uv1.X() - uv2.X() ) > Abs( uv1.Y() - uv2.Y() ) ? 1 : 2;
4004 if ( uv1.Coord( iPar ) > uv2.Coord( iPar ))
4005 std::swap( uv1, uv2 );
4006 // get nodes on seam and its vertices
4007 list< const SMDS_MeshNode* > seamNodes;
4008 SMDS_NodeIteratorPtr nSeamIt = sm->GetNodes();
4009 while ( nSeamIt->more() ) {
4010 const SMDS_MeshNode* node = nSeamIt->next();
4011 if ( !isQuadratic || !IsMedium( node ))
4012 seamNodes.push_back( node );
4014 TopExp_Explorer vExp( edge, TopAbs_VERTEX );
4015 for ( ; vExp.More(); vExp.Next() ) {
4016 sm = aMesh->MeshElements( vExp.Current() );
4018 nSeamIt = sm->GetNodes();
4019 while ( nSeamIt->more() )
4020 seamNodes.push_back( nSeamIt->next() );
4023 // loop on nodes on seam
4024 list< const SMDS_MeshNode* >::iterator noSeIt = seamNodes.begin();
4025 for ( ; noSeIt != seamNodes.end(); ++noSeIt ) {
4026 const SMDS_MeshNode* nSeam = *noSeIt;
4027 map< const SMDS_MeshNode*, gp_XY* >::iterator n_uv = uvMap.find( nSeam );
4028 if ( n_uv == uvMap.end() )
4031 n_uv->second->SetCoord( iPar, uv1.Coord( iPar ));
4032 // set the second UV
4033 listUV.push_back( *n_uv->second );
4034 listUV.back().SetCoord( iPar, uv2.Coord( iPar ));
4035 if ( uvMap2.empty() )
4036 uvMap2 = uvMap; // copy the uvMap contents
4037 uvMap2[ nSeam ] = &listUV.back();
4039 // collect movable nodes linked to ones on seam in nodesNearSeam
4040 SMDS_ElemIteratorPtr eIt = nSeam->GetInverseElementIterator(SMDSAbs_Face);
4041 while ( eIt->more() ) {
4042 const SMDS_MeshElement* e = eIt->next();
4043 int nbUseMap1 = 0, nbUseMap2 = 0;
4044 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
4045 int nn = 0, nbn = e->NbNodes();
4046 if(e->IsQuadratic()) nbn = nbn/2;
4047 while ( nn++ < nbn )
4049 const SMDS_MeshNode* n =
4050 static_cast<const SMDS_MeshNode*>( nIt->next() );
4052 setMovableNodes.find( n ) == setMovableNodes.end() )
4054 // add only nodes being closer to uv2 than to uv1
4055 // gp_Pnt pMid (0.5 * ( n->X() + nSeam->X() ),
4056 // 0.5 * ( n->Y() + nSeam->Y() ),
4057 // 0.5 * ( n->Z() + nSeam->Z() ));
4059 // getClosestUV( projector, pMid, uv );
4060 double x = uvMap[ n ]->Coord( iPar );
4061 if ( Abs( uv1.Coord( iPar ) - x ) >
4062 Abs( uv2.Coord( iPar ) - x )) {
4063 nodesNearSeam.insert( n );
4069 // for centroidalSmooth all element nodes must
4070 // be on one side of a seam
4071 if ( theSmoothMethod == CENTROIDAL && nbUseMap1 && nbUseMap2 ) {
4072 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
4074 while ( nn++ < nbn ) {
4075 const SMDS_MeshNode* n =
4076 static_cast<const SMDS_MeshNode*>( nIt->next() );
4077 setMovableNodes.erase( n );
4081 } // loop on nodes on seam
4082 } // loop on edge of a face
4083 } // if ( !face.IsNull() )
4085 if ( setMovableNodes.empty() ) {
4086 MESSAGE( "Face id : " << *fId << " - NO SMOOTHING: no nodes to move!!!");
4087 continue; // goto next face
4095 double maxRatio = -1., maxDisplacement = -1.;
4096 set<const SMDS_MeshNode*>::iterator nodeToMove;
4097 for ( it = 0; it < theNbIterations; it++ ) {
4098 maxDisplacement = 0.;
4099 nodeToMove = setMovableNodes.begin();
4100 for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
4101 const SMDS_MeshNode* node = (*nodeToMove);
4102 gp_XYZ aPrevPos ( node->X(), node->Y(), node->Z() );
4105 bool map2 = ( nodesNearSeam.find( node ) != nodesNearSeam.end() );
4106 if ( theSmoothMethod == LAPLACIAN )
4107 laplacianSmooth( node, surface, map2 ? uvMap2 : uvMap );
4109 centroidalSmooth( node, surface, map2 ? uvMap2 : uvMap );
4111 // node displacement
4112 gp_XYZ aNewPos ( node->X(), node->Y(), node->Z() );
4113 Standard_Real aDispl = (aPrevPos - aNewPos).SquareModulus();
4114 if ( aDispl > maxDisplacement )
4115 maxDisplacement = aDispl;
4117 // no node movement => exit
4118 //if ( maxDisplacement < 1.e-16 ) {
4119 if ( maxDisplacement < disttol ) {
4120 MESSAGE("-- no node movement --");
4124 // check elements quality
4126 list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4127 for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
4128 const SMDS_MeshElement* elem = (*elemIt);
4129 if ( !elem || elem->GetType() != SMDSAbs_Face )
4131 SMESH::Controls::TSequenceOfXYZ aPoints;
4132 if ( aQualityFunc.GetPoints( elem, aPoints )) {
4133 double aValue = aQualityFunc.GetValue( aPoints );
4134 if ( aValue > maxRatio )
4138 if ( maxRatio <= theTgtAspectRatio ) {
4139 MESSAGE("-- quality achived --");
4142 if (it+1 == theNbIterations) {
4143 MESSAGE("-- Iteration limit exceeded --");
4145 } // smoothing iterations
4147 MESSAGE(" Face id: " << *fId <<
4148 " Nb iterstions: " << it <<
4149 " Displacement: " << maxDisplacement <<
4150 " Aspect Ratio " << maxRatio);
4152 // ---------------------------------------
4153 // new nodes positions are computed,
4154 // record movement in DS and set new UV
4155 // ---------------------------------------
4156 nodeToMove = setMovableNodes.begin();
4157 for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
4158 SMDS_MeshNode* node = const_cast< SMDS_MeshNode* > (*nodeToMove);
4159 aMesh->MoveNode( node, node->X(), node->Y(), node->Z() );
4160 map< const SMDS_MeshNode*, gp_XY* >::iterator node_uv = uvMap.find( node );
4161 if ( node_uv != uvMap.end() ) {
4162 gp_XY* uv = node_uv->second;
4164 ( SMDS_PositionPtr( new SMDS_FacePosition( uv->X(), uv->Y() )));
4168 // move medium nodes of quadratic elements
4171 vector<const SMDS_MeshNode*> nodes;
4173 list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4174 for ( ; elemIt != elemsOnFace.end(); ++elemIt )
4176 const SMDS_MeshElement* QF = *elemIt;
4177 if ( QF->IsQuadratic() )
4179 nodes.assign( SMDS_MeshElement::iterator( QF->interlacedNodesElemIterator() ),
4180 SMDS_MeshElement::iterator() );
4181 nodes.push_back( nodes[0] );
4183 for (size_t i = 1; i < nodes.size(); i += 2 ) // i points to a medium node
4185 if ( !surface.IsNull() )
4187 gp_XY uv1 = helper.GetNodeUV( face, nodes[i-1], nodes[i+1], &checkUV );
4188 gp_XY uv2 = helper.GetNodeUV( face, nodes[i+1], nodes[i-1], &checkUV );
4189 gp_XY uv = helper.GetMiddleUV( surface, uv1, uv2 );
4190 xyz = surface->Value( uv.X(), uv.Y() );
4193 xyz = 0.5 * ( SMESH_TNodeXYZ( nodes[i-1] ) + SMESH_TNodeXYZ( nodes[i+1] ));
4195 if (( SMESH_TNodeXYZ( nodes[i] ) - xyz.XYZ() ).Modulus() > disttol )
4196 // we have to move a medium node
4197 aMesh->MoveNode( nodes[i], xyz.X(), xyz.Y(), xyz.Z() );
4203 } // loop on face ids
4209 //=======================================================================
4210 //function : isReverse
4211 //purpose : Return true if normal of prevNodes is not co-directied with
4212 // gp_Vec(prevNodes[iNotSame],nextNodes[iNotSame]).
4213 // iNotSame is where prevNodes and nextNodes are different.
4214 // If result is true then future volume orientation is OK
4215 //=======================================================================
4217 bool isReverse(const SMDS_MeshElement* face,
4218 const vector<const SMDS_MeshNode*>& prevNodes,
4219 const vector<const SMDS_MeshNode*>& nextNodes,
4223 SMESH_TNodeXYZ pP = prevNodes[ iNotSame ];
4224 SMESH_TNodeXYZ pN = nextNodes[ iNotSame ];
4225 gp_XYZ extrDir( pN - pP ), faceNorm;
4226 SMESH_MeshAlgos::FaceNormal( face, faceNorm, /*normalized=*/false );
4228 return faceNorm * extrDir < 0.0;
4231 //================================================================================
4233 * \brief Assure that theElemSets[0] holds elements, not nodes
4235 //================================================================================
4237 void setElemsFirst( TIDSortedElemSet theElemSets[2] )
4239 if ( !theElemSets[0].empty() &&
4240 (*theElemSets[0].begin())->GetType() == SMDSAbs_Node )
4242 std::swap( theElemSets[0], theElemSets[1] );
4244 else if ( !theElemSets[1].empty() &&
4245 (*theElemSets[1].begin())->GetType() != SMDSAbs_Node )
4247 std::swap( theElemSets[0], theElemSets[1] );
4252 //=======================================================================
4254 * \brief Create elements by sweeping an element
4255 * \param elem - element to sweep
4256 * \param newNodesItVec - nodes generated from each node of the element
4257 * \param newElems - generated elements
4258 * \param nbSteps - number of sweeping steps
4259 * \param srcElements - to append elem for each generated element
4261 //=======================================================================
4263 void SMESH_MeshEditor::sweepElement(const SMDS_MeshElement* elem,
4264 const vector<TNodeOfNodeListMapItr> & newNodesItVec,
4265 list<const SMDS_MeshElement*>& newElems,
4267 SMESH_SequenceOfElemPtr& srcElements)
4269 //MESSAGE("sweepElement " << nbSteps);
4270 SMESHDS_Mesh* aMesh = GetMeshDS();
4272 const int nbNodes = elem->NbNodes();
4273 const int nbCorners = elem->NbCornerNodes();
4274 SMDSAbs_EntityType baseType = elem->GetEntityType(); /* it can change in case of
4275 polyhedron creation !!! */
4276 // Loop on elem nodes:
4277 // find new nodes and detect same nodes indices
4278 vector < list< const SMDS_MeshNode* >::const_iterator > itNN( nbNodes );
4279 vector<const SMDS_MeshNode*> prevNod( nbNodes );
4280 vector<const SMDS_MeshNode*> nextNod( nbNodes );
4281 vector<const SMDS_MeshNode*> midlNod( nbNodes );
4283 int iNode, nbSame = 0, nbDouble = 0, iNotSameNode = 0;
4284 vector<int> sames(nbNodes);
4285 vector<bool> isSingleNode(nbNodes);
4287 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4288 TNodeOfNodeListMapItr nnIt = newNodesItVec[ iNode ];
4289 const SMDS_MeshNode* node = nnIt->first;
4290 const list< const SMDS_MeshNode* > & listNewNodes = nnIt->second;
4291 if ( listNewNodes.empty() )
4294 itNN [ iNode ] = listNewNodes.begin();
4295 prevNod[ iNode ] = node;
4296 nextNod[ iNode ] = listNewNodes.front();
4298 isSingleNode[iNode] = (listNewNodes.size()==nbSteps); /* medium node of quadratic or
4299 corner node of linear */
4300 if ( prevNod[ iNode ] != nextNod [ iNode ])
4301 nbDouble += !isSingleNode[iNode];
4303 if( iNode < nbCorners ) { // check corners only
4304 if ( prevNod[ iNode ] == nextNod [ iNode ])
4305 sames[nbSame++] = iNode;
4307 iNotSameNode = iNode;
4311 if ( nbSame == nbNodes || nbSame > 2) {
4312 MESSAGE( " Too many same nodes of element " << elem->GetID() );
4316 if ( elem->GetType() == SMDSAbs_Face && !isReverse( elem, prevNod, nextNod, iNotSameNode ))
4318 // fix nodes order to have bottom normal external
4319 if ( baseType == SMDSEntity_Polygon )
4321 std::reverse( itNN.begin(), itNN.end() );
4322 std::reverse( prevNod.begin(), prevNod.end() );
4323 std::reverse( midlNod.begin(), midlNod.end() );
4324 std::reverse( nextNod.begin(), nextNod.end() );
4325 std::reverse( isSingleNode.begin(), isSingleNode.end() );
4329 const vector<int>& ind = SMDS_MeshCell::reverseSmdsOrder( baseType );
4330 SMDS_MeshCell::applyInterlace( ind, itNN );
4331 SMDS_MeshCell::applyInterlace( ind, prevNod );
4332 SMDS_MeshCell::applyInterlace( ind, nextNod );
4333 SMDS_MeshCell::applyInterlace( ind, midlNod );
4334 SMDS_MeshCell::applyInterlace( ind, isSingleNode );
4337 sames[nbSame] = iNotSameNode;
4338 for ( int j = 0; j <= nbSame; ++j )
4339 for ( size_t i = 0; i < ind.size(); ++i )
4340 if ( ind[i] == sames[j] )
4345 iNotSameNode = sames[nbSame];
4349 else if ( elem->GetType() == SMDSAbs_Edge )
4351 // orient a new face same as adjacent one
4353 const SMDS_MeshElement* e;
4354 TIDSortedElemSet dummy;
4355 if (( e = SMESH_MeshAlgos::FindFaceInSet( nextNod[0], prevNod[0], dummy,dummy, &i1, &i2 )) ||
4356 ( e = SMESH_MeshAlgos::FindFaceInSet( prevNod[1], nextNod[1], dummy,dummy, &i1, &i2 )) ||
4357 ( e = SMESH_MeshAlgos::FindFaceInSet( prevNod[0], prevNod[1], dummy,dummy, &i1, &i2 )))
4359 // there is an adjacent face, check order of nodes in it
4360 bool sameOrder = ( Abs( i2 - i1 ) == 1 ) ? ( i2 > i1 ) : ( i2 < i1 );
4363 std::swap( itNN[0], itNN[1] );
4364 std::swap( prevNod[0], prevNod[1] );
4365 std::swap( nextNod[0], nextNod[1] );
4366 isSingleNode.swap( isSingleNode[0], isSingleNode[1] );
4368 sames[0] = 1 - sames[0];
4369 iNotSameNode = 1 - iNotSameNode;
4374 int iSameNode = 0, iBeforeSame = 0, iAfterSame = 0, iOpposSame = 0;
4376 iSameNode = sames[ nbSame-1 ];
4377 iBeforeSame = ( iSameNode + nbCorners - 1 ) % nbCorners;
4378 iAfterSame = ( iSameNode + 1 ) % nbCorners;
4379 iOpposSame = ( iSameNode - 2 < 0 ? iSameNode + 2 : iSameNode - 2 );
4382 // make new elements
4383 for (int iStep = 0; iStep < nbSteps; iStep++ )
4386 for ( iNode = 0; iNode < nbNodes; iNode++ )
4388 midlNod[ iNode ] = isSingleNode[iNode] ? 0 : *itNN[ iNode ]++;
4389 nextNod[ iNode ] = *itNN[ iNode ]++;
4392 SMDS_MeshElement* aNewElem = 0;
4393 /*if(!elem->IsPoly())*/ {
4394 switch ( baseType ) {
4396 case SMDSEntity_Node: { // sweep NODE
4397 if ( nbSame == 0 ) {
4398 if ( isSingleNode[0] )
4399 aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ] );
4401 aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ], midlNod[ 0 ] );
4407 case SMDSEntity_Edge: { // sweep EDGE
4408 if ( nbDouble == 0 )
4410 if ( nbSame == 0 ) // ---> quadrangle
4411 aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
4412 nextNod[ 1 ], nextNod[ 0 ] );
4413 else // ---> triangle
4414 aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
4415 nextNod[ iNotSameNode ] );
4417 else // ---> polygon
4419 vector<const SMDS_MeshNode*> poly_nodes;
4420 poly_nodes.push_back( prevNod[0] );
4421 poly_nodes.push_back( prevNod[1] );
4422 if ( prevNod[1] != nextNod[1] )
4424 if ( midlNod[1]) poly_nodes.push_back( midlNod[1]);
4425 poly_nodes.push_back( nextNod[1] );
4427 if ( prevNod[0] != nextNod[0] )
4429 poly_nodes.push_back( nextNod[0] );
4430 if ( midlNod[0]) poly_nodes.push_back( midlNod[0]);
4432 switch ( poly_nodes.size() ) {
4434 aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ], poly_nodes[ 2 ]);
4437 aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ],
4438 poly_nodes[ 2 ], poly_nodes[ 3 ]);
4441 aNewElem = aMesh->AddPolygonalFace (poly_nodes);
4446 case SMDSEntity_Triangle: // TRIANGLE --->
4448 if ( nbDouble > 0 ) break;
4449 if ( nbSame == 0 ) // ---> pentahedron
4450 aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
4451 nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ] );
4453 else if ( nbSame == 1 ) // ---> pyramid
4454 aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
4455 nextNod[ iAfterSame ], nextNod[ iBeforeSame ],
4456 nextNod[ iSameNode ]);
4458 else // 2 same nodes: ---> tetrahedron
4459 aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
4460 nextNod[ iNotSameNode ]);
4463 case SMDSEntity_Quad_Edge: // sweep quadratic EDGE --->
4467 if ( nbDouble+nbSame == 2 )
4469 if(nbSame==0) { // ---> quadratic quadrangle
4470 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
4471 prevNod[2], midlNod[1], nextNod[2], midlNod[0]);
4473 else { //(nbSame==1) // ---> quadratic triangle
4475 return; // medium node on axis
4477 else if(sames[0]==0)
4478 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1],
4479 prevNod[2], midlNod[1], nextNod[2] );
4481 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[0],
4482 prevNod[2], nextNod[2], midlNod[0]);
4485 else if ( nbDouble == 3 )
4487 if ( nbSame == 0 ) { // ---> bi-quadratic quadrangle
4488 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
4489 prevNod[2], midlNod[1], nextNod[2], midlNod[0], midlNod[2]);
4496 case SMDSEntity_Quadrangle: { // sweep QUADRANGLE --->
4497 if ( nbDouble > 0 ) break;
4499 if ( nbSame == 0 ) // ---> hexahedron
4500 aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ], prevNod[ 3 ],
4501 nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ], nextNod[ 3 ]);
4503 else if ( nbSame == 1 ) { // ---> pyramid + pentahedron
4504 aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
4505 nextNod[ iAfterSame ], nextNod[ iBeforeSame ],
4506 nextNod[ iSameNode ]);
4507 newElems.push_back( aNewElem );
4508 aNewElem = aMesh->AddVolume (prevNod[ iAfterSame ], prevNod[ iOpposSame ],
4509 prevNod[ iBeforeSame ], nextNod[ iAfterSame ],
4510 nextNod[ iOpposSame ], nextNod[ iBeforeSame ] );
4512 else if ( nbSame == 2 ) { // ---> pentahedron
4513 if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] )
4514 // iBeforeSame is same too
4515 aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iOpposSame ],
4516 nextNod[ iOpposSame ], prevNod[ iSameNode ],
4517 prevNod[ iAfterSame ], nextNod[ iAfterSame ]);
4519 // iAfterSame is same too
4520 aNewElem = aMesh->AddVolume (prevNod[ iSameNode ], prevNod[ iBeforeSame ],
4521 nextNod[ iBeforeSame ], prevNod[ iAfterSame ],
4522 prevNod[ iOpposSame ], nextNod[ iOpposSame ]);
4526 case SMDSEntity_Quad_Triangle: // sweep (Bi)Quadratic TRIANGLE --->
4527 case SMDSEntity_BiQuad_Triangle: /* ??? */ {
4528 if ( nbDouble+nbSame != 3 ) break;
4530 // ---> pentahedron with 15 nodes
4531 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
4532 nextNod[0], nextNod[1], nextNod[2],
4533 prevNod[3], prevNod[4], prevNod[5],
4534 nextNod[3], nextNod[4], nextNod[5],
4535 midlNod[0], midlNod[1], midlNod[2]);
4537 else if(nbSame==1) {
4538 // ---> 2d order pyramid of 13 nodes
4539 int apex = iSameNode;
4540 int i0 = ( apex + 1 ) % nbCorners;
4541 int i1 = ( apex - 1 + nbCorners ) % nbCorners;
4545 aNewElem = aMesh->AddVolume(prevNod[i1], prevNod[i0],
4546 nextNod[i0], nextNod[i1], prevNod[apex],
4547 prevNod[i01], midlNod[i0],
4548 nextNod[i01], midlNod[i1],
4549 prevNod[i1a], prevNod[i0a],
4550 nextNod[i0a], nextNod[i1a]);
4552 else if(nbSame==2) {
4553 // ---> 2d order tetrahedron of 10 nodes
4554 int n1 = iNotSameNode;
4555 int n2 = ( n1 + 1 ) % nbCorners;
4556 int n3 = ( n1 + nbCorners - 1 ) % nbCorners;
4560 aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], prevNod[n3], nextNod[n1],
4561 prevNod[n12], prevNod[n23], prevNod[n31],
4562 midlNod[n1], nextNod[n12], nextNod[n31]);
4566 case SMDSEntity_Quad_Quadrangle: { // sweep Quadratic QUADRANGLE --->
4568 if ( nbDouble != 4 ) break;
4569 // ---> hexahedron with 20 nodes
4570 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
4571 nextNod[0], nextNod[1], nextNod[2], nextNod[3],
4572 prevNod[4], prevNod[5], prevNod[6], prevNod[7],
4573 nextNod[4], nextNod[5], nextNod[6], nextNod[7],
4574 midlNod[0], midlNod[1], midlNod[2], midlNod[3]);
4576 else if(nbSame==1) {
4577 // ---> pyramid + pentahedron - can not be created since it is needed
4578 // additional middle node at the center of face
4579 INFOS( " Sweep for face " << elem->GetID() << " can not be created" );
4582 else if( nbSame == 2 ) {
4583 if ( nbDouble != 2 ) break;
4584 // ---> 2d order Pentahedron with 15 nodes
4586 if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] ) {
4587 // iBeforeSame is same too
4594 // iAfterSame is same too
4604 aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], nextNod[n2],
4605 prevNod[n4], prevNod[n5], nextNod[n5],
4606 prevNod[n12], midlNod[n2], nextNod[n12],
4607 prevNod[n45], midlNod[n5], nextNod[n45],
4608 prevNod[n14], prevNod[n25], nextNod[n25]);
4612 case SMDSEntity_BiQuad_Quadrangle: { // sweep BiQuadratic QUADRANGLE --->
4614 if( nbSame == 0 && nbDouble == 9 ) {
4615 // ---> tri-quadratic hexahedron with 27 nodes
4616 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
4617 nextNod[0], nextNod[1], nextNod[2], nextNod[3],
4618 prevNod[4], prevNod[5], prevNod[6], prevNod[7],
4619 nextNod[4], nextNod[5], nextNod[6], nextNod[7],
4620 midlNod[0], midlNod[1], midlNod[2], midlNod[3],
4621 prevNod[8], // bottom center
4622 midlNod[4], midlNod[5], midlNod[6], midlNod[7],
4623 nextNod[8], // top center
4624 midlNod[8]);// elem center
4632 case SMDSEntity_Polygon: { // sweep POLYGON
4634 if ( nbNodes == 6 && nbSame == 0 && nbDouble == 0 ) {
4635 // ---> hexagonal prism
4636 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
4637 prevNod[3], prevNod[4], prevNod[5],
4638 nextNod[0], nextNod[1], nextNod[2],
4639 nextNod[3], nextNod[4], nextNod[5]);
4643 case SMDSEntity_Ball:
4648 } // switch ( baseType )
4651 if ( !aNewElem && elem->GetType() == SMDSAbs_Face ) // try to create a polyherdal prism
4653 if ( baseType != SMDSEntity_Polygon )
4655 const std::vector<int>& ind = SMDS_MeshCell::interlacedSmdsOrder(baseType);
4656 SMDS_MeshCell::applyInterlace( ind, prevNod );
4657 SMDS_MeshCell::applyInterlace( ind, nextNod );
4658 SMDS_MeshCell::applyInterlace( ind, midlNod );
4659 SMDS_MeshCell::applyInterlace( ind, itNN );
4660 SMDS_MeshCell::applyInterlace( ind, isSingleNode );
4661 baseType = SMDSEntity_Polygon; // WARNING: change baseType !!!!
4663 vector<const SMDS_MeshNode*> polyedre_nodes (nbNodes*2 + 4*nbNodes);
4664 vector<int> quantities (nbNodes + 2);
4665 polyedre_nodes.clear();
4669 for (int inode = 0; inode < nbNodes; inode++)
4670 polyedre_nodes.push_back( prevNod[inode] );
4671 quantities.push_back( nbNodes );
4674 polyedre_nodes.push_back( nextNod[0] );
4675 for (int inode = nbNodes; inode-1; --inode )
4676 polyedre_nodes.push_back( nextNod[inode-1] );
4677 quantities.push_back( nbNodes );
4680 for (int iface = 0; iface < nbNodes; iface++)
4682 const int prevNbNodes = polyedre_nodes.size();
4683 int inextface = (iface+1) % nbNodes;
4684 polyedre_nodes.push_back( prevNod[inextface] );
4685 polyedre_nodes.push_back( prevNod[iface] );
4686 if ( prevNod[iface] != nextNod[iface] )
4688 if ( midlNod[ iface ]) polyedre_nodes.push_back( midlNod[ iface ]);
4689 polyedre_nodes.push_back( nextNod[iface] );
4691 if ( prevNod[inextface] != nextNod[inextface] )
4693 polyedre_nodes.push_back( nextNod[inextface] );
4694 if ( midlNod[ inextface ]) polyedre_nodes.push_back( midlNod[ inextface ]);
4696 const int nbFaceNodes = polyedre_nodes.size() - prevNbNodes;
4697 if ( nbFaceNodes > 2 )
4698 quantities.push_back( nbFaceNodes );
4699 else // degenerated face
4700 polyedre_nodes.resize( prevNbNodes );
4702 aNewElem = aMesh->AddPolyhedralVolume (polyedre_nodes, quantities);
4704 } // // try to create a polyherdal prism
4707 newElems.push_back( aNewElem );
4708 myLastCreatedElems.Append(aNewElem);
4709 srcElements.Append( elem );
4712 // set new prev nodes
4713 for ( iNode = 0; iNode < nbNodes; iNode++ )
4714 prevNod[ iNode ] = nextNod[ iNode ];
4719 //=======================================================================
4721 * \brief Create 1D and 2D elements around swept elements
4722 * \param mapNewNodes - source nodes and ones generated from them
4723 * \param newElemsMap - source elements and ones generated from them
4724 * \param elemNewNodesMap - nodes generated from each node of each element
4725 * \param elemSet - all swept elements
4726 * \param nbSteps - number of sweeping steps
4727 * \param srcElements - to append elem for each generated element
4729 //=======================================================================
4731 void SMESH_MeshEditor::makeWalls (TNodeOfNodeListMap & mapNewNodes,
4732 TTElemOfElemListMap & newElemsMap,
4733 TElemOfVecOfNnlmiMap & elemNewNodesMap,
4734 TIDSortedElemSet& elemSet,
4736 SMESH_SequenceOfElemPtr& srcElements)
4738 ASSERT( newElemsMap.size() == elemNewNodesMap.size() );
4739 SMESHDS_Mesh* aMesh = GetMeshDS();
4741 // Find nodes belonging to only one initial element - sweep them into edges.
4743 TNodeOfNodeListMapItr nList = mapNewNodes.begin();
4744 for ( ; nList != mapNewNodes.end(); nList++ )
4746 const SMDS_MeshNode* node =
4747 static_cast<const SMDS_MeshNode*>( nList->first );
4748 if ( newElemsMap.count( node ))
4749 continue; // node was extruded into edge
4750 SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator();
4751 int nbInitElems = 0;
4752 const SMDS_MeshElement* el = 0;
4753 SMDSAbs_ElementType highType = SMDSAbs_Edge; // count most complex elements only
4754 while ( eIt->more() && nbInitElems < 2 ) {
4756 SMDSAbs_ElementType type = el->GetType();
4757 if ( type == SMDSAbs_Volume || type < highType ) continue;
4758 if ( type > highType ) {
4762 nbInitElems += elemSet.count(el);
4764 if ( nbInitElems < 2 ) {
4765 bool NotCreateEdge = el && el->IsMediumNode(node);
4766 if(!NotCreateEdge) {
4767 vector<TNodeOfNodeListMapItr> newNodesItVec( 1, nList );
4768 list<const SMDS_MeshElement*> newEdges;
4769 sweepElement( node, newNodesItVec, newEdges, nbSteps, srcElements );
4774 // Make a ceiling for each element ie an equal element of last new nodes.
4775 // Find free links of faces - make edges and sweep them into faces.
4777 TTElemOfElemListMap::iterator itElem = newElemsMap.begin();
4778 TElemOfVecOfNnlmiMap::iterator itElemNodes = elemNewNodesMap.begin();
4779 for ( ; itElem != newElemsMap.end(); itElem++, itElemNodes++ )
4781 const SMDS_MeshElement* elem = itElem->first;
4782 vector<TNodeOfNodeListMapItr>& vecNewNodes = itElemNodes->second;
4784 if(itElem->second.size()==0) continue;
4786 const bool isQuadratic = elem->IsQuadratic();
4788 if ( elem->GetType() == SMDSAbs_Edge ) {
4789 // create a ceiling edge
4790 if ( !isQuadratic ) {
4791 if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
4792 vecNewNodes[ 1 ]->second.back())) {
4793 myLastCreatedElems.Append(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
4794 vecNewNodes[ 1 ]->second.back()));
4795 srcElements.Append( elem );
4799 if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
4800 vecNewNodes[ 1 ]->second.back(),
4801 vecNewNodes[ 2 ]->second.back())) {
4802 myLastCreatedElems.Append(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
4803 vecNewNodes[ 1 ]->second.back(),
4804 vecNewNodes[ 2 ]->second.back()));
4805 srcElements.Append( elem );
4809 if ( elem->GetType() != SMDSAbs_Face )
4812 bool hasFreeLinks = false;
4814 TIDSortedElemSet avoidSet;
4815 avoidSet.insert( elem );
4817 set<const SMDS_MeshNode*> aFaceLastNodes;
4818 int iNode, nbNodes = vecNewNodes.size();
4819 if ( !isQuadratic ) {
4820 // loop on the face nodes
4821 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4822 aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4823 // look for free links of the face
4824 int iNext = ( iNode + 1 == nbNodes ) ? 0 : iNode + 1;
4825 const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
4826 const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
4827 // check if a link n1-n2 is free
4828 if ( ! SMESH_MeshAlgos::FindFaceInSet ( n1, n2, elemSet, avoidSet )) {
4829 hasFreeLinks = true;
4830 // make a new edge and a ceiling for a new edge
4831 const SMDS_MeshElement* edge;
4832 if ( ! ( edge = aMesh->FindEdge( n1, n2 ))) {
4833 myLastCreatedElems.Append( edge = aMesh->AddEdge( n1, n2 )); // free link edge
4834 srcElements.Append( myLastCreatedElems.Last() );
4836 n1 = vecNewNodes[ iNode ]->second.back();
4837 n2 = vecNewNodes[ iNext ]->second.back();
4838 if ( !aMesh->FindEdge( n1, n2 )) {
4839 myLastCreatedElems.Append(aMesh->AddEdge( n1, n2 )); // new edge ceiling
4840 srcElements.Append( edge );
4845 else { // elem is quadratic face
4846 int nbn = nbNodes/2;
4847 for ( iNode = 0; iNode < nbn; iNode++ ) {
4848 aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4849 int iNext = ( iNode + 1 == nbn ) ? 0 : iNode + 1;
4850 const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
4851 const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
4852 const SMDS_MeshNode* n3 = vecNewNodes[ iNode+nbn ]->first;
4853 // check if a link is free
4854 if ( ! SMESH_MeshAlgos::FindFaceInSet ( n1, n2, elemSet, avoidSet ) &&
4855 ! SMESH_MeshAlgos::FindFaceInSet ( n1, n3, elemSet, avoidSet ) &&
4856 ! SMESH_MeshAlgos::FindFaceInSet ( n3, n2, elemSet, avoidSet ) ) {
4857 hasFreeLinks = true;
4858 // make an edge and a ceiling for a new edge
4860 if ( !aMesh->FindEdge( n1, n2, n3 )) {
4861 myLastCreatedElems.Append(aMesh->AddEdge( n1, n2, n3 )); // free link edge
4862 srcElements.Append( elem );
4864 n1 = vecNewNodes[ iNode ]->second.back();
4865 n2 = vecNewNodes[ iNext ]->second.back();
4866 n3 = vecNewNodes[ iNode+nbn ]->second.back();
4867 if ( !aMesh->FindEdge( n1, n2, n3 )) {
4868 myLastCreatedElems.Append(aMesh->AddEdge( n1, n2, n3 )); // ceiling edge
4869 srcElements.Append( elem );
4873 for ( iNode = nbn; iNode < nbNodes; iNode++ ) {
4874 aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4878 // sweep free links into faces
4880 if ( hasFreeLinks ) {
4881 list<const SMDS_MeshElement*> & newVolumes = itElem->second;
4882 int iVol, volNb, nbVolumesByStep = newVolumes.size() / nbSteps;
4884 set<const SMDS_MeshNode*> initNodeSet, topNodeSet, faceNodeSet;
4885 set<const SMDS_MeshNode*> initNodeSetNoCenter/*, topNodeSetNoCenter*/;
4886 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4887 initNodeSet.insert( vecNewNodes[ iNode ]->first );
4888 topNodeSet .insert( vecNewNodes[ iNode ]->second.back() );
4890 if ( isQuadratic && nbNodes % 2 ) { // node set for the case of a biquadratic
4891 initNodeSetNoCenter = initNodeSet; // swept face and a not biquadratic volume
4892 initNodeSetNoCenter.erase( vecNewNodes.back()->first );
4894 for ( volNb = 0; volNb < nbVolumesByStep; volNb++ ) {
4895 list<const SMDS_MeshElement*>::iterator v = newVolumes.begin();
4896 std::advance( v, volNb );
4897 // find indices of free faces of a volume and their source edges
4898 list< int > freeInd;
4899 list< const SMDS_MeshElement* > srcEdges; // source edges of free faces
4900 SMDS_VolumeTool vTool( *v, /*ignoreCentralNodes=*/false );
4901 int iF, nbF = vTool.NbFaces();
4902 for ( iF = 0; iF < nbF; iF ++ ) {
4903 if (vTool.IsFreeFace( iF ) &&
4904 vTool.GetFaceNodes( iF, faceNodeSet ) &&
4905 initNodeSet != faceNodeSet) // except an initial face
4907 if ( nbSteps == 1 && faceNodeSet == topNodeSet )
4909 if ( faceNodeSet == initNodeSetNoCenter )
4911 freeInd.push_back( iF );
4912 // find source edge of a free face iF
4913 vector<const SMDS_MeshNode*> commonNodes; // shared by the initial and free faces
4914 commonNodes.resize( initNodeSet.size(), NULL ); // avoid spoiling memory
4915 std::set_intersection( faceNodeSet.begin(), faceNodeSet.end(),
4916 initNodeSet.begin(), initNodeSet.end(),
4917 commonNodes.begin());
4918 if ( (*v)->IsQuadratic() )
4919 srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1],commonNodes[2]));
4921 srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1]));
4923 if ( !srcEdges.back() )
4925 cout << "SMESH_MeshEditor::makeWalls(), no source edge found for a free face #"
4926 << iF << " of volume #" << vTool.ID() << endl;
4931 if ( freeInd.empty() )
4934 // create faces for all steps;
4935 // if such a face has been already created by sweep of edge,
4936 // assure that its orientation is OK
4937 for ( int iStep = 0; iStep < nbSteps; iStep++ ) {
4938 vTool.Set( *v, /*ignoreCentralNodes=*/false );
4939 vTool.SetExternalNormal();
4940 const int nextShift = vTool.IsForward() ? +1 : -1;
4941 list< int >::iterator ind = freeInd.begin();
4942 list< const SMDS_MeshElement* >::iterator srcEdge = srcEdges.begin();
4943 for ( ; ind != freeInd.end(); ++ind, ++srcEdge ) // loop on free faces
4945 const SMDS_MeshNode** nodes = vTool.GetFaceNodes( *ind );
4946 int nbn = vTool.NbFaceNodes( *ind );
4947 const SMDS_MeshElement * f = 0;
4948 if ( nbn == 3 ) ///// triangle
4950 f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ]);
4952 nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
4954 const SMDS_MeshNode* newOrder[3] = { nodes[ 1 - nextShift ],
4956 nodes[ 1 + nextShift ] };
4958 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4960 myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
4964 else if ( nbn == 4 ) ///// quadrangle
4966 f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ], nodes[ 3 ]);
4968 nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
4970 const SMDS_MeshNode* newOrder[4] = { nodes[ 0 ], nodes[ 2-nextShift ],
4971 nodes[ 2 ], nodes[ 2+nextShift ] };
4973 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4975 myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
4976 newOrder[ 2 ], newOrder[ 3 ]));
4979 else if ( nbn == 6 && isQuadratic ) /////// quadratic triangle
4981 f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[1], nodes[3], nodes[5] );
4983 nodes[2] != f->GetNodeWrap( f->GetNodeIndex( nodes[0] ) + 2*nextShift ))
4985 const SMDS_MeshNode* newOrder[6] = { nodes[2 - 2*nextShift],
4987 nodes[2 + 2*nextShift],
4988 nodes[3 - 2*nextShift],
4990 nodes[3 + 2*nextShift]};
4992 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4994 myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ],
5002 else if ( nbn == 8 && isQuadratic ) /////// quadratic quadrangle
5004 f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[6],
5005 nodes[1], nodes[3], nodes[5], nodes[7] );
5007 nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
5009 const SMDS_MeshNode* newOrder[8] = { nodes[0],
5010 nodes[4 - 2*nextShift],
5012 nodes[4 + 2*nextShift],
5014 nodes[5 - 2*nextShift],
5016 nodes[5 + 2*nextShift] };
5018 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5020 myLastCreatedElems.Append(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
5021 newOrder[ 2 ], newOrder[ 3 ],
5022 newOrder[ 4 ], newOrder[ 5 ],
5023 newOrder[ 6 ], newOrder[ 7 ]));
5026 else if ( nbn == 9 && isQuadratic ) /////// bi-quadratic quadrangle
5028 f = aMesh->FindElement( vector<const SMDS_MeshNode*>( nodes, nodes+nbn ),
5029 SMDSAbs_Face, /*noMedium=*/false);
5031 nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
5033 const SMDS_MeshNode* newOrder[9] = { nodes[0],
5034 nodes[4 - 2*nextShift],
5036 nodes[4 + 2*nextShift],
5038 nodes[5 - 2*nextShift],
5040 nodes[5 + 2*nextShift],
5043 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5045 myLastCreatedElems.Append(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
5046 newOrder[ 2 ], newOrder[ 3 ],
5047 newOrder[ 4 ], newOrder[ 5 ],
5048 newOrder[ 6 ], newOrder[ 7 ],
5052 else //////// polygon
5054 vector<const SMDS_MeshNode*> polygon_nodes ( nodes, nodes+nbn );
5055 const SMDS_MeshFace * f = aMesh->FindFace( polygon_nodes );
5057 nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + nextShift ))
5059 if ( !vTool.IsForward() )
5060 std::reverse( polygon_nodes.begin(), polygon_nodes.end());
5062 aMesh->ChangeElementNodes( f, &polygon_nodes[0], nbn );
5064 AddElement(polygon_nodes, SMDSAbs_Face, polygon_nodes.size()>4);
5068 while ( srcElements.Length() < myLastCreatedElems.Length() )
5069 srcElements.Append( *srcEdge );
5071 } // loop on free faces
5073 // go to the next volume
5075 while ( iVol++ < nbVolumesByStep ) v++;
5078 } // loop on volumes of one step
5079 } // sweep free links into faces
5081 // Make a ceiling face with a normal external to a volume
5083 // use SMDS_VolumeTool to get a correctly ordered nodes of a ceiling face
5084 SMDS_VolumeTool lastVol( itElem->second.back(), /*ignoreCentralNodes=*/false );
5085 int iF = lastVol.GetFaceIndex( aFaceLastNodes );
5087 if ( iF < 0 && isQuadratic && nbNodes % 2 ) { // remove a central node of biquadratic
5088 aFaceLastNodes.erase( vecNewNodes.back()->second.back() );
5089 iF = lastVol.GetFaceIndex( aFaceLastNodes );
5092 lastVol.SetExternalNormal();
5093 const SMDS_MeshNode** nodes = lastVol.GetFaceNodes( iF );
5094 int nbn = lastVol.NbFaceNodes( iF );
5095 // we do not use this->AddElement() because nodes are interlaced
5096 vector<const SMDS_MeshNode*> nodeVec( nodes, nodes+nbn );
5097 if ( !hasFreeLinks ||
5098 !aMesh->FindElement( nodeVec, SMDSAbs_Face, /*noMedium=*/false) )
5101 myLastCreatedElems.Append(aMesh->AddFace( nodes[0], nodes[1], nodes[2] ));
5103 else if ( nbn == 4 )
5104 myLastCreatedElems.Append(aMesh->AddFace( nodes[0], nodes[1], nodes[2], nodes[3]));
5106 else if ( nbn == 6 && isQuadratic )
5107 myLastCreatedElems.Append(aMesh->AddFace( nodes[0], nodes[2], nodes[4],
5108 nodes[1], nodes[3], nodes[5]));
5109 else if ( nbn == 7 && isQuadratic )
5110 myLastCreatedElems.Append(aMesh->AddFace( nodes[0], nodes[2], nodes[4],
5111 nodes[1], nodes[3], nodes[5], nodes[6]));
5112 else if ( nbn == 8 && isQuadratic )
5113 myLastCreatedElems.Append(aMesh->AddFace( nodes[0], nodes[2], nodes[4], nodes[6],
5114 nodes[1], nodes[3], nodes[5], nodes[7]));
5115 else if ( nbn == 9 && isQuadratic )
5116 myLastCreatedElems.Append(aMesh->AddFace( nodes[0], nodes[2], nodes[4], nodes[6],
5117 nodes[1], nodes[3], nodes[5], nodes[7],
5120 myLastCreatedElems.Append(aMesh->AddPolygonalFace( nodeVec ));
5122 while ( srcElements.Length() < myLastCreatedElems.Length() )
5123 srcElements.Append( elem );
5126 } // loop on swept elements
5129 //=======================================================================
5130 //function : RotationSweep
5132 //=======================================================================
5134 SMESH_MeshEditor::PGroupIDs
5135 SMESH_MeshEditor::RotationSweep(TIDSortedElemSet theElemSets[2],
5136 const gp_Ax1& theAxis,
5137 const double theAngle,
5138 const int theNbSteps,
5139 const double theTol,
5140 const bool theMakeGroups,
5141 const bool theMakeWalls)
5143 myLastCreatedElems.Clear();
5144 myLastCreatedNodes.Clear();
5146 // source elements for each generated one
5147 SMESH_SequenceOfElemPtr srcElems, srcNodes;
5149 MESSAGE( "RotationSweep()");
5151 aTrsf.SetRotation( theAxis, theAngle );
5153 aTrsf2.SetRotation( theAxis, theAngle/2. );
5155 gp_Lin aLine( theAxis );
5156 double aSqTol = theTol * theTol;
5158 SMESHDS_Mesh* aMesh = GetMeshDS();
5160 TNodeOfNodeListMap mapNewNodes;
5161 TElemOfVecOfNnlmiMap mapElemNewNodes;
5162 TTElemOfElemListMap newElemsMap;
5164 const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
5165 myMesh->NbFaces(ORDER_QUADRATIC) +
5166 myMesh->NbVolumes(ORDER_QUADRATIC) );
5167 // loop on theElemSets
5168 setElemsFirst( theElemSets );
5169 TIDSortedElemSet::iterator itElem;
5170 for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
5172 TIDSortedElemSet& theElems = theElemSets[ is2ndSet ];
5173 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
5174 const SMDS_MeshElement* elem = *itElem;
5175 if ( !elem || elem->GetType() == SMDSAbs_Volume )
5177 vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5178 newNodesItVec.reserve( elem->NbNodes() );
5180 // loop on elem nodes
5181 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5182 while ( itN->more() )
5184 // check if a node has been already sweeped
5185 const SMDS_MeshNode* node = cast2Node( itN->next() );
5187 gp_XYZ aXYZ( node->X(), node->Y(), node->Z() );
5189 aXYZ.Coord( coord[0], coord[1], coord[2] );
5190 bool isOnAxis = ( aLine.SquareDistance( aXYZ ) <= aSqTol );
5192 TNodeOfNodeListMapItr nIt =
5193 mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5194 list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5195 if ( listNewNodes.empty() )
5197 // check if we are to create medium nodes between corner ones
5198 bool needMediumNodes = false;
5199 if ( isQuadraticMesh )
5201 SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
5202 while (it->more() && !needMediumNodes )
5204 const SMDS_MeshElement* invElem = it->next();
5205 if ( invElem != elem && !theElems.count( invElem )) continue;
5206 needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
5207 if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
5208 needMediumNodes = true;
5213 const SMDS_MeshNode * newNode = node;
5214 for ( int i = 0; i < theNbSteps; i++ ) {
5216 if ( needMediumNodes ) // create a medium node
5218 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
5219 newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5220 myLastCreatedNodes.Append(newNode);
5221 srcNodes.Append( node );
5222 listNewNodes.push_back( newNode );
5223 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
5226 aTrsf.Transforms( coord[0], coord[1], coord[2] );
5228 // create a corner node
5229 newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5230 myLastCreatedNodes.Append(newNode);
5231 srcNodes.Append( node );
5232 listNewNodes.push_back( newNode );
5235 listNewNodes.push_back( newNode );
5236 // if ( needMediumNodes )
5237 // listNewNodes.push_back( newNode );
5241 newNodesItVec.push_back( nIt );
5243 // make new elements
5244 sweepElement( elem, newNodesItVec, newElemsMap[elem], theNbSteps, srcElems );
5249 makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElemSets[0], theNbSteps, srcElems );
5251 PGroupIDs newGroupIDs;
5252 if ( theMakeGroups )
5253 newGroupIDs = generateGroups( srcNodes, srcElems, "rotated");
5258 //=======================================================================
5259 //function : ExtrusParam
5260 //purpose : standard construction
5261 //=======================================================================
5263 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const gp_Vec& theStep,
5264 const int theNbSteps,
5266 const double theTolerance):
5268 myFlags( theFlags ),
5269 myTolerance( theTolerance ),
5270 myElemsToUse( NULL )
5272 mySteps = new TColStd_HSequenceOfReal;
5273 const double stepSize = theStep.Magnitude();
5274 for (int i=1; i<=theNbSteps; i++ )
5275 mySteps->Append( stepSize );
5277 if (( theFlags & EXTRUSION_FLAG_SEW ) &&
5278 ( theTolerance > 0 ))
5280 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDirAndSew;
5284 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDir;
5288 //=======================================================================
5289 //function : ExtrusParam
5290 //purpose : steps are given explicitly
5291 //=======================================================================
5293 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const gp_Dir& theDir,
5294 Handle(TColStd_HSequenceOfReal) theSteps,
5296 const double theTolerance):
5298 mySteps( theSteps ),
5299 myFlags( theFlags ),
5300 myTolerance( theTolerance ),
5301 myElemsToUse( NULL )
5303 if (( theFlags & EXTRUSION_FLAG_SEW ) &&
5304 ( theTolerance > 0 ))
5306 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDirAndSew;
5310 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDir;
5314 //=======================================================================
5315 //function : ExtrusParam
5316 //purpose : for extrusion by normal
5317 //=======================================================================
5319 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const double theStepSize,
5320 const int theNbSteps,
5324 mySteps( new TColStd_HSequenceOfReal ),
5325 myFlags( theFlags ),
5327 myElemsToUse( NULL )
5329 for (int i = 0; i < theNbSteps; i++ )
5330 mySteps->Append( theStepSize );
5334 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByNormal1D;
5338 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByNormal2D;
5342 //=======================================================================
5343 //function : ExtrusParam::SetElementsToUse
5344 //purpose : stores elements to use for extrusion by normal, depending on
5345 // state of EXTRUSION_FLAG_USE_INPUT_ELEMS_ONLY flag
5346 //=======================================================================
5348 void SMESH_MeshEditor::ExtrusParam::SetElementsToUse( const TIDSortedElemSet& elems )
5350 myElemsToUse = ToUseInpElemsOnly() ? & elems : 0;
5353 //=======================================================================
5354 //function : ExtrusParam::beginStepIter
5355 //purpose : prepare iteration on steps
5356 //=======================================================================
5358 void SMESH_MeshEditor::ExtrusParam::beginStepIter( bool withMediumNodes )
5360 myWithMediumNodes = withMediumNodes;
5364 //=======================================================================
5365 //function : ExtrusParam::moreSteps
5366 //purpose : are there more steps?
5367 //=======================================================================
5369 bool SMESH_MeshEditor::ExtrusParam::moreSteps()
5371 return myNextStep <= mySteps->Length() || !myCurSteps.empty();
5373 //=======================================================================
5374 //function : ExtrusParam::nextStep
5375 //purpose : returns the next step
5376 //=======================================================================
5378 double SMESH_MeshEditor::ExtrusParam::nextStep()
5381 if ( !myCurSteps.empty() )
5383 res = myCurSteps.back();
5384 myCurSteps.pop_back();
5386 else if ( myNextStep <= mySteps->Length() )
5388 myCurSteps.push_back( mySteps->Value( myNextStep ));
5390 if ( myWithMediumNodes )
5392 myCurSteps.back() /= 2.;
5393 myCurSteps.push_back( myCurSteps.back() );
5400 //=======================================================================
5401 //function : ExtrusParam::makeNodesByDir
5402 //purpose : create nodes for standard extrusion
5403 //=======================================================================
5405 int SMESH_MeshEditor::ExtrusParam::
5406 makeNodesByDir( SMESHDS_Mesh* mesh,
5407 const SMDS_MeshNode* srcNode,
5408 std::list<const SMDS_MeshNode*> & newNodes,
5409 const bool makeMediumNodes)
5411 gp_XYZ p = SMESH_TNodeXYZ( srcNode );
5414 for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5416 p += myDir.XYZ() * nextStep();
5417 const SMDS_MeshNode * newNode = mesh->AddNode( p.X(), p.Y(), p.Z() );
5418 newNodes.push_back( newNode );
5423 //=======================================================================
5424 //function : ExtrusParam::makeNodesByDirAndSew
5425 //purpose : create nodes for standard extrusion with sewing
5426 //=======================================================================
5428 int SMESH_MeshEditor::ExtrusParam::
5429 makeNodesByDirAndSew( SMESHDS_Mesh* mesh,
5430 const SMDS_MeshNode* srcNode,
5431 std::list<const SMDS_MeshNode*> & newNodes,
5432 const bool makeMediumNodes)
5434 gp_XYZ P1 = SMESH_TNodeXYZ( srcNode );
5437 for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5439 P1 += myDir.XYZ() * nextStep();
5441 // try to search in sequence of existing nodes
5442 // if myNodes.Length()>0 we 'nave to use given sequence
5443 // else - use all nodes of mesh
5444 const SMDS_MeshNode * node = 0;
5445 if ( myNodes.Length() > 0 ) {
5447 for(i=1; i<=myNodes.Length(); i++) {
5448 gp_XYZ P2 = SMESH_TNodeXYZ( myNodes.Value(i) );
5449 if (( P1 - P2 ).SquareModulus() < myTolerance * myTolerance )
5451 node = myNodes.Value(i);
5457 SMDS_NodeIteratorPtr itn = mesh->nodesIterator();
5458 while(itn->more()) {
5459 SMESH_TNodeXYZ P2( itn->next() );
5460 if (( P1 - P2 ).SquareModulus() < myTolerance * myTolerance )
5469 node = mesh->AddNode( P1.X(), P1.Y(), P1.Z() );
5471 newNodes.push_back( node );
5478 //=======================================================================
5479 //function : ExtrusParam::makeNodesByNormal2D
5480 //purpose : create nodes for extrusion using normals of faces
5481 //=======================================================================
5483 int SMESH_MeshEditor::ExtrusParam::
5484 makeNodesByNormal2D( SMESHDS_Mesh* mesh,
5485 const SMDS_MeshNode* srcNode,
5486 std::list<const SMDS_MeshNode*> & newNodes,
5487 const bool makeMediumNodes)
5489 const bool alongAvgNorm = ( myFlags & EXTRUSION_FLAG_BY_AVG_NORMAL );
5491 gp_XYZ p = SMESH_TNodeXYZ( srcNode );
5493 // get normals to faces sharing srcNode
5494 vector< gp_XYZ > norms, baryCenters;
5495 gp_XYZ norm, avgNorm( 0,0,0 );
5496 SMDS_ElemIteratorPtr faceIt = srcNode->GetInverseElementIterator( SMDSAbs_Face );
5497 while ( faceIt->more() )
5499 const SMDS_MeshElement* face = faceIt->next();
5500 if ( myElemsToUse && !myElemsToUse->count( face ))
5502 if ( SMESH_MeshAlgos::FaceNormal( face, norm, /*normalized=*/true ))
5504 norms.push_back( norm );
5506 if ( !alongAvgNorm )
5510 for ( SMDS_ElemIteratorPtr nIt = face->nodesIterator(); nIt->more(); ++nbN )
5511 bc += SMESH_TNodeXYZ( nIt->next() );
5512 baryCenters.push_back( bc / nbN );
5517 if ( norms.empty() ) return 0;
5519 double normSize = avgNorm.Modulus();
5520 if ( normSize < std::numeric_limits<double>::min() )
5523 if ( myFlags & EXTRUSION_FLAG_BY_AVG_NORMAL ) // extrude along avgNorm
5526 return makeNodesByDir( mesh, srcNode, newNodes, makeMediumNodes );
5529 avgNorm /= normSize;
5532 for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5535 double stepSize = nextStep();
5537 if ( norms.size() > 1 )
5539 for ( size_t iF = 0; iF < norms.size(); ++iF ) // loop on faces
5541 // translate plane of a face
5542 baryCenters[ iF ] += norms[ iF ] * stepSize;
5544 // find point of intersection of the face plane located at baryCenters[ iF ]
5545 // and avgNorm located at pNew
5546 double d = -( norms[ iF ] * baryCenters[ iF ]); // d of plane equation ax+by+cz+d=0
5547 double dot = ( norms[ iF ] * avgNorm );
5548 if ( dot < std::numeric_limits<double>::min() )
5549 dot = stepSize * 1e-3;
5550 double step = -( norms[ iF ] * pNew + d ) / dot;
5551 pNew += step * avgNorm;
5556 pNew += stepSize * avgNorm;
5560 const SMDS_MeshNode * newNode = mesh->AddNode( p.X(), p.Y(), p.Z() );
5561 newNodes.push_back( newNode );
5566 //=======================================================================
5567 //function : ExtrusParam::makeNodesByNormal1D
5568 //purpose : create nodes for extrusion using normals of edges
5569 //=======================================================================
5571 int SMESH_MeshEditor::ExtrusParam::
5572 makeNodesByNormal1D( SMESHDS_Mesh* mesh,
5573 const SMDS_MeshNode* srcNode,
5574 std::list<const SMDS_MeshNode*> & newNodes,
5575 const bool makeMediumNodes)
5577 throw SALOME_Exception("Extrusion 1D by Normal not implemented");
5581 //=======================================================================
5582 //function : ExtrusionSweep
5584 //=======================================================================
5586 SMESH_MeshEditor::PGroupIDs
5587 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet theElems[2],
5588 const gp_Vec& theStep,
5589 const int theNbSteps,
5590 TTElemOfElemListMap& newElemsMap,
5592 const double theTolerance)
5594 ExtrusParam aParams( theStep, theNbSteps, theFlags, theTolerance );
5595 return ExtrusionSweep( theElems, aParams, newElemsMap );
5599 //=======================================================================
5600 //function : ExtrusionSweep
5602 //=======================================================================
5604 SMESH_MeshEditor::PGroupIDs
5605 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet theElemSets[2],
5606 ExtrusParam& theParams,
5607 TTElemOfElemListMap& newElemsMap)
5609 myLastCreatedElems.Clear();
5610 myLastCreatedNodes.Clear();
5612 // source elements for each generated one
5613 SMESH_SequenceOfElemPtr srcElems, srcNodes;
5615 SMESHDS_Mesh* aMesh = GetMeshDS();
5617 setElemsFirst( theElemSets );
5618 const int nbSteps = theParams.NbSteps();
5619 theParams.SetElementsToUse( theElemSets[0] );
5621 TNodeOfNodeListMap mapNewNodes;
5622 //TNodeOfNodeVecMap mapNewNodes;
5623 TElemOfVecOfNnlmiMap mapElemNewNodes;
5624 //TElemOfVecOfMapNodesMap mapElemNewNodes;
5626 const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
5627 myMesh->NbFaces(ORDER_QUADRATIC) +
5628 myMesh->NbVolumes(ORDER_QUADRATIC) );
5630 TIDSortedElemSet::iterator itElem;
5631 for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
5633 TIDSortedElemSet& theElems = theElemSets[ is2ndSet ];
5634 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
5636 // check element type
5637 const SMDS_MeshElement* elem = *itElem;
5638 if ( !elem || elem->GetType() == SMDSAbs_Volume )
5641 const size_t nbNodes = elem->NbNodes();
5642 vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5643 newNodesItVec.reserve( nbNodes );
5645 // loop on elem nodes
5646 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5647 while ( itN->more() )
5649 // check if a node has been already sweeped
5650 const SMDS_MeshNode* node = cast2Node( itN->next() );
5651 TNodeOfNodeListMap::iterator nIt =
5652 mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5653 list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5654 if ( listNewNodes.empty() )
5658 // check if we are to create medium nodes between corner ones
5659 bool needMediumNodes = false;
5660 if ( isQuadraticMesh )
5662 SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
5663 while (it->more() && !needMediumNodes )
5665 const SMDS_MeshElement* invElem = it->next();
5666 if ( invElem != elem && !theElems.count( invElem )) continue;
5667 needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
5668 if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
5669 needMediumNodes = true;
5672 // create nodes for all steps
5673 if ( theParams.MakeNodes( GetMeshDS(), node, listNewNodes, needMediumNodes ))
5675 list<const SMDS_MeshNode*>::iterator newNodesIt = listNewNodes.begin();
5676 for ( ; newNodesIt != listNewNodes.end(); ++newNodesIt )
5678 myLastCreatedNodes.Append( *newNodesIt );
5679 srcNodes.Append( node );
5684 break; // newNodesItVec will be shorter than nbNodes
5687 newNodesItVec.push_back( nIt );
5689 // make new elements
5690 if ( newNodesItVec.size() == nbNodes )
5691 sweepElement( elem, newNodesItVec, newElemsMap[elem], nbSteps, srcElems );
5695 if ( theParams.ToMakeBoundary() ) {
5696 makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElemSets[0], nbSteps, srcElems );
5698 PGroupIDs newGroupIDs;
5699 if ( theParams.ToMakeGroups() )
5700 newGroupIDs = generateGroups( srcNodes, srcElems, "extruded");
5705 //=======================================================================
5706 //function : ExtrusionAlongTrack
5708 //=======================================================================
5709 SMESH_MeshEditor::Extrusion_Error
5710 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet theElements[2],
5711 SMESH_subMesh* theTrack,
5712 const SMDS_MeshNode* theN1,
5713 const bool theHasAngles,
5714 list<double>& theAngles,
5715 const bool theLinearVariation,
5716 const bool theHasRefPoint,
5717 const gp_Pnt& theRefPoint,
5718 const bool theMakeGroups)
5720 MESSAGE("ExtrusionAlongTrack");
5721 myLastCreatedElems.Clear();
5722 myLastCreatedNodes.Clear();
5725 std::list<double> aPrms;
5726 TIDSortedElemSet::iterator itElem;
5729 TopoDS_Edge aTrackEdge;
5730 TopoDS_Vertex aV1, aV2;
5732 SMDS_ElemIteratorPtr aItE;
5733 SMDS_NodeIteratorPtr aItN;
5734 SMDSAbs_ElementType aTypeE;
5736 TNodeOfNodeListMap mapNewNodes;
5739 aNbE = theElements[0].size() + theElements[1].size();
5742 return EXTR_NO_ELEMENTS;
5744 // 1.1 Track Pattern
5747 SMESHDS_SubMesh* pSubMeshDS = theTrack->GetSubMeshDS();
5749 aItE = pSubMeshDS->GetElements();
5750 while ( aItE->more() ) {
5751 const SMDS_MeshElement* pE = aItE->next();
5752 aTypeE = pE->GetType();
5753 // Pattern must contain links only
5754 if ( aTypeE != SMDSAbs_Edge )
5755 return EXTR_PATH_NOT_EDGE;
5758 list<SMESH_MeshEditor_PathPoint> fullList;
5760 const TopoDS_Shape& aS = theTrack->GetSubShape();
5761 // Sub-shape for the Pattern must be an Edge or Wire
5762 if( aS.ShapeType() == TopAbs_EDGE ) {
5763 aTrackEdge = TopoDS::Edge( aS );
5764 // the Edge must not be degenerated
5765 if ( SMESH_Algo::isDegenerated( aTrackEdge ) )
5766 return EXTR_BAD_PATH_SHAPE;
5767 TopExp::Vertices( aTrackEdge, aV1, aV2 );
5768 aItN = theTrack->GetFather()->GetSubMesh( aV1 )->GetSubMeshDS()->GetNodes();
5769 const SMDS_MeshNode* aN1 = aItN->next();
5770 aItN = theTrack->GetFather()->GetSubMesh( aV2 )->GetSubMeshDS()->GetNodes();
5771 const SMDS_MeshNode* aN2 = aItN->next();
5772 // starting node must be aN1 or aN2
5773 if ( !( aN1 == theN1 || aN2 == theN1 ) )
5774 return EXTR_BAD_STARTING_NODE;
5775 aItN = pSubMeshDS->GetNodes();
5776 while ( aItN->more() ) {
5777 const SMDS_MeshNode* pNode = aItN->next();
5778 const SMDS_EdgePosition* pEPos =
5779 static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
5780 double aT = pEPos->GetUParameter();
5781 aPrms.push_back( aT );
5783 //Extrusion_Error err =
5784 MakeEdgePathPoints(aPrms, aTrackEdge, (aN1==theN1), fullList);
5785 } else if( aS.ShapeType() == TopAbs_WIRE ) {
5786 list< SMESH_subMesh* > LSM;
5787 TopTools_SequenceOfShape Edges;
5788 SMESH_subMeshIteratorPtr itSM = theTrack->getDependsOnIterator(false,true);
5789 while(itSM->more()) {
5790 SMESH_subMesh* SM = itSM->next();
5792 const TopoDS_Shape& aS = SM->GetSubShape();
5795 list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
5796 int startNid = theN1->GetID();
5797 TColStd_MapOfInteger UsedNums;
5799 int NbEdges = Edges.Length();
5801 for(; i<=NbEdges; i++) {
5803 list< SMESH_subMesh* >::iterator itLSM = LSM.begin();
5804 for(; itLSM!=LSM.end(); itLSM++) {
5806 if(UsedNums.Contains(k)) continue;
5807 aTrackEdge = TopoDS::Edge( Edges.Value(k) );
5808 SMESH_subMesh* locTrack = *itLSM;
5809 SMESHDS_SubMesh* locMeshDS = locTrack->GetSubMeshDS();
5810 TopExp::Vertices( aTrackEdge, aV1, aV2 );
5811 aItN = locTrack->GetFather()->GetSubMesh(aV1)->GetSubMeshDS()->GetNodes();
5812 const SMDS_MeshNode* aN1 = aItN->next();
5813 aItN = locTrack->GetFather()->GetSubMesh(aV2)->GetSubMeshDS()->GetNodes();
5814 const SMDS_MeshNode* aN2 = aItN->next();
5815 // starting node must be aN1 or aN2
5816 if ( !( aN1->GetID() == startNid || aN2->GetID() == startNid ) ) continue;
5817 // 2. Collect parameters on the track edge
5819 aItN = locMeshDS->GetNodes();
5820 while ( aItN->more() ) {
5821 const SMDS_MeshNode* pNode = aItN->next();
5822 const SMDS_EdgePosition* pEPos =
5823 static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
5824 double aT = pEPos->GetUParameter();
5825 aPrms.push_back( aT );
5827 list<SMESH_MeshEditor_PathPoint> LPP;
5828 //Extrusion_Error err =
5829 MakeEdgePathPoints(aPrms, aTrackEdge,(aN1->GetID()==startNid), LPP);
5830 LLPPs.push_back(LPP);
5832 // update startN for search following egde
5833 if( aN1->GetID() == startNid ) startNid = aN2->GetID();
5834 else startNid = aN1->GetID();
5838 list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
5839 list<SMESH_MeshEditor_PathPoint> firstList = *itLLPP;
5840 list<SMESH_MeshEditor_PathPoint>::iterator itPP = firstList.begin();
5841 for(; itPP!=firstList.end(); itPP++) {
5842 fullList.push_back( *itPP );
5844 SMESH_MeshEditor_PathPoint PP1 = fullList.back();
5845 fullList.pop_back();
5847 for(; itLLPP!=LLPPs.end(); itLLPP++) {
5848 list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
5849 itPP = currList.begin();
5850 SMESH_MeshEditor_PathPoint PP2 = currList.front();
5851 gp_Dir D1 = PP1.Tangent();
5852 gp_Dir D2 = PP2.Tangent();
5853 gp_Dir Dnew( gp_Vec( (D1.X()+D2.X())/2, (D1.Y()+D2.Y())/2,
5854 (D1.Z()+D2.Z())/2 ) );
5855 PP1.SetTangent(Dnew);
5856 fullList.push_back(PP1);
5858 for(; itPP!=firstList.end(); itPP++) {
5859 fullList.push_back( *itPP );
5861 PP1 = fullList.back();
5862 fullList.pop_back();
5864 // if wire not closed
5865 fullList.push_back(PP1);
5869 return EXTR_BAD_PATH_SHAPE;
5872 return MakeExtrElements(theElements, fullList, theHasAngles, theAngles, theLinearVariation,
5873 theHasRefPoint, theRefPoint, theMakeGroups);
5877 //=======================================================================
5878 //function : ExtrusionAlongTrack
5880 //=======================================================================
5881 SMESH_MeshEditor::Extrusion_Error
5882 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet theElements[2],
5883 SMESH_Mesh* theTrack,
5884 const SMDS_MeshNode* theN1,
5885 const bool theHasAngles,
5886 list<double>& theAngles,
5887 const bool theLinearVariation,
5888 const bool theHasRefPoint,
5889 const gp_Pnt& theRefPoint,
5890 const bool theMakeGroups)
5892 myLastCreatedElems.Clear();
5893 myLastCreatedNodes.Clear();
5896 std::list<double> aPrms;
5897 TIDSortedElemSet::iterator itElem;
5900 TopoDS_Edge aTrackEdge;
5901 TopoDS_Vertex aV1, aV2;
5903 SMDS_ElemIteratorPtr aItE;
5904 SMDS_NodeIteratorPtr aItN;
5905 SMDSAbs_ElementType aTypeE;
5907 TNodeOfNodeListMap mapNewNodes;
5910 aNbE = theElements[0].size() + theElements[1].size();
5913 return EXTR_NO_ELEMENTS;
5915 // 1.1 Track Pattern
5918 SMESHDS_Mesh* pMeshDS = theTrack->GetMeshDS();
5920 aItE = pMeshDS->elementsIterator();
5921 while ( aItE->more() ) {
5922 const SMDS_MeshElement* pE = aItE->next();
5923 aTypeE = pE->GetType();
5924 // Pattern must contain links only
5925 if ( aTypeE != SMDSAbs_Edge )
5926 return EXTR_PATH_NOT_EDGE;
5929 list<SMESH_MeshEditor_PathPoint> fullList;
5931 const TopoDS_Shape& aS = theTrack->GetShapeToMesh();
5933 if ( !theTrack->HasShapeToMesh() ) {
5934 //Mesh without shape
5935 const SMDS_MeshNode* currentNode = NULL;
5936 const SMDS_MeshNode* prevNode = theN1;
5937 std::vector<const SMDS_MeshNode*> aNodesList;
5938 aNodesList.push_back(theN1);
5939 int nbEdges = 0, conn=0;
5940 const SMDS_MeshElement* prevElem = NULL;
5941 const SMDS_MeshElement* currentElem = NULL;
5942 int totalNbEdges = theTrack->NbEdges();
5943 SMDS_ElemIteratorPtr nIt;
5946 if( !theTrack->GetMeshDS()->Contains(theN1) ) {
5947 return EXTR_BAD_STARTING_NODE;
5950 conn = nbEdgeConnectivity(theN1);
5952 return EXTR_PATH_NOT_EDGE;
5954 aItE = theN1->GetInverseElementIterator();
5955 prevElem = aItE->next();
5956 currentElem = prevElem;
5958 if(totalNbEdges == 1 ) {
5959 nIt = currentElem->nodesIterator();
5960 currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
5961 if(currentNode == prevNode)
5962 currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
5963 aNodesList.push_back(currentNode);
5965 nIt = currentElem->nodesIterator();
5966 while( nIt->more() ) {
5967 currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
5968 if(currentNode == prevNode)
5969 currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
5970 aNodesList.push_back(currentNode);
5972 //case of the closed mesh
5973 if(currentNode == theN1) {
5978 conn = nbEdgeConnectivity(currentNode);
5980 return EXTR_PATH_NOT_EDGE;
5981 }else if( conn == 1 && nbEdges > 0 ) {
5986 prevNode = currentNode;
5987 aItE = currentNode->GetInverseElementIterator();
5988 currentElem = aItE->next();
5989 if( currentElem == prevElem)
5990 currentElem = aItE->next();
5991 nIt = currentElem->nodesIterator();
5992 prevElem = currentElem;
5998 if(nbEdges != totalNbEdges)
5999 return EXTR_PATH_NOT_EDGE;
6001 TopTools_SequenceOfShape Edges;
6002 double x1,x2,y1,y2,z1,z2;
6003 list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
6004 int startNid = theN1->GetID();
6005 for(int i = 1; i < aNodesList.size(); i++) {
6006 x1 = aNodesList[i-1]->X();x2 = aNodesList[i]->X();
6007 y1 = aNodesList[i-1]->Y();y2 = aNodesList[i]->Y();
6008 z1 = aNodesList[i-1]->Z();z2 = aNodesList[i]->Z();
6009 TopoDS_Edge e = BRepBuilderAPI_MakeEdge(gp_Pnt(x1,y1,z1),gp_Pnt(x2,y2,z2));
6010 list<SMESH_MeshEditor_PathPoint> LPP;
6012 MakeEdgePathPoints(aPrms, e, (aNodesList[i-1]->GetID()==startNid), LPP);
6013 LLPPs.push_back(LPP);
6014 if( aNodesList[i-1]->GetID() == startNid ) startNid = aNodesList[i]->GetID();
6015 else startNid = aNodesList[i-1]->GetID();
6019 list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
6020 list<SMESH_MeshEditor_PathPoint> firstList = *itLLPP;
6021 list<SMESH_MeshEditor_PathPoint>::iterator itPP = firstList.begin();
6022 for(; itPP!=firstList.end(); itPP++) {
6023 fullList.push_back( *itPP );
6026 SMESH_MeshEditor_PathPoint PP1 = fullList.back();
6027 SMESH_MeshEditor_PathPoint PP2;
6028 fullList.pop_back();
6030 for(; itLLPP!=LLPPs.end(); itLLPP++) {
6031 list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
6032 itPP = currList.begin();
6033 PP2 = currList.front();
6034 gp_Dir D1 = PP1.Tangent();
6035 gp_Dir D2 = PP2.Tangent();
6036 gp_Dir Dnew( gp_Vec( (D1.X()+D2.X())/2, (D1.Y()+D2.Y())/2,
6037 (D1.Z()+D2.Z())/2 ) );
6038 PP1.SetTangent(Dnew);
6039 fullList.push_back(PP1);
6041 for(; itPP!=currList.end(); itPP++) {
6042 fullList.push_back( *itPP );
6044 PP1 = fullList.back();
6045 fullList.pop_back();
6047 fullList.push_back(PP1);
6049 } // Sub-shape for the Pattern must be an Edge or Wire
6050 else if( aS.ShapeType() == TopAbs_EDGE ) {
6051 aTrackEdge = TopoDS::Edge( aS );
6052 // the Edge must not be degenerated
6053 if ( SMESH_Algo::isDegenerated( aTrackEdge ) )
6054 return EXTR_BAD_PATH_SHAPE;
6055 TopExp::Vertices( aTrackEdge, aV1, aV2 );
6056 const SMDS_MeshNode* aN1 = SMESH_Algo::VertexNode( aV1, pMeshDS );
6057 const SMDS_MeshNode* aN2 = SMESH_Algo::VertexNode( aV2, pMeshDS );
6058 // starting node must be aN1 or aN2
6059 if ( !( aN1 == theN1 || aN2 == theN1 ) )
6060 return EXTR_BAD_STARTING_NODE;
6061 aItN = pMeshDS->nodesIterator();
6062 while ( aItN->more() ) {
6063 const SMDS_MeshNode* pNode = aItN->next();
6064 if( pNode==aN1 || pNode==aN2 ) continue;
6065 const SMDS_EdgePosition* pEPos =
6066 static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
6067 double aT = pEPos->GetUParameter();
6068 aPrms.push_back( aT );
6070 //Extrusion_Error err =
6071 MakeEdgePathPoints(aPrms, aTrackEdge, (aN1==theN1), fullList);
6073 else if( aS.ShapeType() == TopAbs_WIRE ) {
6074 list< SMESH_subMesh* > LSM;
6075 TopTools_SequenceOfShape Edges;
6076 TopExp_Explorer eExp(aS, TopAbs_EDGE);
6077 for(; eExp.More(); eExp.Next()) {
6078 TopoDS_Edge E = TopoDS::Edge( eExp.Current() );
6079 if( SMESH_Algo::isDegenerated(E) ) continue;
6080 SMESH_subMesh* SM = theTrack->GetSubMesh(E);
6086 list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
6087 TopoDS_Vertex aVprev;
6088 TColStd_MapOfInteger UsedNums;
6089 int NbEdges = Edges.Length();
6091 for(; i<=NbEdges; i++) {
6093 list< SMESH_subMesh* >::iterator itLSM = LSM.begin();
6094 for(; itLSM!=LSM.end(); itLSM++) {
6096 if(UsedNums.Contains(k)) continue;
6097 aTrackEdge = TopoDS::Edge( Edges.Value(k) );
6098 SMESH_subMesh* locTrack = *itLSM;
6099 SMESHDS_SubMesh* locMeshDS = locTrack->GetSubMeshDS();
6100 TopExp::Vertices( aTrackEdge, aV1, aV2 );
6101 bool aN1isOK = false, aN2isOK = false;
6102 if ( aVprev.IsNull() ) {
6103 // if previous vertex is not yet defined, it means that we in the beginning of wire
6104 // and we have to find initial vertex corresponding to starting node theN1
6105 const SMDS_MeshNode* aN1 = SMESH_Algo::VertexNode( aV1, pMeshDS );
6106 const SMDS_MeshNode* aN2 = SMESH_Algo::VertexNode( aV2, pMeshDS );
6107 // starting node must be aN1 or aN2
6108 aN1isOK = ( aN1 && aN1 == theN1 );
6109 aN2isOK = ( aN2 && aN2 == theN1 );
6112 // we have specified ending vertex of the previous edge on the previous iteration
6113 // and we have just to check that it corresponds to any vertex in current segment
6114 aN1isOK = aVprev.IsSame( aV1 );
6115 aN2isOK = aVprev.IsSame( aV2 );
6117 if ( !aN1isOK && !aN2isOK ) continue;
6118 // 2. Collect parameters on the track edge
6120 aItN = locMeshDS->GetNodes();
6121 while ( aItN->more() ) {
6122 const SMDS_MeshNode* pNode = aItN->next();
6123 const SMDS_EdgePosition* pEPos =
6124 static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
6125 double aT = pEPos->GetUParameter();
6126 aPrms.push_back( aT );
6128 list<SMESH_MeshEditor_PathPoint> LPP;
6129 //Extrusion_Error err =
6130 MakeEdgePathPoints(aPrms, aTrackEdge, aN1isOK, LPP);
6131 LLPPs.push_back(LPP);
6133 // update startN for search following egde
6134 if ( aN1isOK ) aVprev = aV2;
6139 list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
6140 list<SMESH_MeshEditor_PathPoint>& firstList = *itLLPP;
6141 fullList.splice( fullList.end(), firstList );
6143 SMESH_MeshEditor_PathPoint PP1 = fullList.back();
6144 fullList.pop_back();
6146 for(; itLLPP!=LLPPs.end(); itLLPP++) {
6147 list<SMESH_MeshEditor_PathPoint>& currList = *itLLPP;
6148 SMESH_MeshEditor_PathPoint PP2 = currList.front();
6149 gp_Dir D1 = PP1.Tangent();
6150 gp_Dir D2 = PP2.Tangent();
6151 gp_Dir Dnew( ( D1.XYZ() + D2.XYZ() ) / 2 );
6152 PP1.SetTangent(Dnew);
6153 fullList.push_back(PP1);
6154 fullList.splice( fullList.end(), currList, ++currList.begin(), currList.end() );
6155 PP1 = fullList.back();
6156 fullList.pop_back();
6158 // if wire not closed
6159 fullList.push_back(PP1);
6163 return EXTR_BAD_PATH_SHAPE;
6166 return MakeExtrElements(theElements, fullList, theHasAngles, theAngles, theLinearVariation,
6167 theHasRefPoint, theRefPoint, theMakeGroups);
6171 //=======================================================================
6172 //function : MakeEdgePathPoints
6173 //purpose : auxilary for ExtrusionAlongTrack
6174 //=======================================================================
6175 SMESH_MeshEditor::Extrusion_Error
6176 SMESH_MeshEditor::MakeEdgePathPoints(std::list<double>& aPrms,
6177 const TopoDS_Edge& aTrackEdge,
6179 list<SMESH_MeshEditor_PathPoint>& LPP)
6181 Standard_Real aTx1, aTx2, aL2, aTolVec, aTolVec2;
6183 aTolVec2=aTolVec*aTolVec;
6185 TopoDS_Vertex aV1, aV2;
6186 TopExp::Vertices( aTrackEdge, aV1, aV2 );
6187 aT1=BRep_Tool::Parameter( aV1, aTrackEdge );
6188 aT2=BRep_Tool::Parameter( aV2, aTrackEdge );
6189 // 2. Collect parameters on the track edge
6190 aPrms.push_front( aT1 );
6191 aPrms.push_back( aT2 );
6194 if( FirstIsStart ) {
6205 SMESH_MeshEditor_PathPoint aPP;
6206 Handle(Geom_Curve) aC3D = BRep_Tool::Curve( aTrackEdge, aTx1, aTx2 );
6207 std::list<double>::iterator aItD = aPrms.begin();
6208 for(; aItD != aPrms.end(); ++aItD) {
6212 aC3D->D1( aT, aP3D, aVec );
6213 aL2 = aVec.SquareMagnitude();
6214 if ( aL2 < aTolVec2 )
6215 return EXTR_CANT_GET_TANGENT;
6216 gp_Dir aTgt( aVec );
6218 aPP.SetTangent( aTgt );
6219 aPP.SetParameter( aT );
6226 //=======================================================================
6227 //function : MakeExtrElements
6228 //purpose : auxilary for ExtrusionAlongTrack
6229 //=======================================================================
6230 SMESH_MeshEditor::Extrusion_Error
6231 SMESH_MeshEditor::MakeExtrElements(TIDSortedElemSet theElemSets[2],
6232 list<SMESH_MeshEditor_PathPoint>& fullList,
6233 const bool theHasAngles,
6234 list<double>& theAngles,
6235 const bool theLinearVariation,
6236 const bool theHasRefPoint,
6237 const gp_Pnt& theRefPoint,
6238 const bool theMakeGroups)
6240 const int aNbTP = fullList.size();
6242 if( theHasAngles && !theAngles.empty() && theLinearVariation )
6243 LinearAngleVariation(aNbTP-1, theAngles);
6244 // fill vector of path points with angles
6245 vector<SMESH_MeshEditor_PathPoint> aPPs;
6246 list<SMESH_MeshEditor_PathPoint>::iterator itPP = fullList.begin();
6247 list<double>::iterator itAngles = theAngles.begin();
6248 aPPs.push_back( *itPP++ );
6249 for( ; itPP != fullList.end(); itPP++) {
6250 aPPs.push_back( *itPP );
6251 if ( theHasAngles && itAngles != theAngles.end() )
6252 aPPs.back().SetAngle( *itAngles++ );
6255 TNodeOfNodeListMap mapNewNodes;
6256 TElemOfVecOfNnlmiMap mapElemNewNodes;
6257 TTElemOfElemListMap newElemsMap;
6258 TIDSortedElemSet::iterator itElem;
6259 // source elements for each generated one
6260 SMESH_SequenceOfElemPtr srcElems, srcNodes;
6262 // 3. Center of rotation aV0
6263 gp_Pnt aV0 = theRefPoint;
6264 if ( !theHasRefPoint )
6266 gp_XYZ aGC( 0.,0.,0. );
6267 TIDSortedElemSet newNodes;
6269 for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
6271 TIDSortedElemSet& theElements = theElemSets[ is2ndSet ];
6272 itElem = theElements.begin();
6273 for ( ; itElem != theElements.end(); itElem++ ) {
6274 const SMDS_MeshElement* elem = *itElem;
6276 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6277 while ( itN->more() ) {
6278 const SMDS_MeshElement* node = itN->next();
6279 if ( newNodes.insert( node ).second )
6280 aGC += SMESH_TNodeXYZ( node );
6284 aGC /= newNodes.size();
6286 } // if (!theHasRefPoint) {
6288 // 4. Processing the elements
6289 SMESHDS_Mesh* aMesh = GetMeshDS();
6291 setElemsFirst( theElemSets );
6292 for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
6294 TIDSortedElemSet& theElements = theElemSets[ is2ndSet ];
6295 for ( itElem = theElements.begin(); itElem != theElements.end(); itElem++ ) {
6296 // check element type
6297 const SMDS_MeshElement* elem = *itElem;
6300 // SMDSAbs_ElementType aTypeE = elem->GetType();
6301 // if ( aTypeE != SMDSAbs_Face && aTypeE != SMDSAbs_Edge )
6304 vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
6305 newNodesItVec.reserve( elem->NbNodes() );
6307 // loop on elem nodes
6309 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6310 while ( itN->more() )
6313 // check if a node has been already processed
6314 const SMDS_MeshNode* node =
6315 static_cast<const SMDS_MeshNode*>( itN->next() );
6316 TNodeOfNodeListMap::iterator nIt = mapNewNodes.find( node );
6317 if ( nIt == mapNewNodes.end() ) {
6318 nIt = mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
6319 list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
6322 Standard_Real aAngle1x, aAngleT1T0, aTolAng;
6323 gp_Pnt aP0x, aP1x, aPN0, aPN1, aV0x, aV1x;
6324 gp_Ax1 anAx1, anAxT1T0;
6325 gp_Dir aDT1x, aDT0x, aDT1T0;
6330 aPN0 = SMESH_TNodeXYZ( node );
6332 const SMESH_MeshEditor_PathPoint& aPP0 = aPPs[0];
6334 aDT0x= aPP0.Tangent();
6335 //cout<<"j = 0 PP: Pnt("<<aP0x.X()<<","<<aP0x.Y()<<","<<aP0x.Z()<<")"<<endl;
6337 for ( int j = 1; j < aNbTP; ++j ) {
6338 const SMESH_MeshEditor_PathPoint& aPP1 = aPPs[j];
6340 aDT1x = aPP1.Tangent();
6341 aAngle1x = aPP1.Angle();
6343 gp_Trsf aTrsf, aTrsfRot, aTrsfRotT1T0;
6345 gp_Vec aV01x( aP0x, aP1x );
6346 aTrsf.SetTranslation( aV01x );
6349 aV1x = aV0x.Transformed( aTrsf );
6350 aPN1 = aPN0.Transformed( aTrsf );
6352 // rotation 1 [ T1,T0 ]
6353 aAngleT1T0=-aDT1x.Angle( aDT0x );
6354 if (fabs(aAngleT1T0) > aTolAng) {
6356 anAxT1T0.SetLocation( aV1x );
6357 anAxT1T0.SetDirection( aDT1T0 );
6358 aTrsfRotT1T0.SetRotation( anAxT1T0, aAngleT1T0 );
6360 aPN1 = aPN1.Transformed( aTrsfRotT1T0 );
6364 if ( theHasAngles ) {
6365 anAx1.SetLocation( aV1x );
6366 anAx1.SetDirection( aDT1x );
6367 aTrsfRot.SetRotation( anAx1, aAngle1x );
6369 aPN1 = aPN1.Transformed( aTrsfRot );
6373 //MESSAGE("elem->IsQuadratic " << elem->IsQuadratic() << " " << elem->IsMediumNode(node));
6374 if( elem->IsQuadratic() && !elem->IsMediumNode(node) ) {
6375 // create additional node
6376 double x = ( aPN1.X() + aPN0.X() )/2.;
6377 double y = ( aPN1.Y() + aPN0.Y() )/2.;
6378 double z = ( aPN1.Z() + aPN0.Z() )/2.;
6379 const SMDS_MeshNode* newNode = aMesh->AddNode(x,y,z);
6380 myLastCreatedNodes.Append(newNode);
6381 srcNodes.Append( node );
6382 listNewNodes.push_back( newNode );
6384 const SMDS_MeshNode* newNode = aMesh->AddNode( aPN1.X(), aPN1.Y(), aPN1.Z() );
6385 myLastCreatedNodes.Append(newNode);
6386 srcNodes.Append( node );
6387 listNewNodes.push_back( newNode );
6397 // if current elem is quadratic and current node is not medium
6398 // we have to check - may be it is needed to insert additional nodes
6399 if( elem->IsQuadratic() && !elem->IsMediumNode(node) ) {
6400 list< const SMDS_MeshNode* > & listNewNodes = nIt->second;
6401 if(listNewNodes.size()==aNbTP-1) {
6402 vector<const SMDS_MeshNode*> aNodes(2*(aNbTP-1));
6403 gp_XYZ P(node->X(), node->Y(), node->Z());
6404 list< const SMDS_MeshNode* >::iterator it = listNewNodes.begin();
6406 for(i=0; i<aNbTP-1; i++) {
6407 const SMDS_MeshNode* N = *it;
6408 double x = ( N->X() + P.X() )/2.;
6409 double y = ( N->Y() + P.Y() )/2.;
6410 double z = ( N->Z() + P.Z() )/2.;
6411 const SMDS_MeshNode* newN = aMesh->AddNode(x,y,z);
6412 srcNodes.Append( node );
6413 myLastCreatedNodes.Append(newN);
6416 P = gp_XYZ(N->X(),N->Y(),N->Z());
6418 listNewNodes.clear();
6419 for(i=0; i<2*(aNbTP-1); i++) {
6420 listNewNodes.push_back(aNodes[i]);
6426 newNodesItVec.push_back( nIt );
6428 // make new elements
6429 //sweepElement( aMesh, elem, newNodesItVec, newElemsMap[elem],
6430 // newNodesItVec[0]->second.size(), myLastCreatedElems );
6431 sweepElement( elem, newNodesItVec, newElemsMap[elem], aNbTP-1, srcElems );
6435 makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElemSets[0], aNbTP-1, srcElems );
6437 if ( theMakeGroups )
6438 generateGroups( srcNodes, srcElems, "extruded");
6444 //=======================================================================
6445 //function : LinearAngleVariation
6446 //purpose : auxilary for ExtrusionAlongTrack
6447 //=======================================================================
6448 void SMESH_MeshEditor::LinearAngleVariation(const int nbSteps,
6449 list<double>& Angles)
6451 int nbAngles = Angles.size();
6452 if( nbSteps > nbAngles ) {
6453 vector<double> theAngles(nbAngles);
6454 list<double>::iterator it = Angles.begin();
6456 for(; it!=Angles.end(); it++) {
6458 theAngles[i] = (*it);
6461 double rAn2St = double( nbAngles ) / double( nbSteps );
6462 double angPrev = 0, angle;
6463 for ( int iSt = 0; iSt < nbSteps; ++iSt ) {
6464 double angCur = rAn2St * ( iSt+1 );
6465 double angCurFloor = floor( angCur );
6466 double angPrevFloor = floor( angPrev );
6467 if ( angPrevFloor == angCurFloor )
6468 angle = rAn2St * theAngles[ int( angCurFloor ) ];
6470 int iP = int( angPrevFloor );
6471 double angPrevCeil = ceil(angPrev);
6472 angle = ( angPrevCeil - angPrev ) * theAngles[ iP ];
6474 int iC = int( angCurFloor );
6475 if ( iC < nbAngles )
6476 angle += ( angCur - angCurFloor ) * theAngles[ iC ];
6478 iP = int( angPrevCeil );
6480 angle += theAngles[ iC ];
6482 res.push_back(angle);
6487 for(; it!=res.end(); it++)
6488 Angles.push_back( *it );
6493 //================================================================================
6495 * \brief Move or copy theElements applying theTrsf to their nodes
6496 * \param theElems - elements to transform, if theElems is empty then apply to all mesh nodes
6497 * \param theTrsf - transformation to apply
6498 * \param theCopy - if true, create translated copies of theElems
6499 * \param theMakeGroups - if true and theCopy, create translated groups
6500 * \param theTargetMesh - mesh to copy translated elements into
6501 * \return SMESH_MeshEditor::PGroupIDs - list of ids of created groups
6503 //================================================================================
6505 SMESH_MeshEditor::PGroupIDs
6506 SMESH_MeshEditor::Transform (TIDSortedElemSet & theElems,
6507 const gp_Trsf& theTrsf,
6509 const bool theMakeGroups,
6510 SMESH_Mesh* theTargetMesh)
6512 myLastCreatedElems.Clear();
6513 myLastCreatedNodes.Clear();
6515 bool needReverse = false;
6516 string groupPostfix;
6517 switch ( theTrsf.Form() ) {
6519 MESSAGE("gp_PntMirror");
6521 groupPostfix = "mirrored";
6524 MESSAGE("gp_Ax1Mirror");
6525 groupPostfix = "mirrored";
6528 MESSAGE("gp_Ax2Mirror");
6530 groupPostfix = "mirrored";
6533 MESSAGE("gp_Rotation");
6534 groupPostfix = "rotated";
6536 case gp_Translation:
6537 MESSAGE("gp_Translation");
6538 groupPostfix = "translated";
6541 MESSAGE("gp_Scale");
6542 groupPostfix = "scaled";
6544 case gp_CompoundTrsf: // different scale by axis
6545 MESSAGE("gp_CompoundTrsf");
6546 groupPostfix = "scaled";
6550 needReverse = false;
6551 groupPostfix = "transformed";
6554 SMESH_MeshEditor targetMeshEditor( theTargetMesh );
6555 SMESHDS_Mesh* aTgtMesh = theTargetMesh ? theTargetMesh->GetMeshDS() : 0;
6556 SMESHDS_Mesh* aMesh = GetMeshDS();
6559 // map old node to new one
6560 TNodeNodeMap nodeMap;
6562 // elements sharing moved nodes; those of them which have all
6563 // nodes mirrored but are not in theElems are to be reversed
6564 TIDSortedElemSet inverseElemSet;
6566 // source elements for each generated one
6567 SMESH_SequenceOfElemPtr srcElems, srcNodes;
6569 // issue 021015: EDF 1578 SMESH: Free nodes are removed when translating a mesh
6570 TIDSortedElemSet orphanNode;
6572 if ( theElems.empty() ) // transform the whole mesh
6575 SMDS_ElemIteratorPtr eIt = aMesh->elementsIterator();
6576 while ( eIt->more() ) theElems.insert( eIt->next() );
6578 SMDS_NodeIteratorPtr nIt = aMesh->nodesIterator();
6579 while ( nIt->more() )
6581 const SMDS_MeshNode* node = nIt->next();
6582 if ( node->NbInverseElements() == 0)
6583 orphanNode.insert( node );
6587 // loop on elements to transform nodes : first orphan nodes then elems
6588 TIDSortedElemSet::iterator itElem;
6589 TIDSortedElemSet *elements[] = {&orphanNode, &theElems };
6590 for (int i=0; i<2; i++)
6591 for ( itElem = elements[i]->begin(); itElem != elements[i]->end(); itElem++ ) {
6592 const SMDS_MeshElement* elem = *itElem;
6596 // loop on elem nodes
6597 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6598 while ( itN->more() ) {
6600 const SMDS_MeshNode* node = cast2Node( itN->next() );
6601 // check if a node has been already transformed
6602 pair<TNodeNodeMap::iterator,bool> n2n_isnew =
6603 nodeMap.insert( make_pair ( node, node ));
6604 if ( !n2n_isnew.second )
6608 coord[0] = node->X();
6609 coord[1] = node->Y();
6610 coord[2] = node->Z();
6611 theTrsf.Transforms( coord[0], coord[1], coord[2] );
6612 if ( theTargetMesh ) {
6613 const SMDS_MeshNode * newNode = aTgtMesh->AddNode( coord[0], coord[1], coord[2] );
6614 n2n_isnew.first->second = newNode;
6615 myLastCreatedNodes.Append(newNode);
6616 srcNodes.Append( node );
6618 else if ( theCopy ) {
6619 const SMDS_MeshNode * newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
6620 n2n_isnew.first->second = newNode;
6621 myLastCreatedNodes.Append(newNode);
6622 srcNodes.Append( node );
6625 aMesh->MoveNode( node, coord[0], coord[1], coord[2] );
6626 // node position on shape becomes invalid
6627 const_cast< SMDS_MeshNode* > ( node )->SetPosition
6628 ( SMDS_SpacePosition::originSpacePosition() );
6631 // keep inverse elements
6632 if ( !theCopy && !theTargetMesh && needReverse ) {
6633 SMDS_ElemIteratorPtr invElemIt = node->GetInverseElementIterator();
6634 while ( invElemIt->more() ) {
6635 const SMDS_MeshElement* iel = invElemIt->next();
6636 inverseElemSet.insert( iel );
6642 // either create new elements or reverse mirrored ones
6643 if ( !theCopy && !needReverse && !theTargetMesh )
6646 TIDSortedElemSet::iterator invElemIt = inverseElemSet.begin();
6647 for ( ; invElemIt != inverseElemSet.end(); invElemIt++ )
6648 theElems.insert( *invElemIt );
6650 // Replicate or reverse elements
6652 std::vector<int> iForw;
6653 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
6655 const SMDS_MeshElement* elem = *itElem;
6656 if ( !elem ) continue;
6658 SMDSAbs_GeometryType geomType = elem->GetGeomType();
6659 int nbNodes = elem->NbNodes();
6660 if ( geomType == SMDSGeom_NONE ) continue; // node
6662 switch ( geomType ) {
6664 case SMDSGeom_POLYGON: // ---------------------- polygon
6666 vector<const SMDS_MeshNode*> poly_nodes (nbNodes);
6668 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6669 while (itN->more()) {
6670 const SMDS_MeshNode* node =
6671 static_cast<const SMDS_MeshNode*>(itN->next());
6672 TNodeNodeMap::iterator nodeMapIt = nodeMap.find(node);
6673 if (nodeMapIt == nodeMap.end())
6674 break; // not all nodes transformed
6676 // reverse mirrored faces and volumes
6677 poly_nodes[nbNodes - iNode - 1] = (*nodeMapIt).second;
6679 poly_nodes[iNode] = (*nodeMapIt).second;
6683 if ( iNode != nbNodes )
6684 continue; // not all nodes transformed
6686 if ( theTargetMesh ) {
6687 myLastCreatedElems.Append(aTgtMesh->AddPolygonalFace(poly_nodes));
6688 srcElems.Append( elem );
6690 else if ( theCopy ) {
6691 myLastCreatedElems.Append(aMesh->AddPolygonalFace(poly_nodes));
6692 srcElems.Append( elem );
6695 aMesh->ChangePolygonNodes(elem, poly_nodes);
6700 case SMDSGeom_POLYHEDRA: // ------------------ polyhedral volume
6702 const SMDS_VtkVolume* aPolyedre =
6703 dynamic_cast<const SMDS_VtkVolume*>( elem );
6705 MESSAGE("Warning: bad volumic element");
6709 vector<const SMDS_MeshNode*> poly_nodes; poly_nodes.reserve( nbNodes );
6710 vector<int> quantities; quantities.reserve( nbNodes );
6712 bool allTransformed = true;
6713 int nbFaces = aPolyedre->NbFaces();
6714 for (int iface = 1; iface <= nbFaces && allTransformed; iface++) {
6715 int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
6716 for (int inode = 1; inode <= nbFaceNodes && allTransformed; inode++) {
6717 const SMDS_MeshNode* node = aPolyedre->GetFaceNode(iface, inode);
6718 TNodeNodeMap::iterator nodeMapIt = nodeMap.find(node);
6719 if (nodeMapIt == nodeMap.end()) {
6720 allTransformed = false; // not all nodes transformed
6722 poly_nodes.push_back((*nodeMapIt).second);
6724 if ( needReverse && allTransformed )
6725 std::reverse( poly_nodes.end() - nbFaceNodes, poly_nodes.end() );
6727 quantities.push_back(nbFaceNodes);
6729 if ( !allTransformed )
6730 continue; // not all nodes transformed
6732 if ( theTargetMesh ) {
6733 myLastCreatedElems.Append(aTgtMesh->AddPolyhedralVolume(poly_nodes, quantities));
6734 srcElems.Append( elem );
6736 else if ( theCopy ) {
6737 myLastCreatedElems.Append(aMesh->AddPolyhedralVolume(poly_nodes, quantities));
6738 srcElems.Append( elem );
6741 aMesh->ChangePolyhedronNodes(elem, poly_nodes, quantities);
6746 case SMDSGeom_BALL: // -------------------- Ball
6748 if ( !theCopy && !theTargetMesh ) continue;
6750 TNodeNodeMap::iterator nodeMapIt = nodeMap.find( elem->GetNode(0) );
6751 if (nodeMapIt == nodeMap.end())
6752 continue; // not all nodes transformed
6754 double diameter = static_cast<const SMDS_BallElement*>(elem)->GetDiameter();
6755 if ( theTargetMesh ) {
6756 myLastCreatedElems.Append(aTgtMesh->AddBall( nodeMapIt->second, diameter ));
6757 srcElems.Append( elem );
6760 myLastCreatedElems.Append(aMesh->AddBall( nodeMapIt->second, diameter ));
6761 srcElems.Append( elem );
6766 default: // ----------------------- Regular elements
6768 while ( iForw.size() < nbNodes ) iForw.push_back( iForw.size() );
6769 const std::vector<int>& iRev = SMDS_MeshCell::reverseSmdsOrder( elem->GetEntityType() );
6770 const std::vector<int>& i = needReverse ? iRev : iForw;
6772 // find transformed nodes
6773 vector<const SMDS_MeshNode*> nodes(nbNodes);
6775 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6776 while ( itN->more() ) {
6777 const SMDS_MeshNode* node =
6778 static_cast<const SMDS_MeshNode*>( itN->next() );
6779 TNodeNodeMap::iterator nodeMapIt = nodeMap.find( node );
6780 if ( nodeMapIt == nodeMap.end() )
6781 break; // not all nodes transformed
6782 nodes[ i [ iNode++ ]] = (*nodeMapIt).second;
6784 if ( iNode != nbNodes )
6785 continue; // not all nodes transformed
6787 if ( theTargetMesh ) {
6788 if ( SMDS_MeshElement* copy =
6789 targetMeshEditor.AddElement( nodes, elem->GetType(), elem->IsPoly() )) {
6790 myLastCreatedElems.Append( copy );
6791 srcElems.Append( elem );
6794 else if ( theCopy ) {
6795 if ( AddElement( nodes, elem->GetType(), elem->IsPoly() ))
6796 srcElems.Append( elem );
6799 // reverse element as it was reversed by transformation
6801 aMesh->ChangeElementNodes( elem, &nodes[0], nbNodes );
6803 } // switch ( geomType )
6805 } // loop on elements
6807 PGroupIDs newGroupIDs;
6809 if ( ( theMakeGroups && theCopy ) ||
6810 ( theMakeGroups && theTargetMesh ) )
6811 newGroupIDs = generateGroups( srcNodes, srcElems, groupPostfix, theTargetMesh, false );
6816 //=======================================================================
6818 * \brief Create groups of elements made during transformation
6819 * \param nodeGens - nodes making corresponding myLastCreatedNodes
6820 * \param elemGens - elements making corresponding myLastCreatedElems
6821 * \param postfix - to append to names of new groups
6822 * \param targetMesh - mesh to create groups in
6823 * \param topPresent - is there "top" elements that are created by sweeping
6825 //=======================================================================
6827 SMESH_MeshEditor::PGroupIDs
6828 SMESH_MeshEditor::generateGroups(const SMESH_SequenceOfElemPtr& nodeGens,
6829 const SMESH_SequenceOfElemPtr& elemGens,
6830 const std::string& postfix,
6831 SMESH_Mesh* targetMesh,
6832 const bool topPresent)
6834 PGroupIDs newGroupIDs( new list<int> );
6835 SMESH_Mesh* mesh = targetMesh ? targetMesh : GetMesh();
6837 // Sort existing groups by types and collect their names
6839 // containers to store an old group and generated new ones;
6840 // 1st new group is for result elems of different type than a source one;
6841 // 2nd new group is for same type result elems ("top" group at extrusion)
6843 using boost::make_tuple;
6844 typedef tuple< SMESHDS_GroupBase*, SMESHDS_Group*, SMESHDS_Group* > TOldNewGroup;
6845 vector< list< TOldNewGroup > > groupsByType( SMDSAbs_NbElementTypes );
6846 vector< TOldNewGroup* > orderedOldNewGroups; // in order of old groups
6848 set< string > groupNames;
6850 SMESH_Mesh::GroupIteratorPtr groupIt = GetMesh()->GetGroups();
6851 if ( !groupIt->more() ) return newGroupIDs;
6853 int newGroupID = mesh->GetGroupIds().back()+1;
6854 while ( groupIt->more() )
6856 SMESH_Group * group = groupIt->next();
6857 if ( !group ) continue;
6858 SMESHDS_GroupBase* groupDS = group->GetGroupDS();
6859 if ( !groupDS || groupDS->IsEmpty() ) continue;
6860 groupNames.insert ( group->GetName() );
6861 groupDS->SetStoreName( group->GetName() );
6862 const SMDSAbs_ElementType type = groupDS->GetType();
6863 SMESHDS_Group* newGroup = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
6864 SMESHDS_Group* newTopGroup = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
6865 groupsByType[ type ].push_back( make_tuple( groupDS, newGroup, newTopGroup ));
6866 orderedOldNewGroups.push_back( & groupsByType[ type ].back() );
6869 // Loop on nodes and elements to add them in new groups
6871 vector< const SMDS_MeshElement* > resultElems;
6872 for ( int isNodes = 0; isNodes < 2; ++isNodes )
6874 const SMESH_SequenceOfElemPtr& gens = isNodes ? nodeGens : elemGens;
6875 const SMESH_SequenceOfElemPtr& elems = isNodes ? myLastCreatedNodes : myLastCreatedElems;
6876 if ( gens.Length() != elems.Length() )
6877 throw SALOME_Exception("SMESH_MeshEditor::generateGroups(): invalid args");
6879 // loop on created elements
6880 for (int iElem = 1; iElem <= elems.Length(); ++iElem )
6882 const SMDS_MeshElement* sourceElem = gens( iElem );
6883 if ( !sourceElem ) {
6884 MESSAGE("generateGroups(): NULL source element");
6887 list< TOldNewGroup > & groupsOldNew = groupsByType[ sourceElem->GetType() ];
6888 if ( groupsOldNew.empty() ) { // no groups of this type at all
6889 while ( iElem < gens.Length() && gens( iElem+1 ) == sourceElem )
6890 ++iElem; // skip all elements made by sourceElem
6893 // collect all elements made by the iElem-th sourceElem
6894 resultElems.clear();
6895 if ( const SMDS_MeshElement* resElem = elems( iElem ))
6896 if ( resElem != sourceElem )
6897 resultElems.push_back( resElem );
6898 while ( iElem < gens.Length() && gens( iElem+1 ) == sourceElem )
6899 if ( const SMDS_MeshElement* resElem = elems( ++iElem ))
6900 if ( resElem != sourceElem )
6901 resultElems.push_back( resElem );
6903 const SMDS_MeshElement* topElem = 0;
6904 if ( isNodes ) // there must be a top element
6906 topElem = resultElems.back();
6907 resultElems.pop_back();
6911 vector< const SMDS_MeshElement* >::reverse_iterator resElemIt = resultElems.rbegin();
6912 for ( ; resElemIt != resultElems.rend() ; ++resElemIt )
6913 if ( (*resElemIt)->GetType() == sourceElem->GetType() )
6915 topElem = *resElemIt;
6916 *resElemIt = 0; // erase *resElemIt
6920 // add resultElems to groups originted from ones the sourceElem belongs to
6921 list< TOldNewGroup >::iterator gOldNew, gLast = groupsOldNew.end();
6922 for ( gOldNew = groupsOldNew.begin(); gOldNew != gLast; ++gOldNew )
6924 SMESHDS_GroupBase* oldGroup = gOldNew->get<0>();
6925 if ( oldGroup->Contains( sourceElem )) // sourceElem is in oldGroup
6927 // fill in a new group
6928 SMDS_MeshGroup & newGroup = gOldNew->get<1>()->SMDSGroup();
6929 vector< const SMDS_MeshElement* >::iterator resLast = resultElems.end(), resElemIt;
6930 for ( resElemIt = resultElems.begin(); resElemIt != resLast; ++resElemIt )
6932 newGroup.Add( *resElemIt );
6934 // fill a "top" group
6937 SMDS_MeshGroup & newTopGroup = gOldNew->get<2>()->SMDSGroup();
6938 newTopGroup.Add( topElem );
6942 } // loop on created elements
6943 }// loop on nodes and elements
6945 // Create new SMESH_Groups from SMESHDS_Groups and remove empty SMESHDS_Groups
6947 list<int> topGrouIds;
6948 for ( size_t i = 0; i < orderedOldNewGroups.size(); ++i )
6950 SMESHDS_GroupBase* oldGroupDS = orderedOldNewGroups[i]->get<0>();
6951 SMESHDS_Group* newGroups[2] = { orderedOldNewGroups[i]->get<1>(),
6952 orderedOldNewGroups[i]->get<2>() };
6953 for ( int is2nd = 0; is2nd < 2; ++is2nd )
6955 SMESHDS_Group* newGroupDS = newGroups[ is2nd ];
6956 if ( newGroupDS->IsEmpty() )
6958 mesh->GetMeshDS()->RemoveGroup( newGroupDS );
6963 newGroupDS->SetType( newGroupDS->GetElements()->next()->GetType() );
6966 const bool isTop = ( topPresent &&
6967 newGroupDS->GetType() == oldGroupDS->GetType() &&
6970 string name = oldGroupDS->GetStoreName();
6971 { // remove trailing whitespaces (issue 22599)
6972 size_t size = name.size();
6973 while ( size > 1 && isspace( name[ size-1 ]))
6975 if ( size != name.size() )
6977 name.resize( size );
6978 oldGroupDS->SetStoreName( name.c_str() );
6981 if ( !targetMesh ) {
6982 string suffix = ( isTop ? "top": postfix.c_str() );
6986 while ( !groupNames.insert( name ).second ) // name exists
6987 name = SMESH_Comment( oldGroupDS->GetStoreName() ) << "_" << suffix << "_" << nb++;
6992 newGroupDS->SetStoreName( name.c_str() );
6994 // make a SMESH_Groups
6995 mesh->AddGroup( newGroupDS );
6997 topGrouIds.push_back( newGroupDS->GetID() );
6999 newGroupIDs->push_back( newGroupDS->GetID() );
7003 newGroupIDs->splice( newGroupIDs->end(), topGrouIds );
7008 //================================================================================
7010 * \brief Return list of group of nodes close to each other within theTolerance
7011 * Search among theNodes or in the whole mesh if theNodes is empty using
7012 * an Octree algorithm
7014 //================================================================================
7016 void SMESH_MeshEditor::FindCoincidentNodes (TIDSortedNodeSet & theNodes,
7017 const double theTolerance,
7018 TListOfListOfNodes & theGroupsOfNodes)
7020 myLastCreatedElems.Clear();
7021 myLastCreatedNodes.Clear();
7023 if ( theNodes.empty() )
7024 { // get all nodes in the mesh
7025 SMDS_NodeIteratorPtr nIt = GetMeshDS()->nodesIterator(/*idInceasingOrder=*/true);
7026 while ( nIt->more() )
7027 theNodes.insert( theNodes.end(),nIt->next());
7030 SMESH_OctreeNode::FindCoincidentNodes ( theNodes, &theGroupsOfNodes, theTolerance);
7033 //=======================================================================
7034 //function : SimplifyFace
7036 //=======================================================================
7038 int SMESH_MeshEditor::SimplifyFace (const vector<const SMDS_MeshNode *>& faceNodes,
7039 vector<const SMDS_MeshNode *>& poly_nodes,
7040 vector<int>& quantities) const
7042 int nbNodes = faceNodes.size();
7047 set<const SMDS_MeshNode*> nodeSet;
7049 // get simple seq of nodes
7050 //const SMDS_MeshNode* simpleNodes[ nbNodes ];
7051 vector<const SMDS_MeshNode*> simpleNodes( nbNodes );
7052 int iSimple = 0, nbUnique = 0;
7054 simpleNodes[iSimple++] = faceNodes[0];
7056 for (int iCur = 1; iCur < nbNodes; iCur++) {
7057 if (faceNodes[iCur] != simpleNodes[iSimple - 1]) {
7058 simpleNodes[iSimple++] = faceNodes[iCur];
7059 if (nodeSet.insert( faceNodes[iCur] ).second)
7063 int nbSimple = iSimple;
7064 if (simpleNodes[nbSimple - 1] == simpleNodes[0]) {
7074 bool foundLoop = (nbSimple > nbUnique);
7077 set<const SMDS_MeshNode*> loopSet;
7078 for (iSimple = 0; iSimple < nbSimple && !foundLoop; iSimple++) {
7079 const SMDS_MeshNode* n = simpleNodes[iSimple];
7080 if (!loopSet.insert( n ).second) {
7084 int iC = 0, curLast = iSimple;
7085 for (; iC < curLast; iC++) {
7086 if (simpleNodes[iC] == n) break;
7088 int loopLen = curLast - iC;
7090 // create sub-element
7092 quantities.push_back(loopLen);
7093 for (; iC < curLast; iC++) {
7094 poly_nodes.push_back(simpleNodes[iC]);
7097 // shift the rest nodes (place from the first loop position)
7098 for (iC = curLast + 1; iC < nbSimple; iC++) {
7099 simpleNodes[iC - loopLen] = simpleNodes[iC];
7101 nbSimple -= loopLen;
7104 } // for (iSimple = 0; iSimple < nbSimple; iSimple++)
7105 } // while (foundLoop)
7109 quantities.push_back(iSimple);
7110 for (int i = 0; i < iSimple; i++)
7111 poly_nodes.push_back(simpleNodes[i]);
7117 //=======================================================================
7118 //function : MergeNodes
7119 //purpose : In each group, the cdr of nodes are substituted by the first one
7121 //=======================================================================
7123 void SMESH_MeshEditor::MergeNodes (TListOfListOfNodes & theGroupsOfNodes)
7125 MESSAGE("MergeNodes");
7126 myLastCreatedElems.Clear();
7127 myLastCreatedNodes.Clear();
7129 SMESHDS_Mesh* aMesh = GetMeshDS();
7131 TNodeNodeMap nodeNodeMap; // node to replace - new node
7132 set<const SMDS_MeshElement*> elems; // all elements with changed nodes
7133 list< int > rmElemIds, rmNodeIds;
7135 // Fill nodeNodeMap and elems
7137 TListOfListOfNodes::iterator grIt = theGroupsOfNodes.begin();
7138 for ( ; grIt != theGroupsOfNodes.end(); grIt++ ) {
7139 list<const SMDS_MeshNode*>& nodes = *grIt;
7140 list<const SMDS_MeshNode*>::iterator nIt = nodes.begin();
7141 const SMDS_MeshNode* nToKeep = *nIt;
7142 //MESSAGE("node to keep " << nToKeep->GetID());
7143 for ( ++nIt; nIt != nodes.end(); nIt++ ) {
7144 const SMDS_MeshNode* nToRemove = *nIt;
7145 nodeNodeMap.insert( TNodeNodeMap::value_type( nToRemove, nToKeep ));
7146 if ( nToRemove != nToKeep ) {
7147 //MESSAGE(" node to remove " << nToRemove->GetID());
7148 rmNodeIds.push_back( nToRemove->GetID() );
7149 AddToSameGroups( nToKeep, nToRemove, aMesh );
7150 // set _alwaysComputed to a sub-mesh of VERTEX to enable mesh computing
7151 // after MergeNodes() w/o creating node in place of merged ones.
7152 const SMDS_PositionPtr& pos = nToRemove->GetPosition();
7153 if ( pos && pos->GetTypeOfPosition() == SMDS_TOP_VERTEX )
7154 if ( SMESH_subMesh* sm = myMesh->GetSubMeshContaining( nToRemove->getshapeId() ))
7155 sm->SetIsAlwaysComputed( true );
7158 SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
7159 while ( invElemIt->more() ) {
7160 const SMDS_MeshElement* elem = invElemIt->next();
7165 // Change element nodes or remove an element
7167 set<const SMDS_MeshElement*>::iterator eIt = elems.begin();
7168 for ( ; eIt != elems.end(); eIt++ ) {
7169 const SMDS_MeshElement* elem = *eIt;
7170 //MESSAGE(" ---- inverse elem on node to remove " << elem->GetID());
7171 int nbNodes = elem->NbNodes();
7172 int aShapeId = FindShape( elem );
7174 set<const SMDS_MeshNode*> nodeSet;
7175 vector< const SMDS_MeshNode*> curNodes( nbNodes ), uniqueNodes( nbNodes );
7176 int iUnique = 0, iCur = 0, nbRepl = 0;
7177 vector<int> iRepl( nbNodes );
7179 // get new seq of nodes
7180 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
7181 while ( itN->more() ) {
7182 const SMDS_MeshNode* n =
7183 static_cast<const SMDS_MeshNode*>( itN->next() );
7185 TNodeNodeMap::iterator nnIt = nodeNodeMap.find( n );
7186 if ( nnIt != nodeNodeMap.end() ) { // n sticks
7188 // BUG 0020185: begin
7190 bool stopRecur = false;
7191 set<const SMDS_MeshNode*> nodesRecur;
7192 nodesRecur.insert(n);
7193 while (!stopRecur) {
7194 TNodeNodeMap::iterator nnIt_i = nodeNodeMap.find( n );
7195 if ( nnIt_i != nodeNodeMap.end() ) { // n sticks
7196 n = (*nnIt_i).second;
7197 if (!nodesRecur.insert(n).second) {
7198 // error: recursive dependancy
7208 curNodes[ iCur ] = n;
7209 bool isUnique = nodeSet.insert( n ).second;
7211 uniqueNodes[ iUnique++ ] = n;
7213 iRepl[ nbRepl++ ] = iCur;
7217 // Analyse element topology after replacement
7220 int nbUniqueNodes = nodeSet.size();
7221 //MESSAGE("nbNodes nbUniqueNodes " << nbNodes << " " << nbUniqueNodes);
7222 if ( nbNodes != nbUniqueNodes ) { // some nodes stick
7223 // Polygons and Polyhedral volumes
7224 if (elem->IsPoly()) {
7226 if (elem->GetType() == SMDSAbs_Face) {
7228 vector<const SMDS_MeshNode *> face_nodes (nbNodes);
7230 for (; inode < nbNodes; inode++) {
7231 face_nodes[inode] = curNodes[inode];
7234 vector<const SMDS_MeshNode *> polygons_nodes;
7235 vector<int> quantities;
7236 int nbNew = SimplifyFace(face_nodes, polygons_nodes, quantities);
7239 for (int iface = 0; iface < nbNew; iface++) {
7240 int nbNodes = quantities[iface];
7241 vector<const SMDS_MeshNode *> poly_nodes (nbNodes);
7242 for (int ii = 0; ii < nbNodes; ii++, inode++) {
7243 poly_nodes[ii] = polygons_nodes[inode];
7245 SMDS_MeshElement* newElem = aMesh->AddPolygonalFace(poly_nodes);
7246 myLastCreatedElems.Append(newElem);
7248 aMesh->SetMeshElementOnShape(newElem, aShapeId);
7251 MESSAGE("ChangeElementNodes MergeNodes Polygon");
7252 //aMesh->ChangeElementNodes(elem, &polygons_nodes[inode], quantities[nbNew - 1]);
7253 vector<const SMDS_MeshNode *> polynodes(polygons_nodes.begin()+inode,polygons_nodes.end());
7255 if (nbNew > 0) quid = nbNew - 1;
7256 vector<int> newquant(quantities.begin()+quid, quantities.end());
7257 const SMDS_MeshElement* newElem = 0;
7258 newElem = aMesh->AddPolyhedralVolume(polynodes, newquant);
7259 myLastCreatedElems.Append(newElem);
7260 if ( aShapeId && newElem )
7261 aMesh->SetMeshElementOnShape( newElem, aShapeId );
7262 rmElemIds.push_back(elem->GetID());
7265 rmElemIds.push_back(elem->GetID());
7269 else if (elem->GetType() == SMDSAbs_Volume) {
7270 // Polyhedral volume
7271 if (nbUniqueNodes < 4) {
7272 rmElemIds.push_back(elem->GetID());
7275 // each face has to be analyzed in order to check volume validity
7276 const SMDS_VtkVolume* aPolyedre =
7277 dynamic_cast<const SMDS_VtkVolume*>( elem );
7279 int nbFaces = aPolyedre->NbFaces();
7281 vector<const SMDS_MeshNode *> poly_nodes;
7282 vector<int> quantities;
7284 for (int iface = 1; iface <= nbFaces; iface++) {
7285 int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
7286 vector<const SMDS_MeshNode *> faceNodes (nbFaceNodes);
7288 for (int inode = 1; inode <= nbFaceNodes; inode++) {
7289 const SMDS_MeshNode * faceNode = aPolyedre->GetFaceNode(iface, inode);
7290 TNodeNodeMap::iterator nnIt = nodeNodeMap.find(faceNode);
7291 if (nnIt != nodeNodeMap.end()) { // faceNode sticks
7292 faceNode = (*nnIt).second;
7294 faceNodes[inode - 1] = faceNode;
7297 SimplifyFace(faceNodes, poly_nodes, quantities);
7300 if (quantities.size() > 3) {
7301 // to be done: remove coincident faces
7304 if (quantities.size() > 3)
7306 MESSAGE("ChangeElementNodes MergeNodes Polyhedron");
7307 //aMesh->ChangePolyhedronNodes(elem, poly_nodes, quantities);
7308 const SMDS_MeshElement* newElem = 0;
7309 newElem = aMesh->AddPolyhedralVolume(poly_nodes, quantities);
7310 myLastCreatedElems.Append(newElem);
7311 if ( aShapeId && newElem )
7312 aMesh->SetMeshElementOnShape( newElem, aShapeId );
7313 rmElemIds.push_back(elem->GetID());
7317 rmElemIds.push_back(elem->GetID());
7328 // TODO not all the possible cases are solved. Find something more generic?
7329 switch ( nbNodes ) {
7330 case 2: ///////////////////////////////////// EDGE
7331 isOk = false; break;
7332 case 3: ///////////////////////////////////// TRIANGLE
7333 isOk = false; break;
7335 if ( elem->GetType() == SMDSAbs_Volume ) // TETRAHEDRON
7337 else { //////////////////////////////////// QUADRANGLE
7338 if ( nbUniqueNodes < 3 )
7340 else if ( nbRepl == 2 && iRepl[ 1 ] - iRepl[ 0 ] == 2 )
7341 isOk = false; // opposite nodes stick
7342 //MESSAGE("isOk " << isOk);
7345 case 6: ///////////////////////////////////// PENTAHEDRON
7346 if ( nbUniqueNodes == 4 ) {
7347 // ---------------------------------> tetrahedron
7349 iRepl[ 0 ] > 2 && iRepl[ 1 ] > 2 && iRepl[ 2 ] > 2 ) {
7350 // all top nodes stick: reverse a bottom
7351 uniqueNodes[ 0 ] = curNodes [ 1 ];
7352 uniqueNodes[ 1 ] = curNodes [ 0 ];
7354 else if (nbRepl == 3 &&
7355 iRepl[ 0 ] < 3 && iRepl[ 1 ] < 3 && iRepl[ 2 ] < 3 ) {
7356 // all bottom nodes stick: set a top before
7357 uniqueNodes[ 3 ] = uniqueNodes [ 0 ];
7358 uniqueNodes[ 0 ] = curNodes [ 3 ];
7359 uniqueNodes[ 1 ] = curNodes [ 4 ];
7360 uniqueNodes[ 2 ] = curNodes [ 5 ];
7362 else if (nbRepl == 4 &&
7363 iRepl[ 2 ] - iRepl [ 0 ] == 3 && iRepl[ 3 ] - iRepl [ 1 ] == 3 ) {
7364 // a lateral face turns into a line: reverse a bottom
7365 uniqueNodes[ 0 ] = curNodes [ 1 ];
7366 uniqueNodes[ 1 ] = curNodes [ 0 ];
7371 else if ( nbUniqueNodes == 5 ) {
7372 // PENTAHEDRON --------------------> 2 tetrahedrons
7373 if ( nbRepl == 2 && iRepl[ 1 ] - iRepl [ 0 ] == 3 ) {
7374 // a bottom node sticks with a linked top one
7376 SMDS_MeshElement* newElem =
7377 aMesh->AddVolume(curNodes[ 3 ],
7380 curNodes[ iRepl[ 0 ] == 2 ? 1 : 2 ]);
7381 myLastCreatedElems.Append(newElem);
7383 aMesh->SetMeshElementOnShape( newElem, aShapeId );
7384 // 2. : reverse a bottom
7385 uniqueNodes[ 0 ] = curNodes [ 1 ];
7386 uniqueNodes[ 1 ] = curNodes [ 0 ];
7396 if(elem->IsQuadratic()) { // Quadratic quadrangle
7408 MESSAGE("nbRepl=2: " << iRepl[0] << " " << iRepl[1]);
7411 MESSAGE("nbRepl=3: " << iRepl[0] << " " << iRepl[1] << " " << iRepl[2]);
7413 if( iRepl[0]==0 && iRepl[1]==1 && iRepl[2]==4 ) {
7414 uniqueNodes[0] = curNodes[0];
7415 uniqueNodes[1] = curNodes[2];
7416 uniqueNodes[2] = curNodes[3];
7417 uniqueNodes[3] = curNodes[5];
7418 uniqueNodes[4] = curNodes[6];
7419 uniqueNodes[5] = curNodes[7];
7422 if( iRepl[0]==0 && iRepl[1]==3 && iRepl[2]==7 ) {
7423 uniqueNodes[0] = curNodes[0];
7424 uniqueNodes[1] = curNodes[1];
7425 uniqueNodes[2] = curNodes[2];
7426 uniqueNodes[3] = curNodes[4];
7427 uniqueNodes[4] = curNodes[5];
7428 uniqueNodes[5] = curNodes[6];
7431 if( iRepl[0]==0 && iRepl[1]==4 && iRepl[2]==7 ) {
7432 uniqueNodes[0] = curNodes[1];
7433 uniqueNodes[1] = curNodes[2];
7434 uniqueNodes[2] = curNodes[3];
7435 uniqueNodes[3] = curNodes[5];
7436 uniqueNodes[4] = curNodes[6];
7437 uniqueNodes[5] = curNodes[0];
7440 if( iRepl[0]==1 && iRepl[1]==2 && iRepl[2]==5 ) {
7441 uniqueNodes[0] = curNodes[0];
7442 uniqueNodes[1] = curNodes[1];
7443 uniqueNodes[2] = curNodes[3];
7444 uniqueNodes[3] = curNodes[4];
7445 uniqueNodes[4] = curNodes[6];
7446 uniqueNodes[5] = curNodes[7];
7449 if( iRepl[0]==1 && iRepl[1]==4 && iRepl[2]==5 ) {
7450 uniqueNodes[0] = curNodes[0];
7451 uniqueNodes[1] = curNodes[2];
7452 uniqueNodes[2] = curNodes[3];
7453 uniqueNodes[3] = curNodes[1];
7454 uniqueNodes[4] = curNodes[6];
7455 uniqueNodes[5] = curNodes[7];
7458 if( iRepl[0]==2 && iRepl[1]==3 && iRepl[2]==6 ) {
7459 uniqueNodes[0] = curNodes[0];
7460 uniqueNodes[1] = curNodes[1];
7461 uniqueNodes[2] = curNodes[2];
7462 uniqueNodes[3] = curNodes[4];
7463 uniqueNodes[4] = curNodes[5];
7464 uniqueNodes[5] = curNodes[7];
7467 if( iRepl[0]==2 && iRepl[1]==5 && iRepl[2]==6 ) {
7468 uniqueNodes[0] = curNodes[0];
7469 uniqueNodes[1] = curNodes[1];
7470 uniqueNodes[2] = curNodes[3];
7471 uniqueNodes[3] = curNodes[4];
7472 uniqueNodes[4] = curNodes[2];
7473 uniqueNodes[5] = curNodes[7];
7476 if( iRepl[0]==3 && iRepl[1]==6 && iRepl[2]==7 ) {
7477 uniqueNodes[0] = curNodes[0];
7478 uniqueNodes[1] = curNodes[1];
7479 uniqueNodes[2] = curNodes[2];
7480 uniqueNodes[3] = curNodes[4];
7481 uniqueNodes[4] = curNodes[5];
7482 uniqueNodes[5] = curNodes[3];
7487 MESSAGE("nbRepl=4: " << iRepl[0] << " " << iRepl[1] << " " << iRepl[2] << " " << iRepl[3]);
7490 MESSAGE("nbRepl=5: " << iRepl[0] << " " << iRepl[1] << " " << iRepl[2] << " " << iRepl[3] << " " << iRepl[4]);
7494 //////////////////////////////////// HEXAHEDRON
7496 SMDS_VolumeTool hexa (elem);
7497 hexa.SetExternalNormal();
7498 if ( nbUniqueNodes == 4 && nbRepl == 4 ) {
7499 //////////////////////// HEX ---> 1 tetrahedron
7500 for ( int iFace = 0; iFace < 6; iFace++ ) {
7501 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7502 if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7503 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7504 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7505 // one face turns into a point ...
7506 int iOppFace = hexa.GetOppFaceIndex( iFace );
7507 ind = hexa.GetFaceNodesIndices( iOppFace );
7509 for ( iCur = 0; iCur < 4 && nbStick < 2; iCur++ ) {
7510 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7513 if ( nbStick == 1 ) {
7514 // ... and the opposite one - into a triangle.
7516 ind = hexa.GetFaceNodesIndices( iFace );
7517 uniqueNodes[ 3 ] = curNodes[ind[ 0 ]];
7524 else if ( nbUniqueNodes == 6 && nbRepl == 2 ) {
7525 //////////////////////// HEX ---> 1 prism
7526 int nbTria = 0, iTria[3];
7527 const int *ind; // indices of face nodes
7528 // look for triangular faces
7529 for ( int iFace = 0; iFace < 6 && nbTria < 3; iFace++ ) {
7530 ind = hexa.GetFaceNodesIndices( iFace );
7531 TIDSortedNodeSet faceNodes;
7532 for ( iCur = 0; iCur < 4; iCur++ )
7533 faceNodes.insert( curNodes[ind[iCur]] );
7534 if ( faceNodes.size() == 3 )
7535 iTria[ nbTria++ ] = iFace;
7537 // check if triangles are opposite
7538 if ( nbTria == 2 && iTria[0] == hexa.GetOppFaceIndex( iTria[1] ))
7541 // set nodes of the bottom triangle
7542 ind = hexa.GetFaceNodesIndices( iTria[ 0 ]);
7544 for ( iCur = 0; iCur < 4; iCur++ )
7545 if ( ind[iCur] != iRepl[0] && ind[iCur] != iRepl[1])
7546 indB.push_back( ind[iCur] );
7547 if ( !hexa.IsForward() )
7548 std::swap( indB[0], indB[2] );
7549 for ( iCur = 0; iCur < 3; iCur++ )
7550 uniqueNodes[ iCur ] = curNodes[indB[iCur]];
7551 // set nodes of the top triangle
7552 const int *indT = hexa.GetFaceNodesIndices( iTria[ 1 ]);
7553 for ( iCur = 0; iCur < 3; ++iCur )
7554 for ( int j = 0; j < 4; ++j )
7555 if ( hexa.IsLinked( indB[ iCur ], indT[ j ] ))
7557 uniqueNodes[ iCur + 3 ] = curNodes[ indT[ j ]];
7563 else if (nbUniqueNodes == 5 && nbRepl == 4 ) {
7564 //////////////////// HEXAHEDRON ---> 2 tetrahedrons
7565 for ( int iFace = 0; iFace < 6; iFace++ ) {
7566 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7567 if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7568 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7569 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7570 // one face turns into a point ...
7571 int iOppFace = hexa.GetOppFaceIndex( iFace );
7572 ind = hexa.GetFaceNodesIndices( iOppFace );
7574 iUnique = 2; // reverse a tetrahedron 1 bottom
7575 for ( iCur = 0; iCur < 4 && nbStick == 0; iCur++ ) {
7576 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7578 else if ( iUnique >= 0 )
7579 uniqueNodes[ iUnique-- ] = curNodes[ind[ iCur ]];
7581 if ( nbStick == 0 ) {
7582 // ... and the opposite one is a quadrangle
7584 const int* indTop = hexa.GetFaceNodesIndices( iFace );
7585 uniqueNodes[ 3 ] = curNodes[indTop[ 0 ]];
7588 SMDS_MeshElement* newElem =
7589 aMesh->AddVolume(curNodes[ind[ 0 ]],
7592 curNodes[indTop[ 0 ]]);
7593 myLastCreatedElems.Append(newElem);
7595 aMesh->SetMeshElementOnShape( newElem, aShapeId );
7602 else if ( nbUniqueNodes == 6 && nbRepl == 4 ) {
7603 ////////////////// HEXAHEDRON ---> 2 tetrahedrons or 1 prism
7604 // find indices of quad and tri faces
7605 int iQuadFace[ 6 ], iTriFace[ 6 ], nbQuad = 0, nbTri = 0, iFace;
7606 for ( iFace = 0; iFace < 6; iFace++ ) {
7607 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7609 for ( iCur = 0; iCur < 4; iCur++ )
7610 nodeSet.insert( curNodes[ind[ iCur ]] );
7611 nbUniqueNodes = nodeSet.size();
7612 if ( nbUniqueNodes == 3 )
7613 iTriFace[ nbTri++ ] = iFace;
7614 else if ( nbUniqueNodes == 4 )
7615 iQuadFace[ nbQuad++ ] = iFace;
7617 if (nbQuad == 2 && nbTri == 4 &&
7618 hexa.GetOppFaceIndex( iQuadFace[ 0 ] ) == iQuadFace[ 1 ]) {
7619 // 2 opposite quadrangles stuck with a diagonal;
7620 // sample groups of merged indices: (0-4)(2-6)
7621 // --------------------------------------------> 2 tetrahedrons
7622 const int *ind1 = hexa.GetFaceNodesIndices( iQuadFace[ 0 ]); // indices of quad1 nodes
7623 const int *ind2 = hexa.GetFaceNodesIndices( iQuadFace[ 1 ]);
7624 int i0, i1d, i2, i3d, i0t, i2t; // d-daigonal, t-top
7625 if (curNodes[ind1[ 0 ]] == curNodes[ind2[ 0 ]] &&
7626 curNodes[ind1[ 2 ]] == curNodes[ind2[ 2 ]]) {
7627 // stuck with 0-2 diagonal
7635 else if (curNodes[ind1[ 1 ]] == curNodes[ind2[ 3 ]] &&
7636 curNodes[ind1[ 3 ]] == curNodes[ind2[ 1 ]]) {
7637 // stuck with 1-3 diagonal
7649 uniqueNodes[ 0 ] = curNodes [ i0 ];
7650 uniqueNodes[ 1 ] = curNodes [ i1d ];
7651 uniqueNodes[ 2 ] = curNodes [ i3d ];
7652 uniqueNodes[ 3 ] = curNodes [ i0t ];
7655 SMDS_MeshElement* newElem = aMesh->AddVolume(curNodes[ i1d ],
7659 myLastCreatedElems.Append(newElem);
7661 aMesh->SetMeshElementOnShape( newElem, aShapeId );
7664 else if (( nbTri == 2 && nbQuad == 3 ) || // merged (0-4)(1-5)
7665 ( nbTri == 4 && nbQuad == 2 )) { // merged (7-4)(1-5)
7666 // --------------------------------------------> prism
7667 // find 2 opposite triangles
7669 for ( iFace = 0; iFace + 1 < nbTri; iFace++ ) {
7670 if ( hexa.GetOppFaceIndex( iTriFace[ iFace ] ) == iTriFace[ iFace + 1 ]) {
7671 // find indices of kept and replaced nodes
7672 // and fill unique nodes of 2 opposite triangles
7673 const int *ind1 = hexa.GetFaceNodesIndices( iTriFace[ iFace ]);
7674 const int *ind2 = hexa.GetFaceNodesIndices( iTriFace[ iFace + 1 ]);
7675 const SMDS_MeshNode** hexanodes = hexa.GetNodes();
7676 // fill unique nodes
7679 for ( iCur = 0; iCur < 4 && isOk; iCur++ ) {
7680 const SMDS_MeshNode* n = curNodes[ind1[ iCur ]];
7681 const SMDS_MeshNode* nInit = hexanodes[ind1[ iCur ]];
7683 // iCur of a linked node of the opposite face (make normals co-directed):
7684 int iCurOpp = ( iCur == 1 || iCur == 3 ) ? 4 - iCur : iCur;
7685 // check that correspondent corners of triangles are linked
7686 if ( !hexa.IsLinked( ind1[ iCur ], ind2[ iCurOpp ] ))
7689 uniqueNodes[ iUnique ] = n;
7690 uniqueNodes[ iUnique + 3 ] = curNodes[ind2[ iCurOpp ]];
7699 } // if ( nbUniqueNodes == 6 && nbRepl == 4 )
7702 MESSAGE("MergeNodes() removes hexahedron "<< elem);
7709 } // switch ( nbNodes )
7711 } // if ( nbNodes != nbUniqueNodes ) // some nodes stick
7713 if ( isOk ) { // the elem remains valid after sticking nodes
7714 if (elem->IsPoly() && elem->GetType() == SMDSAbs_Volume)
7716 // Change nodes of polyedre
7717 const SMDS_VtkVolume* aPolyedre =
7718 dynamic_cast<const SMDS_VtkVolume*>( elem );
7720 int nbFaces = aPolyedre->NbFaces();
7722 vector<const SMDS_MeshNode *> poly_nodes;
7723 vector<int> quantities (nbFaces);
7725 for (int iface = 1; iface <= nbFaces; iface++) {
7726 int inode, nbFaceNodes = aPolyedre->NbFaceNodes(iface);
7727 quantities[iface - 1] = nbFaceNodes;
7729 for (inode = 1; inode <= nbFaceNodes; inode++) {
7730 const SMDS_MeshNode* curNode = aPolyedre->GetFaceNode(iface, inode);
7732 TNodeNodeMap::iterator nnIt = nodeNodeMap.find( curNode );
7733 if (nnIt != nodeNodeMap.end()) { // curNode sticks
7734 curNode = (*nnIt).second;
7736 poly_nodes.push_back(curNode);
7739 aMesh->ChangePolyhedronNodes( elem, poly_nodes, quantities );
7742 else // replace non-polyhedron elements
7744 const SMDSAbs_ElementType etyp = elem->GetType();
7745 const int elemId = elem->GetID();
7746 const bool isPoly = (elem->GetEntityType() == SMDSEntity_Polygon);
7747 uniqueNodes.resize(nbUniqueNodes);
7749 SMESHDS_SubMesh * sm = aShapeId > 0 ? aMesh->MeshElements(aShapeId) : 0;
7751 aMesh->RemoveFreeElement(elem, sm, /*fromGroups=*/false);
7752 SMDS_MeshElement* newElem = this->AddElement(uniqueNodes, etyp, isPoly, elemId);
7753 if ( sm && newElem )
7754 sm->AddElement( newElem );
7755 if ( elem != newElem )
7756 ReplaceElemInGroups( elem, newElem, aMesh );
7760 // Remove invalid regular element or invalid polygon
7761 rmElemIds.push_back( elem->GetID() );
7764 } // loop on elements
7766 // Remove bad elements, then equal nodes (order important)
7768 Remove( rmElemIds, false );
7769 Remove( rmNodeIds, true );
7774 // ========================================================
7775 // class : SortableElement
7776 // purpose : allow sorting elements basing on their nodes
7777 // ========================================================
7778 class SortableElement : public set <const SMDS_MeshElement*>
7782 SortableElement( const SMDS_MeshElement* theElem )
7785 SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator();
7786 while ( nodeIt->more() )
7787 this->insert( nodeIt->next() );
7790 const SMDS_MeshElement* Get() const
7793 void Set(const SMDS_MeshElement* e) const
7798 mutable const SMDS_MeshElement* myElem;
7801 //=======================================================================
7802 //function : FindEqualElements
7803 //purpose : Return list of group of elements built on the same nodes.
7804 // Search among theElements or in the whole mesh if theElements is empty
7805 //=======================================================================
7807 void SMESH_MeshEditor::FindEqualElements(TIDSortedElemSet & theElements,
7808 TListOfListOfElementsID & theGroupsOfElementsID)
7810 myLastCreatedElems.Clear();
7811 myLastCreatedNodes.Clear();
7813 typedef map< SortableElement, int > TMapOfNodeSet;
7814 typedef list<int> TGroupOfElems;
7816 if ( theElements.empty() )
7817 { // get all elements in the mesh
7818 SMDS_ElemIteratorPtr eIt = GetMeshDS()->elementsIterator();
7819 while ( eIt->more() )
7820 theElements.insert( theElements.end(), eIt->next());
7823 vector< TGroupOfElems > arrayOfGroups;
7824 TGroupOfElems groupOfElems;
7825 TMapOfNodeSet mapOfNodeSet;
7827 TIDSortedElemSet::iterator elemIt = theElements.begin();
7828 for ( int i = 0, j=0; elemIt != theElements.end(); ++elemIt, ++j ) {
7829 const SMDS_MeshElement* curElem = *elemIt;
7830 SortableElement SE(curElem);
7833 pair< TMapOfNodeSet::iterator, bool> pp = mapOfNodeSet.insert(make_pair(SE, i));
7834 if( !(pp.second) ) {
7835 TMapOfNodeSet::iterator& itSE = pp.first;
7836 ind = (*itSE).second;
7837 arrayOfGroups[ind].push_back(curElem->GetID());
7840 groupOfElems.clear();
7841 groupOfElems.push_back(curElem->GetID());
7842 arrayOfGroups.push_back(groupOfElems);
7847 vector< TGroupOfElems >::iterator groupIt = arrayOfGroups.begin();
7848 for ( ; groupIt != arrayOfGroups.end(); ++groupIt ) {
7849 groupOfElems = *groupIt;
7850 if ( groupOfElems.size() > 1 ) {
7851 groupOfElems.sort();
7852 theGroupsOfElementsID.push_back(groupOfElems);
7857 //=======================================================================
7858 //function : MergeElements
7859 //purpose : In each given group, substitute all elements by the first one.
7860 //=======================================================================
7862 void SMESH_MeshEditor::MergeElements(TListOfListOfElementsID & theGroupsOfElementsID)
7864 myLastCreatedElems.Clear();
7865 myLastCreatedNodes.Clear();
7867 typedef list<int> TListOfIDs;
7868 TListOfIDs rmElemIds; // IDs of elems to remove
7870 SMESHDS_Mesh* aMesh = GetMeshDS();
7872 TListOfListOfElementsID::iterator groupsIt = theGroupsOfElementsID.begin();
7873 while ( groupsIt != theGroupsOfElementsID.end() ) {
7874 TListOfIDs& aGroupOfElemID = *groupsIt;
7875 aGroupOfElemID.sort();
7876 int elemIDToKeep = aGroupOfElemID.front();
7877 const SMDS_MeshElement* elemToKeep = aMesh->FindElement(elemIDToKeep);
7878 aGroupOfElemID.pop_front();
7879 TListOfIDs::iterator idIt = aGroupOfElemID.begin();
7880 while ( idIt != aGroupOfElemID.end() ) {
7881 int elemIDToRemove = *idIt;
7882 const SMDS_MeshElement* elemToRemove = aMesh->FindElement(elemIDToRemove);
7883 // add the kept element in groups of removed one (PAL15188)
7884 AddToSameGroups( elemToKeep, elemToRemove, aMesh );
7885 rmElemIds.push_back( elemIDToRemove );
7891 Remove( rmElemIds, false );
7894 //=======================================================================
7895 //function : MergeEqualElements
7896 //purpose : Remove all but one of elements built on the same nodes.
7897 //=======================================================================
7899 void SMESH_MeshEditor::MergeEqualElements()
7901 TIDSortedElemSet aMeshElements; /* empty input ==
7902 to merge equal elements in the whole mesh */
7903 TListOfListOfElementsID aGroupsOfElementsID;
7904 FindEqualElements(aMeshElements, aGroupsOfElementsID);
7905 MergeElements(aGroupsOfElementsID);
7908 //=======================================================================
7909 //function : findAdjacentFace
7911 //=======================================================================
7913 static const SMDS_MeshElement* findAdjacentFace(const SMDS_MeshNode* n1,
7914 const SMDS_MeshNode* n2,
7915 const SMDS_MeshElement* elem)
7917 TIDSortedElemSet elemSet, avoidSet;
7919 avoidSet.insert ( elem );
7920 return SMESH_MeshAlgos::FindFaceInSet( n1, n2, elemSet, avoidSet );
7923 //=======================================================================
7924 //function : FindFreeBorder
7926 //=======================================================================
7928 #define ControlFreeBorder SMESH::Controls::FreeEdges::IsFreeEdge
7930 bool SMESH_MeshEditor::FindFreeBorder (const SMDS_MeshNode* theFirstNode,
7931 const SMDS_MeshNode* theSecondNode,
7932 const SMDS_MeshNode* theLastNode,
7933 list< const SMDS_MeshNode* > & theNodes,
7934 list< const SMDS_MeshElement* >& theFaces)
7936 if ( !theFirstNode || !theSecondNode )
7938 // find border face between theFirstNode and theSecondNode
7939 const SMDS_MeshElement* curElem = findAdjacentFace( theFirstNode, theSecondNode, 0 );
7943 theFaces.push_back( curElem );
7944 theNodes.push_back( theFirstNode );
7945 theNodes.push_back( theSecondNode );
7947 //vector<const SMDS_MeshNode*> nodes;
7948 const SMDS_MeshNode *nIgnore = theFirstNode, *nStart = theSecondNode;
7949 TIDSortedElemSet foundElems;
7950 bool needTheLast = ( theLastNode != 0 );
7952 while ( nStart != theLastNode ) {
7953 if ( nStart == theFirstNode )
7954 return !needTheLast;
7956 // find all free border faces sharing form nStart
7958 list< const SMDS_MeshElement* > curElemList;
7959 list< const SMDS_MeshNode* > nStartList;
7960 SMDS_ElemIteratorPtr invElemIt = nStart->GetInverseElementIterator(SMDSAbs_Face);
7961 while ( invElemIt->more() ) {
7962 const SMDS_MeshElement* e = invElemIt->next();
7963 if ( e == curElem || foundElems.insert( e ).second ) {
7965 int iNode = 0, nbNodes = e->NbNodes();
7966 //const SMDS_MeshNode* nodes[nbNodes+1];
7967 vector<const SMDS_MeshNode*> nodes(nbNodes+1);
7969 if(e->IsQuadratic()) {
7970 const SMDS_VtkFace* F =
7971 dynamic_cast<const SMDS_VtkFace*>(e);
7972 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
7973 // use special nodes iterator
7974 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
7975 while( anIter->more() ) {
7976 nodes[ iNode++ ] = cast2Node(anIter->next());
7980 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
7981 while ( nIt->more() )
7982 nodes[ iNode++ ] = static_cast<const SMDS_MeshNode*>( nIt->next() );
7984 nodes[ iNode ] = nodes[ 0 ];
7986 for ( iNode = 0; iNode < nbNodes; iNode++ )
7987 if (((nodes[ iNode ] == nStart && nodes[ iNode + 1] != nIgnore ) ||
7988 (nodes[ iNode + 1] == nStart && nodes[ iNode ] != nIgnore )) &&
7989 ControlFreeBorder( &nodes[ iNode ], e->GetID() ))
7991 nStartList.push_back( nodes[ iNode + ( nodes[ iNode ] == nStart ? 1 : 0 )]);
7992 curElemList.push_back( e );
7996 // analyse the found
7998 int nbNewBorders = curElemList.size();
7999 if ( nbNewBorders == 0 ) {
8000 // no free border furthermore
8001 return !needTheLast;
8003 else if ( nbNewBorders == 1 ) {
8004 // one more element found
8006 nStart = nStartList.front();
8007 curElem = curElemList.front();
8008 theFaces.push_back( curElem );
8009 theNodes.push_back( nStart );
8012 // several continuations found
8013 list< const SMDS_MeshElement* >::iterator curElemIt;
8014 list< const SMDS_MeshNode* >::iterator nStartIt;
8015 // check if one of them reached the last node
8016 if ( needTheLast ) {
8017 for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
8018 curElemIt!= curElemList.end();
8019 curElemIt++, nStartIt++ )
8020 if ( *nStartIt == theLastNode ) {
8021 theFaces.push_back( *curElemIt );
8022 theNodes.push_back( *nStartIt );
8026 // find the best free border by the continuations
8027 list<const SMDS_MeshNode*> contNodes[ 2 ], *cNL;
8028 list<const SMDS_MeshElement*> contFaces[ 2 ], *cFL;
8029 for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
8030 curElemIt!= curElemList.end();
8031 curElemIt++, nStartIt++ )
8033 cNL = & contNodes[ contNodes[0].empty() ? 0 : 1 ];
8034 cFL = & contFaces[ contFaces[0].empty() ? 0 : 1 ];
8035 // find one more free border
8036 if ( ! SMESH_MeshEditor::FindFreeBorder( nStart, *nStartIt, theLastNode, *cNL, *cFL )) {
8040 else if ( !contNodes[0].empty() && !contNodes[1].empty() ) {
8041 // choice: clear a worse one
8042 int iLongest = ( contNodes[0].size() < contNodes[1].size() ? 1 : 0 );
8043 int iWorse = ( needTheLast ? 1 - iLongest : iLongest );
8044 contNodes[ iWorse ].clear();
8045 contFaces[ iWorse ].clear();
8048 if ( contNodes[0].empty() && contNodes[1].empty() )
8051 // append the best free border
8052 cNL = & contNodes[ contNodes[0].empty() ? 1 : 0 ];
8053 cFL = & contFaces[ contFaces[0].empty() ? 1 : 0 ];
8054 theNodes.pop_back(); // remove nIgnore
8055 theNodes.pop_back(); // remove nStart
8056 theFaces.pop_back(); // remove curElem
8057 list< const SMDS_MeshNode* >::iterator nIt = cNL->begin();
8058 list< const SMDS_MeshElement* >::iterator fIt = cFL->begin();
8059 for ( ; nIt != cNL->end(); nIt++ ) theNodes.push_back( *nIt );
8060 for ( ; fIt != cFL->end(); fIt++ ) theFaces.push_back( *fIt );
8063 } // several continuations found
8064 } // while ( nStart != theLastNode )
8069 //=======================================================================
8070 //function : CheckFreeBorderNodes
8071 //purpose : Return true if the tree nodes are on a free border
8072 //=======================================================================
8074 bool SMESH_MeshEditor::CheckFreeBorderNodes(const SMDS_MeshNode* theNode1,
8075 const SMDS_MeshNode* theNode2,
8076 const SMDS_MeshNode* theNode3)
8078 list< const SMDS_MeshNode* > nodes;
8079 list< const SMDS_MeshElement* > faces;
8080 return FindFreeBorder( theNode1, theNode2, theNode3, nodes, faces);
8083 //=======================================================================
8084 //function : SewFreeBorder
8086 //=======================================================================
8088 SMESH_MeshEditor::Sew_Error
8089 SMESH_MeshEditor::SewFreeBorder (const SMDS_MeshNode* theBordFirstNode,
8090 const SMDS_MeshNode* theBordSecondNode,
8091 const SMDS_MeshNode* theBordLastNode,
8092 const SMDS_MeshNode* theSideFirstNode,
8093 const SMDS_MeshNode* theSideSecondNode,
8094 const SMDS_MeshNode* theSideThirdNode,
8095 const bool theSideIsFreeBorder,
8096 const bool toCreatePolygons,
8097 const bool toCreatePolyedrs)
8099 myLastCreatedElems.Clear();
8100 myLastCreatedNodes.Clear();
8102 MESSAGE("::SewFreeBorder()");
8103 Sew_Error aResult = SEW_OK;
8105 // ====================================
8106 // find side nodes and elements
8107 // ====================================
8109 list< const SMDS_MeshNode* > nSide[ 2 ];
8110 list< const SMDS_MeshElement* > eSide[ 2 ];
8111 list< const SMDS_MeshNode* >::iterator nIt[ 2 ];
8112 list< const SMDS_MeshElement* >::iterator eIt[ 2 ];
8116 if (!FindFreeBorder(theBordFirstNode,theBordSecondNode,theBordLastNode,
8117 nSide[0], eSide[0])) {
8118 MESSAGE(" Free Border 1 not found " );
8119 aResult = SEW_BORDER1_NOT_FOUND;
8121 if (theSideIsFreeBorder) {
8124 if (!FindFreeBorder(theSideFirstNode, theSideSecondNode, theSideThirdNode,
8125 nSide[1], eSide[1])) {
8126 MESSAGE(" Free Border 2 not found " );
8127 aResult = ( aResult != SEW_OK ? SEW_BOTH_BORDERS_NOT_FOUND : SEW_BORDER2_NOT_FOUND );
8130 if ( aResult != SEW_OK )
8133 if (!theSideIsFreeBorder) {
8137 // -------------------------------------------------------------------------
8139 // 1. If nodes to merge are not coincident, move nodes of the free border
8140 // from the coord sys defined by the direction from the first to last
8141 // nodes of the border to the correspondent sys of the side 2
8142 // 2. On the side 2, find the links most co-directed with the correspondent
8143 // links of the free border
8144 // -------------------------------------------------------------------------
8146 // 1. Since sewing may break if there are volumes to split on the side 2,
8147 // we wont move nodes but just compute new coordinates for them
8148 typedef map<const SMDS_MeshNode*, gp_XYZ> TNodeXYZMap;
8149 TNodeXYZMap nBordXYZ;
8150 list< const SMDS_MeshNode* >& bordNodes = nSide[ 0 ];
8151 list< const SMDS_MeshNode* >::iterator nBordIt;
8153 gp_XYZ Pb1( theBordFirstNode->X(), theBordFirstNode->Y(), theBordFirstNode->Z() );
8154 gp_XYZ Pb2( theBordLastNode->X(), theBordLastNode->Y(), theBordLastNode->Z() );
8155 gp_XYZ Ps1( theSideFirstNode->X(), theSideFirstNode->Y(), theSideFirstNode->Z() );
8156 gp_XYZ Ps2( theSideSecondNode->X(), theSideSecondNode->Y(), theSideSecondNode->Z() );
8157 double tol2 = 1.e-8;
8158 gp_Vec Vbs1( Pb1 - Ps1 ),Vbs2( Pb2 - Ps2 );
8159 if ( Vbs1.SquareMagnitude() > tol2 || Vbs2.SquareMagnitude() > tol2 ) {
8160 // Need node movement.
8162 // find X and Z axes to create trsf
8163 gp_Vec Zb( Pb1 - Pb2 ), Zs( Ps1 - Ps2 );
8165 if ( X.SquareMagnitude() <= gp::Resolution() * gp::Resolution() )
8167 X = gp_Ax2( gp::Origin(), Zb ).XDirection();
8170 gp_Ax3 toBordAx( Pb1, Zb, X );
8171 gp_Ax3 fromSideAx( Ps1, Zs, X );
8172 gp_Ax3 toGlobalAx( gp::Origin(), gp::DZ(), gp::DX() );
8174 gp_Trsf toBordSys, fromSide2Sys;
8175 toBordSys.SetTransformation( toBordAx );
8176 fromSide2Sys.SetTransformation( fromSideAx, toGlobalAx );
8177 fromSide2Sys.SetScaleFactor( Zs.Magnitude() / Zb.Magnitude() );
8180 for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
8181 const SMDS_MeshNode* n = *nBordIt;
8182 gp_XYZ xyz( n->X(),n->Y(),n->Z() );
8183 toBordSys.Transforms( xyz );
8184 fromSide2Sys.Transforms( xyz );
8185 nBordXYZ.insert( TNodeXYZMap::value_type( n, xyz ));
8189 // just insert nodes XYZ in the nBordXYZ map
8190 for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
8191 const SMDS_MeshNode* n = *nBordIt;
8192 nBordXYZ.insert( TNodeXYZMap::value_type( n, gp_XYZ( n->X(),n->Y(),n->Z() )));
8196 // 2. On the side 2, find the links most co-directed with the correspondent
8197 // links of the free border
8199 list< const SMDS_MeshElement* >& sideElems = eSide[ 1 ];
8200 list< const SMDS_MeshNode* >& sideNodes = nSide[ 1 ];
8201 sideNodes.push_back( theSideFirstNode );
8203 bool hasVolumes = false;
8204 LinkID_Gen aLinkID_Gen( GetMeshDS() );
8205 set<long> foundSideLinkIDs, checkedLinkIDs;
8206 SMDS_VolumeTool volume;
8207 //const SMDS_MeshNode* faceNodes[ 4 ];
8209 const SMDS_MeshNode* sideNode;
8210 const SMDS_MeshElement* sideElem;
8211 const SMDS_MeshNode* prevSideNode = theSideFirstNode;
8212 const SMDS_MeshNode* prevBordNode = theBordFirstNode;
8213 nBordIt = bordNodes.begin();
8215 // border node position and border link direction to compare with
8216 gp_XYZ bordPos = nBordXYZ[ *nBordIt ];
8217 gp_XYZ bordDir = bordPos - nBordXYZ[ prevBordNode ];
8218 // choose next side node by link direction or by closeness to
8219 // the current border node:
8220 bool searchByDir = ( *nBordIt != theBordLastNode );
8222 // find the next node on the Side 2
8224 double maxDot = -DBL_MAX, minDist = DBL_MAX;
8226 checkedLinkIDs.clear();
8227 gp_XYZ prevXYZ( prevSideNode->X(), prevSideNode->Y(), prevSideNode->Z() );
8229 // loop on inverse elements of current node (prevSideNode) on the Side 2
8230 SMDS_ElemIteratorPtr invElemIt = prevSideNode->GetInverseElementIterator();
8231 while ( invElemIt->more() )
8233 const SMDS_MeshElement* elem = invElemIt->next();
8234 // prepare data for a loop on links coming to prevSideNode, of a face or a volume
8235 int iPrevNode, iNode = 0, nbNodes = elem->NbNodes();
8236 vector< const SMDS_MeshNode* > faceNodes( nbNodes, (const SMDS_MeshNode*)0 );
8237 bool isVolume = volume.Set( elem );
8238 const SMDS_MeshNode** nodes = isVolume ? volume.GetNodes() : & faceNodes[0];
8239 if ( isVolume ) // --volume
8241 else if ( elem->GetType()==SMDSAbs_Face ) { // --face
8242 // retrieve all face nodes and find iPrevNode - an index of the prevSideNode
8243 if(elem->IsQuadratic()) {
8244 const SMDS_VtkFace* F =
8245 dynamic_cast<const SMDS_VtkFace*>(elem);
8246 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8247 // use special nodes iterator
8248 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8249 while( anIter->more() ) {
8250 nodes[ iNode ] = cast2Node(anIter->next());
8251 if ( nodes[ iNode++ ] == prevSideNode )
8252 iPrevNode = iNode - 1;
8256 SMDS_ElemIteratorPtr nIt = elem->nodesIterator();
8257 while ( nIt->more() ) {
8258 nodes[ iNode ] = cast2Node( nIt->next() );
8259 if ( nodes[ iNode++ ] == prevSideNode )
8260 iPrevNode = iNode - 1;
8263 // there are 2 links to check
8268 // loop on links, to be precise, on the second node of links
8269 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
8270 const SMDS_MeshNode* n = nodes[ iNode ];
8272 if ( !volume.IsLinked( n, prevSideNode ))
8276 if ( iNode ) // a node before prevSideNode
8277 n = nodes[ iPrevNode == 0 ? elem->NbNodes() - 1 : iPrevNode - 1 ];
8278 else // a node after prevSideNode
8279 n = nodes[ iPrevNode + 1 == elem->NbNodes() ? 0 : iPrevNode + 1 ];
8281 // check if this link was already used
8282 long iLink = aLinkID_Gen.GetLinkID( prevSideNode, n );
8283 bool isJustChecked = !checkedLinkIDs.insert( iLink ).second;
8284 if (!isJustChecked &&
8285 foundSideLinkIDs.find( iLink ) == foundSideLinkIDs.end() )
8287 // test a link geometrically
8288 gp_XYZ nextXYZ ( n->X(), n->Y(), n->Z() );
8289 bool linkIsBetter = false;
8290 double dot = 0.0, dist = 0.0;
8291 if ( searchByDir ) { // choose most co-directed link
8292 dot = bordDir * ( nextXYZ - prevXYZ ).Normalized();
8293 linkIsBetter = ( dot > maxDot );
8295 else { // choose link with the node closest to bordPos
8296 dist = ( nextXYZ - bordPos ).SquareModulus();
8297 linkIsBetter = ( dist < minDist );
8299 if ( linkIsBetter ) {
8308 } // loop on inverse elements of prevSideNode
8311 MESSAGE(" Cant find path by links of the Side 2 ");
8312 return SEW_BAD_SIDE_NODES;
8314 sideNodes.push_back( sideNode );
8315 sideElems.push_back( sideElem );
8316 foundSideLinkIDs.insert ( linkID );
8317 prevSideNode = sideNode;
8319 if ( *nBordIt == theBordLastNode )
8320 searchByDir = false;
8322 // find the next border link to compare with
8323 gp_XYZ sidePos( sideNode->X(), sideNode->Y(), sideNode->Z() );
8324 searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8325 // move to next border node if sideNode is before forward border node (bordPos)
8326 while ( *nBordIt != theBordLastNode && !searchByDir ) {
8327 prevBordNode = *nBordIt;
8329 bordPos = nBordXYZ[ *nBordIt ];
8330 bordDir = bordPos - nBordXYZ[ prevBordNode ];
8331 searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8335 while ( sideNode != theSideSecondNode );
8337 if ( hasVolumes && sideNodes.size () != bordNodes.size() && !toCreatePolyedrs) {
8338 MESSAGE("VOLUME SPLITTING IS FORBIDDEN");
8339 return SEW_VOLUMES_TO_SPLIT; // volume splitting is forbidden
8341 } // end nodes search on the side 2
8343 // ============================
8344 // sew the border to the side 2
8345 // ============================
8347 int nbNodes[] = { nSide[0].size(), nSide[1].size() };
8348 int maxNbNodes = Max( nbNodes[0], nbNodes[1] );
8350 TListOfListOfNodes nodeGroupsToMerge;
8351 if ( nbNodes[0] == nbNodes[1] ||
8352 ( theSideIsFreeBorder && !theSideThirdNode)) {
8354 // all nodes are to be merged
8356 for (nIt[0] = nSide[0].begin(), nIt[1] = nSide[1].begin();
8357 nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end();
8358 nIt[0]++, nIt[1]++ )
8360 nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8361 nodeGroupsToMerge.back().push_back( *nIt[1] ); // to keep
8362 nodeGroupsToMerge.back().push_back( *nIt[0] ); // to remove
8367 // insert new nodes into the border and the side to get equal nb of segments
8369 // get normalized parameters of nodes on the borders
8370 //double param[ 2 ][ maxNbNodes ];
8372 param[0] = new double [ maxNbNodes ];
8373 param[1] = new double [ maxNbNodes ];
8375 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8376 list< const SMDS_MeshNode* >& nodes = nSide[ iBord ];
8377 list< const SMDS_MeshNode* >::iterator nIt = nodes.begin();
8378 const SMDS_MeshNode* nPrev = *nIt;
8379 double bordLength = 0;
8380 for ( iNode = 0; nIt != nodes.end(); nIt++, iNode++ ) { // loop on border nodes
8381 const SMDS_MeshNode* nCur = *nIt;
8382 gp_XYZ segment (nCur->X() - nPrev->X(),
8383 nCur->Y() - nPrev->Y(),
8384 nCur->Z() - nPrev->Z());
8385 double segmentLen = segment.Modulus();
8386 bordLength += segmentLen;
8387 param[ iBord ][ iNode ] = bordLength;
8390 // normalize within [0,1]
8391 for ( iNode = 0; iNode < nbNodes[ iBord ]; iNode++ ) {
8392 param[ iBord ][ iNode ] /= bordLength;
8396 // loop on border segments
8397 const SMDS_MeshNode *nPrev[ 2 ] = { 0, 0 };
8398 int i[ 2 ] = { 0, 0 };
8399 nIt[0] = nSide[0].begin(); eIt[0] = eSide[0].begin();
8400 nIt[1] = nSide[1].begin(); eIt[1] = eSide[1].begin();
8402 TElemOfNodeListMap insertMap;
8403 TElemOfNodeListMap::iterator insertMapIt;
8405 // key: elem to insert nodes into
8406 // value: 2 nodes to insert between + nodes to be inserted
8408 bool next[ 2 ] = { false, false };
8410 // find min adjacent segment length after sewing
8411 double nextParam = 10., prevParam = 0;
8412 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8413 if ( i[ iBord ] + 1 < nbNodes[ iBord ])
8414 nextParam = Min( nextParam, param[iBord][ i[iBord] + 1 ]);
8415 if ( i[ iBord ] > 0 )
8416 prevParam = Max( prevParam, param[iBord][ i[iBord] - 1 ]);
8418 double minParam = Min( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8419 double maxParam = Max( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8420 double minSegLen = Min( nextParam - minParam, maxParam - prevParam );
8422 // choose to insert or to merge nodes
8423 double du = param[ 1 ][ i[1] ] - param[ 0 ][ i[0] ];
8424 if ( Abs( du ) <= minSegLen * 0.2 ) {
8427 nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8428 const SMDS_MeshNode* n0 = *nIt[0];
8429 const SMDS_MeshNode* n1 = *nIt[1];
8430 nodeGroupsToMerge.back().push_back( n1 );
8431 nodeGroupsToMerge.back().push_back( n0 );
8432 // position of node of the border changes due to merge
8433 param[ 0 ][ i[0] ] += du;
8434 // move n1 for the sake of elem shape evaluation during insertion.
8435 // n1 will be removed by MergeNodes() anyway
8436 const_cast<SMDS_MeshNode*>( n0 )->setXYZ( n1->X(), n1->Y(), n1->Z() );
8437 next[0] = next[1] = true;
8442 int intoBord = ( du < 0 ) ? 0 : 1;
8443 const SMDS_MeshElement* elem = *eIt[ intoBord ];
8444 const SMDS_MeshNode* n1 = nPrev[ intoBord ];
8445 const SMDS_MeshNode* n2 = *nIt[ intoBord ];
8446 const SMDS_MeshNode* nIns = *nIt[ 1 - intoBord ];
8447 if ( intoBord == 1 ) {
8448 // move node of the border to be on a link of elem of the side
8449 gp_XYZ p1 (n1->X(), n1->Y(), n1->Z());
8450 gp_XYZ p2 (n2->X(), n2->Y(), n2->Z());
8451 double ratio = du / ( param[ 1 ][ i[1] ] - param[ 1 ][ i[1]-1 ]);
8452 gp_XYZ p = p2 * ( 1 - ratio ) + p1 * ratio;
8453 GetMeshDS()->MoveNode( nIns, p.X(), p.Y(), p.Z() );
8455 insertMapIt = insertMap.find( elem );
8456 bool notFound = ( insertMapIt == insertMap.end() );
8457 bool otherLink = ( !notFound && (*insertMapIt).second.front() != n1 );
8459 // insert into another link of the same element:
8460 // 1. perform insertion into the other link of the elem
8461 list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8462 const SMDS_MeshNode* n12 = nodeList.front(); nodeList.pop_front();
8463 const SMDS_MeshNode* n22 = nodeList.front(); nodeList.pop_front();
8464 InsertNodesIntoLink( elem, n12, n22, nodeList, toCreatePolygons );
8465 // 2. perform insertion into the link of adjacent faces
8467 const SMDS_MeshElement* adjElem = findAdjacentFace( n12, n22, elem );
8469 InsertNodesIntoLink( adjElem, n12, n22, nodeList, toCreatePolygons );
8473 if (toCreatePolyedrs) {
8474 // perform insertion into the links of adjacent volumes
8475 UpdateVolumes(n12, n22, nodeList);
8477 // 3. find an element appeared on n1 and n2 after the insertion
8478 insertMap.erase( elem );
8479 elem = findAdjacentFace( n1, n2, 0 );
8481 if ( notFound || otherLink ) {
8482 // add element and nodes of the side into the insertMap
8483 insertMapIt = insertMap.insert
8484 ( TElemOfNodeListMap::value_type( elem, list<const SMDS_MeshNode*>() )).first;
8485 (*insertMapIt).second.push_back( n1 );
8486 (*insertMapIt).second.push_back( n2 );
8488 // add node to be inserted into elem
8489 (*insertMapIt).second.push_back( nIns );
8490 next[ 1 - intoBord ] = true;
8493 // go to the next segment
8494 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8495 if ( next[ iBord ] ) {
8496 if ( i[ iBord ] != 0 && eIt[ iBord ] != eSide[ iBord ].end())
8498 nPrev[ iBord ] = *nIt[ iBord ];
8499 nIt[ iBord ]++; i[ iBord ]++;
8503 while ( nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end());
8505 // perform insertion of nodes into elements
8507 for (insertMapIt = insertMap.begin();
8508 insertMapIt != insertMap.end();
8511 const SMDS_MeshElement* elem = (*insertMapIt).first;
8512 list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8513 const SMDS_MeshNode* n1 = nodeList.front(); nodeList.pop_front();
8514 const SMDS_MeshNode* n2 = nodeList.front(); nodeList.pop_front();
8516 InsertNodesIntoLink( elem, n1, n2, nodeList, toCreatePolygons );
8518 if ( !theSideIsFreeBorder ) {
8519 // look for and insert nodes into the faces adjacent to elem
8521 const SMDS_MeshElement* adjElem = findAdjacentFace( n1, n2, elem );
8523 InsertNodesIntoLink( adjElem, n1, n2, nodeList, toCreatePolygons );
8528 if (toCreatePolyedrs) {
8529 // perform insertion into the links of adjacent volumes
8530 UpdateVolumes(n1, n2, nodeList);
8536 } // end: insert new nodes
8538 MergeNodes ( nodeGroupsToMerge );
8543 //=======================================================================
8544 //function : InsertNodesIntoLink
8545 //purpose : insert theNodesToInsert into theFace between theBetweenNode1
8546 // and theBetweenNode2 and split theElement
8547 //=======================================================================
8549 void SMESH_MeshEditor::InsertNodesIntoLink(const SMDS_MeshElement* theFace,
8550 const SMDS_MeshNode* theBetweenNode1,
8551 const SMDS_MeshNode* theBetweenNode2,
8552 list<const SMDS_MeshNode*>& theNodesToInsert,
8553 const bool toCreatePoly)
8555 if ( theFace->GetType() != SMDSAbs_Face ) return;
8557 // find indices of 2 link nodes and of the rest nodes
8558 int iNode = 0, il1, il2, i3, i4;
8559 il1 = il2 = i3 = i4 = -1;
8560 //const SMDS_MeshNode* nodes[ theFace->NbNodes() ];
8561 vector<const SMDS_MeshNode*> nodes( theFace->NbNodes() );
8563 if(theFace->IsQuadratic()) {
8564 const SMDS_VtkFace* F =
8565 dynamic_cast<const SMDS_VtkFace*>(theFace);
8566 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8567 // use special nodes iterator
8568 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8569 while( anIter->more() ) {
8570 const SMDS_MeshNode* n = cast2Node(anIter->next());
8571 if ( n == theBetweenNode1 )
8573 else if ( n == theBetweenNode2 )
8579 nodes[ iNode++ ] = n;
8583 SMDS_ElemIteratorPtr nodeIt = theFace->nodesIterator();
8584 while ( nodeIt->more() ) {
8585 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8586 if ( n == theBetweenNode1 )
8588 else if ( n == theBetweenNode2 )
8594 nodes[ iNode++ ] = n;
8597 if ( il1 < 0 || il2 < 0 || i3 < 0 )
8600 // arrange link nodes to go one after another regarding the face orientation
8601 bool reverse = ( Abs( il2 - il1 ) == 1 ? il2 < il1 : il1 < il2 );
8602 list<const SMDS_MeshNode *> aNodesToInsert = theNodesToInsert;
8607 aNodesToInsert.reverse();
8609 // check that not link nodes of a quadrangles are in good order
8610 int nbFaceNodes = theFace->NbNodes();
8611 if ( nbFaceNodes == 4 && i4 - i3 != 1 ) {
8617 if (toCreatePoly || theFace->IsPoly()) {
8620 vector<const SMDS_MeshNode *> poly_nodes (nbFaceNodes + aNodesToInsert.size());
8622 // add nodes of face up to first node of link
8625 if(theFace->IsQuadratic()) {
8626 const SMDS_VtkFace* F =
8627 dynamic_cast<const SMDS_VtkFace*>(theFace);
8628 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8629 // use special nodes iterator
8630 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8631 while( anIter->more() && !isFLN ) {
8632 const SMDS_MeshNode* n = cast2Node(anIter->next());
8633 poly_nodes[iNode++] = n;
8634 if (n == nodes[il1]) {
8638 // add nodes to insert
8639 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8640 for (; nIt != aNodesToInsert.end(); nIt++) {
8641 poly_nodes[iNode++] = *nIt;
8643 // add nodes of face starting from last node of link
8644 while ( anIter->more() ) {
8645 poly_nodes[iNode++] = cast2Node(anIter->next());
8649 SMDS_ElemIteratorPtr nodeIt = theFace->nodesIterator();
8650 while ( nodeIt->more() && !isFLN ) {
8651 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8652 poly_nodes[iNode++] = n;
8653 if (n == nodes[il1]) {
8657 // add nodes to insert
8658 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8659 for (; nIt != aNodesToInsert.end(); nIt++) {
8660 poly_nodes[iNode++] = *nIt;
8662 // add nodes of face starting from last node of link
8663 while ( nodeIt->more() ) {
8664 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8665 poly_nodes[iNode++] = n;
8669 // edit or replace the face
8670 SMESHDS_Mesh *aMesh = GetMeshDS();
8672 if (theFace->IsPoly()) {
8673 aMesh->ChangePolygonNodes(theFace, poly_nodes);
8676 int aShapeId = FindShape( theFace );
8678 SMDS_MeshElement* newElem = aMesh->AddPolygonalFace(poly_nodes);
8679 myLastCreatedElems.Append(newElem);
8680 if ( aShapeId && newElem )
8681 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8683 aMesh->RemoveElement(theFace);
8688 SMESHDS_Mesh *aMesh = GetMeshDS();
8689 if( !theFace->IsQuadratic() ) {
8691 // put aNodesToInsert between theBetweenNode1 and theBetweenNode2
8692 int nbLinkNodes = 2 + aNodesToInsert.size();
8693 //const SMDS_MeshNode* linkNodes[ nbLinkNodes ];
8694 vector<const SMDS_MeshNode*> linkNodes( nbLinkNodes );
8695 linkNodes[ 0 ] = nodes[ il1 ];
8696 linkNodes[ nbLinkNodes - 1 ] = nodes[ il2 ];
8697 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8698 for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
8699 linkNodes[ iNode++ ] = *nIt;
8701 // decide how to split a quadrangle: compare possible variants
8702 // and choose which of splits to be a quadrangle
8703 int i1, i2, iSplit, nbSplits = nbLinkNodes - 1, iBestQuad;
8704 if ( nbFaceNodes == 3 ) {
8705 iBestQuad = nbSplits;
8708 else if ( nbFaceNodes == 4 ) {
8709 SMESH::Controls::NumericalFunctorPtr aCrit( new SMESH::Controls::AspectRatio);
8710 double aBestRate = DBL_MAX;
8711 for ( int iQuad = 0; iQuad < nbSplits; iQuad++ ) {
8713 double aBadRate = 0;
8714 // evaluate elements quality
8715 for ( iSplit = 0; iSplit < nbSplits; iSplit++ ) {
8716 if ( iSplit == iQuad ) {
8717 SMDS_FaceOfNodes quad (linkNodes[ i1++ ],
8721 aBadRate += getBadRate( &quad, aCrit );
8724 SMDS_FaceOfNodes tria (linkNodes[ i1++ ],
8726 nodes[ iSplit < iQuad ? i4 : i3 ]);
8727 aBadRate += getBadRate( &tria, aCrit );
8731 if ( aBadRate < aBestRate ) {
8733 aBestRate = aBadRate;
8738 // create new elements
8739 int aShapeId = FindShape( theFace );
8742 for ( iSplit = 0; iSplit < nbSplits - 1; iSplit++ ) {
8743 SMDS_MeshElement* newElem = 0;
8744 if ( iSplit == iBestQuad )
8745 newElem = aMesh->AddFace (linkNodes[ i1++ ],
8750 newElem = aMesh->AddFace (linkNodes[ i1++ ],
8752 nodes[ iSplit < iBestQuad ? i4 : i3 ]);
8753 myLastCreatedElems.Append(newElem);
8754 if ( aShapeId && newElem )
8755 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8758 // change nodes of theFace
8759 const SMDS_MeshNode* newNodes[ 4 ];
8760 newNodes[ 0 ] = linkNodes[ i1 ];
8761 newNodes[ 1 ] = linkNodes[ i2 ];
8762 newNodes[ 2 ] = nodes[ iSplit >= iBestQuad ? i3 : i4 ];
8763 newNodes[ 3 ] = nodes[ i4 ];
8764 //aMesh->ChangeElementNodes( theFace, newNodes, iSplit == iBestQuad ? 4 : 3 );
8765 const SMDS_MeshElement* newElem = 0;
8766 if (iSplit == iBestQuad)
8767 newElem = aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2], newNodes[3] );
8769 newElem = aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2] );
8770 myLastCreatedElems.Append(newElem);
8771 if ( aShapeId && newElem )
8772 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8773 } // end if(!theFace->IsQuadratic())
8774 else { // theFace is quadratic
8775 // we have to split theFace on simple triangles and one simple quadrangle
8777 int nbshift = tmp*2;
8778 // shift nodes in nodes[] by nbshift
8780 for(i=0; i<nbshift; i++) {
8781 const SMDS_MeshNode* n = nodes[0];
8782 for(j=0; j<nbFaceNodes-1; j++) {
8783 nodes[j] = nodes[j+1];
8785 nodes[nbFaceNodes-1] = n;
8787 il1 = il1 - nbshift;
8788 // now have to insert nodes between n0 and n1 or n1 and n2 (see below)
8789 // n0 n1 n2 n0 n1 n2
8790 // +-----+-----+ +-----+-----+
8799 // create new elements
8800 int aShapeId = FindShape( theFace );
8803 if(nbFaceNodes==6) { // quadratic triangle
8804 SMDS_MeshElement* newElem =
8805 aMesh->AddFace(nodes[3],nodes[4],nodes[5]);
8806 myLastCreatedElems.Append(newElem);
8807 if ( aShapeId && newElem )
8808 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8809 if(theFace->IsMediumNode(nodes[il1])) {
8810 // create quadrangle
8811 newElem = aMesh->AddFace(nodes[0],nodes[1],nodes[3],nodes[5]);
8812 myLastCreatedElems.Append(newElem);
8813 if ( aShapeId && newElem )
8814 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8820 // create quadrangle
8821 newElem = aMesh->AddFace(nodes[1],nodes[2],nodes[3],nodes[5]);
8822 myLastCreatedElems.Append(newElem);
8823 if ( aShapeId && newElem )
8824 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8830 else { // nbFaceNodes==8 - quadratic quadrangle
8831 SMDS_MeshElement* newElem =
8832 aMesh->AddFace(nodes[3],nodes[4],nodes[5]);
8833 myLastCreatedElems.Append(newElem);
8834 if ( aShapeId && newElem )
8835 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8836 newElem = aMesh->AddFace(nodes[5],nodes[6],nodes[7]);
8837 myLastCreatedElems.Append(newElem);
8838 if ( aShapeId && newElem )
8839 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8840 newElem = aMesh->AddFace(nodes[5],nodes[7],nodes[3]);
8841 myLastCreatedElems.Append(newElem);
8842 if ( aShapeId && newElem )
8843 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8844 if(theFace->IsMediumNode(nodes[il1])) {
8845 // create quadrangle
8846 newElem = aMesh->AddFace(nodes[0],nodes[1],nodes[3],nodes[7]);
8847 myLastCreatedElems.Append(newElem);
8848 if ( aShapeId && newElem )
8849 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8855 // create quadrangle
8856 newElem = aMesh->AddFace(nodes[1],nodes[2],nodes[3],nodes[7]);
8857 myLastCreatedElems.Append(newElem);
8858 if ( aShapeId && newElem )
8859 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8865 // create needed triangles using n1,n2,n3 and inserted nodes
8866 int nbn = 2 + aNodesToInsert.size();
8867 //const SMDS_MeshNode* aNodes[nbn];
8868 vector<const SMDS_MeshNode*> aNodes(nbn);
8869 aNodes[0] = nodes[n1];
8870 aNodes[nbn-1] = nodes[n2];
8871 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8872 for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
8873 aNodes[iNode++] = *nIt;
8875 for(i=1; i<nbn; i++) {
8876 SMDS_MeshElement* newElem =
8877 aMesh->AddFace(aNodes[i-1],aNodes[i],nodes[n3]);
8878 myLastCreatedElems.Append(newElem);
8879 if ( aShapeId && newElem )
8880 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8884 aMesh->RemoveElement(theFace);
8887 //=======================================================================
8888 //function : UpdateVolumes
8890 //=======================================================================
8891 void SMESH_MeshEditor::UpdateVolumes (const SMDS_MeshNode* theBetweenNode1,
8892 const SMDS_MeshNode* theBetweenNode2,
8893 list<const SMDS_MeshNode*>& theNodesToInsert)
8895 myLastCreatedElems.Clear();
8896 myLastCreatedNodes.Clear();
8898 SMDS_ElemIteratorPtr invElemIt = theBetweenNode1->GetInverseElementIterator(SMDSAbs_Volume);
8899 while (invElemIt->more()) { // loop on inverse elements of theBetweenNode1
8900 const SMDS_MeshElement* elem = invElemIt->next();
8902 // check, if current volume has link theBetweenNode1 - theBetweenNode2
8903 SMDS_VolumeTool aVolume (elem);
8904 if (!aVolume.IsLinked(theBetweenNode1, theBetweenNode2))
8907 // insert new nodes in all faces of the volume, sharing link theBetweenNode1 - theBetweenNode2
8908 int iface, nbFaces = aVolume.NbFaces();
8909 vector<const SMDS_MeshNode *> poly_nodes;
8910 vector<int> quantities (nbFaces);
8912 for (iface = 0; iface < nbFaces; iface++) {
8913 int nbFaceNodes = aVolume.NbFaceNodes(iface), nbInserted = 0;
8914 // faceNodes will contain (nbFaceNodes + 1) nodes, last = first
8915 const SMDS_MeshNode** faceNodes = aVolume.GetFaceNodes(iface);
8917 for (int inode = 0; inode < nbFaceNodes; inode++) {
8918 poly_nodes.push_back(faceNodes[inode]);
8920 if (nbInserted == 0) {
8921 if (faceNodes[inode] == theBetweenNode1) {
8922 if (faceNodes[inode + 1] == theBetweenNode2) {
8923 nbInserted = theNodesToInsert.size();
8925 // add nodes to insert
8926 list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.begin();
8927 for (; nIt != theNodesToInsert.end(); nIt++) {
8928 poly_nodes.push_back(*nIt);
8932 else if (faceNodes[inode] == theBetweenNode2) {
8933 if (faceNodes[inode + 1] == theBetweenNode1) {
8934 nbInserted = theNodesToInsert.size();
8936 // add nodes to insert in reversed order
8937 list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.end();
8939 for (; nIt != theNodesToInsert.begin(); nIt--) {
8940 poly_nodes.push_back(*nIt);
8942 poly_nodes.push_back(*nIt);
8949 quantities[iface] = nbFaceNodes + nbInserted;
8952 // Replace or update the volume
8953 SMESHDS_Mesh *aMesh = GetMeshDS();
8955 if (elem->IsPoly()) {
8956 aMesh->ChangePolyhedronNodes(elem, poly_nodes, quantities);
8960 int aShapeId = FindShape( elem );
8962 SMDS_MeshElement* newElem =
8963 aMesh->AddPolyhedralVolume(poly_nodes, quantities);
8964 myLastCreatedElems.Append(newElem);
8965 if (aShapeId && newElem)
8966 aMesh->SetMeshElementOnShape(newElem, aShapeId);
8968 aMesh->RemoveElement(elem);
8975 //================================================================================
8977 * \brief Transform any volume into data of SMDSEntity_Polyhedra
8979 //================================================================================
8981 void volumeToPolyhedron( const SMDS_MeshElement* elem,
8982 vector<const SMDS_MeshNode *> & nodes,
8983 vector<int> & nbNodeInFaces )
8986 nbNodeInFaces.clear();
8987 SMDS_VolumeTool vTool ( elem );
8988 for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
8990 const SMDS_MeshNode** fNodes = vTool.GetFaceNodes( iF );
8991 nodes.insert( nodes.end(), fNodes, fNodes + vTool.NbFaceNodes( iF ));
8992 nbNodeInFaces.push_back( vTool.NbFaceNodes( iF ));
8997 //=======================================================================
8999 * \brief Convert elements contained in a submesh to quadratic
9000 * \return int - nb of checked elements
9002 //=======================================================================
9004 int SMESH_MeshEditor::convertElemToQuadratic(SMESHDS_SubMesh * theSm,
9005 SMESH_MesherHelper& theHelper,
9006 const bool theForce3d)
9009 if( !theSm ) return nbElem;
9011 vector<int> nbNodeInFaces;
9012 vector<const SMDS_MeshNode *> nodes;
9013 SMDS_ElemIteratorPtr ElemItr = theSm->GetElements();
9014 while(ElemItr->more())
9017 const SMDS_MeshElement* elem = ElemItr->next();
9018 if( !elem ) continue;
9020 // analyse a necessity of conversion
9021 const SMDSAbs_ElementType aType = elem->GetType();
9022 if ( aType < SMDSAbs_Edge || aType > SMDSAbs_Volume )
9024 const SMDSAbs_EntityType aGeomType = elem->GetEntityType();
9025 bool hasCentralNodes = false;
9026 if ( elem->IsQuadratic() )
9029 switch ( aGeomType ) {
9030 case SMDSEntity_Quad_Triangle:
9031 case SMDSEntity_Quad_Quadrangle:
9032 case SMDSEntity_Quad_Hexa:
9033 alreadyOK = !theHelper.GetIsBiQuadratic(); break;
9035 case SMDSEntity_BiQuad_Triangle:
9036 case SMDSEntity_BiQuad_Quadrangle:
9037 case SMDSEntity_TriQuad_Hexa:
9038 alreadyOK = theHelper.GetIsBiQuadratic();
9039 hasCentralNodes = true;
9044 // take into account already present modium nodes
9046 case SMDSAbs_Volume:
9047 theHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( elem )); break;
9049 theHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( elem )); break;
9051 theHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( elem )); break;
9057 // get elem data needed to re-create it
9059 const int id = elem->GetID();
9060 const int nbNodes = elem->NbCornerNodes();
9061 nodes.assign(elem->begin_nodes(), elem->end_nodes());
9062 if ( aGeomType == SMDSEntity_Polyhedra )
9063 nbNodeInFaces = static_cast<const SMDS_VtkVolume* >( elem )->GetQuantities();
9064 else if ( aGeomType == SMDSEntity_Hexagonal_Prism )
9065 volumeToPolyhedron( elem, nodes, nbNodeInFaces );
9067 // remove a linear element
9068 GetMeshDS()->RemoveFreeElement(elem, theSm, /*fromGroups=*/false);
9070 // remove central nodes of biquadratic elements (biquad->quad convertion)
9071 if ( hasCentralNodes )
9072 for ( size_t i = nbNodes * 2; i < nodes.size(); ++i )
9073 if ( nodes[i]->NbInverseElements() == 0 )
9074 GetMeshDS()->RemoveFreeNode( nodes[i], theSm, /*fromGroups=*/true );
9076 const SMDS_MeshElement* NewElem = 0;
9082 NewElem = theHelper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9090 NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
9093 NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9096 NewElem = theHelper.AddPolygonalFace(nodes, id, theForce3d);
9100 case SMDSAbs_Volume :
9104 case SMDSEntity_Tetra:
9105 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9107 case SMDSEntity_Pyramid:
9108 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], id, theForce3d);
9110 case SMDSEntity_Penta:
9111 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], nodes[5], id, theForce3d);
9113 case SMDSEntity_Hexa:
9114 case SMDSEntity_Quad_Hexa:
9115 case SMDSEntity_TriQuad_Hexa:
9116 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9117 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9119 case SMDSEntity_Hexagonal_Prism:
9121 NewElem = theHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9128 ReplaceElemInGroups( elem, NewElem, GetMeshDS());
9129 if( NewElem && NewElem->getshapeId() < 1 )
9130 theSm->AddElement( NewElem );
9134 //=======================================================================
9135 //function : ConvertToQuadratic
9137 //=======================================================================
9139 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d, const bool theToBiQuad)
9141 SMESHDS_Mesh* meshDS = GetMeshDS();
9143 SMESH_MesherHelper aHelper(*myMesh);
9145 aHelper.SetIsQuadratic( true );
9146 aHelper.SetIsBiQuadratic( theToBiQuad );
9147 aHelper.SetElementsOnShape(true);
9148 aHelper.ToFixNodeParameters( true );
9150 // convert elements assigned to sub-meshes
9151 int nbCheckedElems = 0;
9152 if ( myMesh->HasShapeToMesh() )
9154 if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9156 SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9157 while ( smIt->more() ) {
9158 SMESH_subMesh* sm = smIt->next();
9159 if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() ) {
9160 aHelper.SetSubShape( sm->GetSubShape() );
9161 nbCheckedElems += convertElemToQuadratic(smDS, aHelper, theForce3d);
9167 // convert elements NOT assigned to sub-meshes
9168 int totalNbElems = meshDS->NbEdges() + meshDS->NbFaces() + meshDS->NbVolumes();
9169 if ( nbCheckedElems < totalNbElems ) // not all elements are in sub-meshes
9171 aHelper.SetElementsOnShape(false);
9172 SMESHDS_SubMesh *smDS = 0;
9175 SMDS_EdgeIteratorPtr aEdgeItr = meshDS->edgesIterator();
9176 while( aEdgeItr->more() )
9178 const SMDS_MeshEdge* edge = aEdgeItr->next();
9179 if ( !edge->IsQuadratic() )
9181 int id = edge->GetID();
9182 const SMDS_MeshNode* n1 = edge->GetNode(0);
9183 const SMDS_MeshNode* n2 = edge->GetNode(1);
9185 meshDS->RemoveFreeElement(edge, smDS, /*fromGroups=*/false);
9187 const SMDS_MeshEdge* NewEdge = aHelper.AddEdge(n1, n2, id, theForce3d);
9188 ReplaceElemInGroups( edge, NewEdge, GetMeshDS());
9192 aHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( edge ));
9197 SMDS_FaceIteratorPtr aFaceItr = meshDS->facesIterator();
9198 while( aFaceItr->more() )
9200 const SMDS_MeshFace* face = aFaceItr->next();
9201 if ( !face ) continue;
9203 const SMDSAbs_EntityType type = face->GetEntityType();
9207 case SMDSEntity_Quad_Triangle:
9208 case SMDSEntity_Quad_Quadrangle:
9209 alreadyOK = !theToBiQuad;
9210 aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
9212 case SMDSEntity_BiQuad_Triangle:
9213 case SMDSEntity_BiQuad_Quadrangle:
9214 alreadyOK = theToBiQuad;
9215 aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
9217 default: alreadyOK = false;
9222 const int id = face->GetID();
9223 vector<const SMDS_MeshNode *> nodes ( face->begin_nodes(), face->end_nodes());
9225 meshDS->RemoveFreeElement(face, smDS, /*fromGroups=*/false);
9227 SMDS_MeshFace * NewFace = 0;
9230 case SMDSEntity_Triangle:
9231 case SMDSEntity_Quad_Triangle:
9232 case SMDSEntity_BiQuad_Triangle:
9233 NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
9234 if ( nodes.size() == 7 && nodes[6]->NbInverseElements() == 0 ) // rm a central node
9235 GetMeshDS()->RemoveFreeNode( nodes[6], /*sm=*/0, /*fromGroups=*/true );
9238 case SMDSEntity_Quadrangle:
9239 case SMDSEntity_Quad_Quadrangle:
9240 case SMDSEntity_BiQuad_Quadrangle:
9241 NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9242 if ( nodes.size() == 9 && nodes[8]->NbInverseElements() == 0 ) // rm a central node
9243 GetMeshDS()->RemoveFreeNode( nodes[8], /*sm=*/0, /*fromGroups=*/true );
9247 NewFace = aHelper.AddPolygonalFace(nodes, id, theForce3d);
9249 ReplaceElemInGroups( face, NewFace, GetMeshDS());
9253 vector<int> nbNodeInFaces;
9254 SMDS_VolumeIteratorPtr aVolumeItr = meshDS->volumesIterator();
9255 while(aVolumeItr->more())
9257 const SMDS_MeshVolume* volume = aVolumeItr->next();
9258 if ( !volume ) continue;
9260 const SMDSAbs_EntityType type = volume->GetEntityType();
9261 if ( volume->IsQuadratic() )
9266 case SMDSEntity_Quad_Hexa: alreadyOK = !theToBiQuad; break;
9267 case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; break;
9268 default: alreadyOK = true;
9272 aHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( volume ));
9276 const int id = volume->GetID();
9277 vector<const SMDS_MeshNode *> nodes (volume->begin_nodes(), volume->end_nodes());
9278 if ( type == SMDSEntity_Polyhedra )
9279 nbNodeInFaces = static_cast<const SMDS_VtkVolume* >(volume)->GetQuantities();
9280 else if ( type == SMDSEntity_Hexagonal_Prism )
9281 volumeToPolyhedron( volume, nodes, nbNodeInFaces );
9283 meshDS->RemoveFreeElement(volume, smDS, /*fromGroups=*/false);
9285 SMDS_MeshVolume * NewVolume = 0;
9288 case SMDSEntity_Tetra:
9289 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d );
9291 case SMDSEntity_Hexa:
9292 case SMDSEntity_Quad_Hexa:
9293 case SMDSEntity_TriQuad_Hexa:
9294 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9295 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9296 for ( size_t i = 20; i < nodes.size(); ++i ) // rm central nodes
9297 if ( nodes[i]->NbInverseElements() == 0 )
9298 GetMeshDS()->RemoveFreeNode( nodes[i], /*sm=*/0, /*fromGroups=*/true );
9300 case SMDSEntity_Pyramid:
9301 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9302 nodes[3], nodes[4], id, theForce3d);
9304 case SMDSEntity_Penta:
9305 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9306 nodes[3], nodes[4], nodes[5], id, theForce3d);
9308 case SMDSEntity_Hexagonal_Prism:
9310 NewVolume = aHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9312 ReplaceElemInGroups(volume, NewVolume, meshDS);
9317 { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9318 // aHelper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9319 // aHelper.FixQuadraticElements(myError);
9320 SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
9324 //================================================================================
9326 * \brief Makes given elements quadratic
9327 * \param theForce3d - if true, the medium nodes will be placed in the middle of link
9328 * \param theElements - elements to make quadratic
9330 //================================================================================
9332 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d,
9333 TIDSortedElemSet& theElements,
9334 const bool theToBiQuad)
9336 if ( theElements.empty() ) return;
9338 // we believe that all theElements are of the same type
9339 const SMDSAbs_ElementType elemType = (*theElements.begin())->GetType();
9341 // get all nodes shared by theElements
9342 TIDSortedNodeSet allNodes;
9343 TIDSortedElemSet::iterator eIt = theElements.begin();
9344 for ( ; eIt != theElements.end(); ++eIt )
9345 allNodes.insert( (*eIt)->begin_nodes(), (*eIt)->end_nodes() );
9347 // complete theElements with elements of lower dim whose all nodes are in allNodes
9349 TIDSortedElemSet quadAdjacentElems [ SMDSAbs_NbElementTypes ]; // quadratic adjacent elements
9350 TIDSortedElemSet checkedAdjacentElems [ SMDSAbs_NbElementTypes ];
9351 TIDSortedNodeSet::iterator nIt = allNodes.begin();
9352 for ( ; nIt != allNodes.end(); ++nIt )
9354 const SMDS_MeshNode* n = *nIt;
9355 SMDS_ElemIteratorPtr invIt = n->GetInverseElementIterator();
9356 while ( invIt->more() )
9358 const SMDS_MeshElement* e = invIt->next();
9359 const SMDSAbs_ElementType type = e->GetType();
9360 if ( e->IsQuadratic() )
9362 quadAdjacentElems[ type ].insert( e );
9365 switch ( e->GetEntityType() ) {
9366 case SMDSEntity_Quad_Triangle:
9367 case SMDSEntity_Quad_Quadrangle:
9368 case SMDSEntity_Quad_Hexa: alreadyOK = !theToBiQuad; break;
9369 case SMDSEntity_BiQuad_Triangle:
9370 case SMDSEntity_BiQuad_Quadrangle:
9371 case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; break;
9372 default: alreadyOK = true;
9377 if ( type >= elemType )
9378 continue; // same type or more complex linear element
9380 if ( !checkedAdjacentElems[ type ].insert( e ).second )
9381 continue; // e is already checked
9385 SMDS_NodeIteratorPtr nodeIt = e->nodeIterator();
9386 while ( nodeIt->more() && allIn )
9387 allIn = allNodes.count( nodeIt->next() );
9389 theElements.insert(e );
9393 SMESH_MesherHelper helper(*myMesh);
9394 helper.SetIsQuadratic( true );
9395 helper.SetIsBiQuadratic( theToBiQuad );
9397 // add links of quadratic adjacent elements to the helper
9399 if ( !quadAdjacentElems[SMDSAbs_Edge].empty() )
9400 for ( eIt = quadAdjacentElems[SMDSAbs_Edge].begin();
9401 eIt != quadAdjacentElems[SMDSAbs_Edge].end(); ++eIt )
9403 helper.AddTLinks( static_cast< const SMDS_MeshEdge*> (*eIt) );
9405 if ( !quadAdjacentElems[SMDSAbs_Face].empty() )
9406 for ( eIt = quadAdjacentElems[SMDSAbs_Face].begin();
9407 eIt != quadAdjacentElems[SMDSAbs_Face].end(); ++eIt )
9409 helper.AddTLinks( static_cast< const SMDS_MeshFace*> (*eIt) );
9411 if ( !quadAdjacentElems[SMDSAbs_Volume].empty() )
9412 for ( eIt = quadAdjacentElems[SMDSAbs_Volume].begin();
9413 eIt != quadAdjacentElems[SMDSAbs_Volume].end(); ++eIt )
9415 helper.AddTLinks( static_cast< const SMDS_MeshVolume*> (*eIt) );
9418 // make quadratic (or bi-tri-quadratic) elements instead of linear ones
9420 SMESHDS_Mesh* meshDS = GetMeshDS();
9421 SMESHDS_SubMesh* smDS = 0;
9422 for ( eIt = theElements.begin(); eIt != theElements.end(); ++eIt )
9424 const SMDS_MeshElement* elem = *eIt;
9427 int nbCentralNodes = 0;
9428 switch ( elem->GetEntityType() ) {
9429 // linear convertible
9430 case SMDSEntity_Edge:
9431 case SMDSEntity_Triangle:
9432 case SMDSEntity_Quadrangle:
9433 case SMDSEntity_Tetra:
9434 case SMDSEntity_Pyramid:
9435 case SMDSEntity_Hexa:
9436 case SMDSEntity_Penta: alreadyOK = false; nbCentralNodes = 0; break;
9437 // quadratic that can become bi-quadratic
9438 case SMDSEntity_Quad_Triangle:
9439 case SMDSEntity_Quad_Quadrangle:
9440 case SMDSEntity_Quad_Hexa: alreadyOK =!theToBiQuad; nbCentralNodes = 0; break;
9442 case SMDSEntity_BiQuad_Triangle:
9443 case SMDSEntity_BiQuad_Quadrangle: alreadyOK = theToBiQuad; nbCentralNodes = 1; break;
9444 case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; nbCentralNodes = 7; break;
9446 default: alreadyOK = true;
9448 if ( alreadyOK ) continue;
9450 const SMDSAbs_ElementType type = elem->GetType();
9451 const int id = elem->GetID();
9452 const int nbNodes = elem->NbCornerNodes();
9453 vector<const SMDS_MeshNode *> nodes ( elem->begin_nodes(), elem->end_nodes());
9455 helper.SetSubShape( elem->getshapeId() );
9457 if ( !smDS || !smDS->Contains( elem ))
9458 smDS = meshDS->MeshElements( elem->getshapeId() );
9459 meshDS->RemoveFreeElement(elem, smDS, /*fromGroups=*/false);
9461 SMDS_MeshElement * newElem = 0;
9464 case 4: // cases for most frequently used element types go first (for optimization)
9465 if ( type == SMDSAbs_Volume )
9466 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9468 newElem = helper.AddFace (nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9471 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9472 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9475 newElem = helper.AddFace (nodes[0], nodes[1], nodes[2], id, theForce3d);
9478 newElem = helper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9481 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9482 nodes[4], id, theForce3d);
9485 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9486 nodes[4], nodes[5], id, theForce3d);
9490 ReplaceElemInGroups( elem, newElem, meshDS);
9491 if( newElem && smDS )
9492 smDS->AddElement( newElem );
9494 // remove central nodes
9495 for ( size_t i = nodes.size() - nbCentralNodes; i < nodes.size(); ++i )
9496 if ( nodes[i]->NbInverseElements() == 0 )
9497 meshDS->RemoveFreeNode( nodes[i], smDS, /*fromGroups=*/true );
9499 } // loop on theElements
9502 { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9503 // helper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9504 // helper.FixQuadraticElements( myError );
9505 SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
9509 //=======================================================================
9511 * \brief Convert quadratic elements to linear ones and remove quadratic nodes
9512 * \return int - nb of checked elements
9514 //=======================================================================
9516 int SMESH_MeshEditor::removeQuadElem(SMESHDS_SubMesh * theSm,
9517 SMDS_ElemIteratorPtr theItr,
9518 const int theShapeID)
9521 SMESHDS_Mesh* meshDS = GetMeshDS();
9523 while( theItr->more() )
9525 const SMDS_MeshElement* elem = theItr->next();
9527 if( elem && elem->IsQuadratic())
9529 int id = elem->GetID();
9530 int nbCornerNodes = elem->NbCornerNodes();
9531 SMDSAbs_ElementType aType = elem->GetType();
9533 vector<const SMDS_MeshNode *> nodes( elem->begin_nodes(), elem->end_nodes() );
9535 //remove a quadratic element
9536 if ( !theSm || !theSm->Contains( elem ))
9537 theSm = meshDS->MeshElements( elem->getshapeId() );
9538 meshDS->RemoveFreeElement( elem, theSm, /*fromGroups=*/false );
9540 // remove medium nodes
9541 for ( unsigned i = nbCornerNodes; i < nodes.size(); ++i )
9542 if ( nodes[i]->NbInverseElements() == 0 )
9543 meshDS->RemoveFreeNode( nodes[i], theSm );
9545 // add a linear element
9546 nodes.resize( nbCornerNodes );
9547 SMDS_MeshElement * newElem = AddElement( nodes, aType, false, id );
9548 ReplaceElemInGroups(elem, newElem, meshDS);
9549 if( theSm && newElem )
9550 theSm->AddElement( newElem );
9556 //=======================================================================
9557 //function : ConvertFromQuadratic
9559 //=======================================================================
9561 bool SMESH_MeshEditor::ConvertFromQuadratic()
9563 int nbCheckedElems = 0;
9564 if ( myMesh->HasShapeToMesh() )
9566 if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9568 SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9569 while ( smIt->more() ) {
9570 SMESH_subMesh* sm = smIt->next();
9571 if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() )
9572 nbCheckedElems += removeQuadElem( smDS, smDS->GetElements(), sm->GetId() );
9578 GetMeshDS()->NbEdges() + GetMeshDS()->NbFaces() + GetMeshDS()->NbVolumes();
9579 if ( nbCheckedElems < totalNbElems ) // not all elements are in submeshes
9581 SMESHDS_SubMesh *aSM = 0;
9582 removeQuadElem( aSM, GetMeshDS()->elementsIterator(), 0 );
9590 //================================================================================
9592 * \brief Return true if all medium nodes of the element are in the node set
9594 //================================================================================
9596 bool allMediumNodesIn(const SMDS_MeshElement* elem, TIDSortedNodeSet& nodeSet )
9598 for ( int i = elem->NbCornerNodes(); i < elem->NbNodes(); ++i )
9599 if ( !nodeSet.count( elem->GetNode(i) ))
9605 //================================================================================
9607 * \brief Makes given elements linear
9609 //================================================================================
9611 void SMESH_MeshEditor::ConvertFromQuadratic(TIDSortedElemSet& theElements)
9613 if ( theElements.empty() ) return;
9615 // collect IDs of medium nodes of theElements; some of these nodes will be removed
9616 set<int> mediumNodeIDs;
9617 TIDSortedElemSet::iterator eIt = theElements.begin();
9618 for ( ; eIt != theElements.end(); ++eIt )
9620 const SMDS_MeshElement* e = *eIt;
9621 for ( int i = e->NbCornerNodes(); i < e->NbNodes(); ++i )
9622 mediumNodeIDs.insert( e->GetNode(i)->GetID() );
9625 // replace given elements by linear ones
9626 SMDS_ElemIteratorPtr elemIt = elemSetIterator( theElements );
9627 removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9629 // we need to convert remaining elements whose all medium nodes are in mediumNodeIDs
9630 // except those elements sharing medium nodes of quadratic element whose medium nodes
9631 // are not all in mediumNodeIDs
9633 // get remaining medium nodes
9634 TIDSortedNodeSet mediumNodes;
9635 set<int>::iterator nIdsIt = mediumNodeIDs.begin();
9636 for ( ; nIdsIt != mediumNodeIDs.end(); ++nIdsIt )
9637 if ( const SMDS_MeshNode* n = GetMeshDS()->FindNode( *nIdsIt ))
9638 mediumNodes.insert( mediumNodes.end(), n );
9640 // find more quadratic elements to convert
9641 TIDSortedElemSet moreElemsToConvert;
9642 TIDSortedNodeSet::iterator nIt = mediumNodes.begin();
9643 for ( ; nIt != mediumNodes.end(); ++nIt )
9645 SMDS_ElemIteratorPtr invIt = (*nIt)->GetInverseElementIterator();
9646 while ( invIt->more() )
9648 const SMDS_MeshElement* e = invIt->next();
9649 if ( e->IsQuadratic() && allMediumNodesIn( e, mediumNodes ))
9651 // find a more complex element including e and
9652 // whose medium nodes are not in mediumNodes
9653 bool complexFound = false;
9654 for ( int type = e->GetType() + 1; type < SMDSAbs_0DElement; ++type )
9656 SMDS_ElemIteratorPtr invIt2 =
9657 (*nIt)->GetInverseElementIterator( SMDSAbs_ElementType( type ));
9658 while ( invIt2->more() )
9660 const SMDS_MeshElement* eComplex = invIt2->next();
9661 if ( eComplex->IsQuadratic() && !allMediumNodesIn( eComplex, mediumNodes))
9663 int nbCommonNodes = SMESH_MeshAlgos::GetCommonNodes( e, eComplex ).size();
9664 if ( nbCommonNodes == e->NbNodes())
9666 complexFound = true;
9667 type = SMDSAbs_NbElementTypes; // to quit from the outer loop
9673 if ( !complexFound )
9674 moreElemsToConvert.insert( e );
9678 elemIt = elemSetIterator( moreElemsToConvert );
9679 removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9682 //=======================================================================
9683 //function : SewSideElements
9685 //=======================================================================
9687 SMESH_MeshEditor::Sew_Error
9688 SMESH_MeshEditor::SewSideElements (TIDSortedElemSet& theSide1,
9689 TIDSortedElemSet& theSide2,
9690 const SMDS_MeshNode* theFirstNode1,
9691 const SMDS_MeshNode* theFirstNode2,
9692 const SMDS_MeshNode* theSecondNode1,
9693 const SMDS_MeshNode* theSecondNode2)
9695 myLastCreatedElems.Clear();
9696 myLastCreatedNodes.Clear();
9698 MESSAGE ("::::SewSideElements()");
9699 if ( theSide1.size() != theSide2.size() )
9700 return SEW_DIFF_NB_OF_ELEMENTS;
9702 Sew_Error aResult = SEW_OK;
9704 // 1. Build set of faces representing each side
9705 // 2. Find which nodes of the side 1 to merge with ones on the side 2
9706 // 3. Replace nodes in elements of the side 1 and remove replaced nodes
9708 // =======================================================================
9709 // 1. Build set of faces representing each side:
9710 // =======================================================================
9711 // a. build set of nodes belonging to faces
9712 // b. complete set of faces: find missing faces whose nodes are in set of nodes
9713 // c. create temporary faces representing side of volumes if correspondent
9714 // face does not exist
9716 SMESHDS_Mesh* aMesh = GetMeshDS();
9717 // TODO algoritm not OK with vtkUnstructuredGrid: 2 meshes can't share nodes
9718 //SMDS_Mesh aTmpFacesMesh; // try to use the same mesh
9719 TIDSortedElemSet faceSet1, faceSet2;
9720 set<const SMDS_MeshElement*> volSet1, volSet2;
9721 set<const SMDS_MeshNode*> nodeSet1, nodeSet2;
9722 TIDSortedElemSet * faceSetPtr[] = { &faceSet1, &faceSet2 };
9723 set<const SMDS_MeshElement*> * volSetPtr[] = { &volSet1, &volSet2 };
9724 set<const SMDS_MeshNode*> * nodeSetPtr[] = { &nodeSet1, &nodeSet2 };
9725 TIDSortedElemSet * elemSetPtr[] = { &theSide1, &theSide2 };
9726 int iSide, iFace, iNode;
9728 list<const SMDS_MeshElement* > tempFaceList;
9729 for ( iSide = 0; iSide < 2; iSide++ ) {
9730 set<const SMDS_MeshNode*> * nodeSet = nodeSetPtr[ iSide ];
9731 TIDSortedElemSet * elemSet = elemSetPtr[ iSide ];
9732 TIDSortedElemSet * faceSet = faceSetPtr[ iSide ];
9733 set<const SMDS_MeshElement*> * volSet = volSetPtr [ iSide ];
9734 set<const SMDS_MeshElement*>::iterator vIt;
9735 TIDSortedElemSet::iterator eIt;
9736 set<const SMDS_MeshNode*>::iterator nIt;
9738 // check that given nodes belong to given elements
9739 const SMDS_MeshNode* n1 = ( iSide == 0 ) ? theFirstNode1 : theFirstNode2;
9740 const SMDS_MeshNode* n2 = ( iSide == 0 ) ? theSecondNode1 : theSecondNode2;
9741 int firstIndex = -1, secondIndex = -1;
9742 for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9743 const SMDS_MeshElement* elem = *eIt;
9744 if ( firstIndex < 0 ) firstIndex = elem->GetNodeIndex( n1 );
9745 if ( secondIndex < 0 ) secondIndex = elem->GetNodeIndex( n2 );
9746 if ( firstIndex > -1 && secondIndex > -1 ) break;
9748 if ( firstIndex < 0 || secondIndex < 0 ) {
9749 // we can simply return until temporary faces created
9750 return (iSide == 0 ) ? SEW_BAD_SIDE1_NODES : SEW_BAD_SIDE2_NODES;
9753 // -----------------------------------------------------------
9754 // 1a. Collect nodes of existing faces
9755 // and build set of face nodes in order to detect missing
9756 // faces corresponding to sides of volumes
9757 // -----------------------------------------------------------
9759 set< set <const SMDS_MeshNode*> > setOfFaceNodeSet;
9761 // loop on the given element of a side
9762 for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9763 //const SMDS_MeshElement* elem = *eIt;
9764 const SMDS_MeshElement* elem = *eIt;
9765 if ( elem->GetType() == SMDSAbs_Face ) {
9766 faceSet->insert( elem );
9767 set <const SMDS_MeshNode*> faceNodeSet;
9768 SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
9769 while ( nodeIt->more() ) {
9770 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9771 nodeSet->insert( n );
9772 faceNodeSet.insert( n );
9774 setOfFaceNodeSet.insert( faceNodeSet );
9776 else if ( elem->GetType() == SMDSAbs_Volume )
9777 volSet->insert( elem );
9779 // ------------------------------------------------------------------------------
9780 // 1b. Complete set of faces: find missing faces whose nodes are in set of nodes
9781 // ------------------------------------------------------------------------------
9783 for ( nIt = nodeSet->begin(); nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
9784 SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
9785 while ( fIt->more() ) { // loop on faces sharing a node
9786 const SMDS_MeshElement* f = fIt->next();
9787 if ( faceSet->find( f ) == faceSet->end() ) {
9788 // check if all nodes are in nodeSet and
9789 // complete setOfFaceNodeSet if they are
9790 set <const SMDS_MeshNode*> faceNodeSet;
9791 SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
9792 bool allInSet = true;
9793 while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
9794 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9795 if ( nodeSet->find( n ) == nodeSet->end() )
9798 faceNodeSet.insert( n );
9801 faceSet->insert( f );
9802 setOfFaceNodeSet.insert( faceNodeSet );
9808 // -------------------------------------------------------------------------
9809 // 1c. Create temporary faces representing sides of volumes if correspondent
9810 // face does not exist
9811 // -------------------------------------------------------------------------
9813 if ( !volSet->empty() ) {
9814 //int nodeSetSize = nodeSet->size();
9816 // loop on given volumes
9817 for ( vIt = volSet->begin(); vIt != volSet->end(); vIt++ ) {
9818 SMDS_VolumeTool vol (*vIt);
9819 // loop on volume faces: find free faces
9820 // --------------------------------------
9821 list<const SMDS_MeshElement* > freeFaceList;
9822 for ( iFace = 0; iFace < vol.NbFaces(); iFace++ ) {
9823 if ( !vol.IsFreeFace( iFace ))
9825 // check if there is already a face with same nodes in a face set
9826 const SMDS_MeshElement* aFreeFace = 0;
9827 const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iFace );
9828 int nbNodes = vol.NbFaceNodes( iFace );
9829 set <const SMDS_MeshNode*> faceNodeSet;
9830 vol.GetFaceNodes( iFace, faceNodeSet );
9831 bool isNewFace = setOfFaceNodeSet.insert( faceNodeSet ).second;
9833 // no such a face is given but it still can exist, check it
9834 vector<const SMDS_MeshNode *> nodes ( fNodes, fNodes + nbNodes);
9835 aFreeFace = aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false );
9838 // create a temporary face
9839 if ( nbNodes == 3 ) {
9840 //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2] );
9841 aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2] );
9843 else if ( nbNodes == 4 ) {
9844 //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
9845 aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
9848 vector<const SMDS_MeshNode *> poly_nodes ( fNodes, & fNodes[nbNodes]);
9849 //aFreeFace = aTmpFacesMesh.AddPolygonalFace(poly_nodes);
9850 aFreeFace = aMesh->AddPolygonalFace(poly_nodes);
9853 tempFaceList.push_back( aFreeFace );
9857 freeFaceList.push_back( aFreeFace );
9859 } // loop on faces of a volume
9861 // choose one of several free faces of a volume
9862 // --------------------------------------------
9863 if ( freeFaceList.size() > 1 ) {
9864 // choose a face having max nb of nodes shared by other elems of a side
9865 int maxNbNodes = -1;
9866 list<const SMDS_MeshElement* >::iterator fIt = freeFaceList.begin();
9867 while ( fIt != freeFaceList.end() ) { // loop on free faces
9868 int nbSharedNodes = 0;
9869 SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
9870 while ( nodeIt->more() ) { // loop on free face nodes
9871 const SMDS_MeshNode* n =
9872 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9873 SMDS_ElemIteratorPtr invElemIt = n->GetInverseElementIterator();
9874 while ( invElemIt->more() ) {
9875 const SMDS_MeshElement* e = invElemIt->next();
9876 nbSharedNodes += faceSet->count( e );
9877 nbSharedNodes += elemSet->count( e );
9880 if ( nbSharedNodes > maxNbNodes ) {
9881 maxNbNodes = nbSharedNodes;
9882 freeFaceList.erase( freeFaceList.begin(), fIt++ );
9884 else if ( nbSharedNodes == maxNbNodes ) {
9888 freeFaceList.erase( fIt++ ); // here fIt++ occurs before erase
9891 if ( freeFaceList.size() > 1 )
9893 // could not choose one face, use another way
9894 // choose a face most close to the bary center of the opposite side
9895 gp_XYZ aBC( 0., 0., 0. );
9896 set <const SMDS_MeshNode*> addedNodes;
9897 TIDSortedElemSet * elemSet2 = elemSetPtr[ 1 - iSide ];
9898 eIt = elemSet2->begin();
9899 for ( eIt = elemSet2->begin(); eIt != elemSet2->end(); eIt++ ) {
9900 SMDS_ElemIteratorPtr nodeIt = (*eIt)->nodesIterator();
9901 while ( nodeIt->more() ) { // loop on free face nodes
9902 const SMDS_MeshNode* n =
9903 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9904 if ( addedNodes.insert( n ).second )
9905 aBC += gp_XYZ( n->X(),n->Y(),n->Z() );
9908 aBC /= addedNodes.size();
9909 double minDist = DBL_MAX;
9910 fIt = freeFaceList.begin();
9911 while ( fIt != freeFaceList.end() ) { // loop on free faces
9913 SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
9914 while ( nodeIt->more() ) { // loop on free face nodes
9915 const SMDS_MeshNode* n =
9916 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9917 gp_XYZ p( n->X(),n->Y(),n->Z() );
9918 dist += ( aBC - p ).SquareModulus();
9920 if ( dist < minDist ) {
9922 freeFaceList.erase( freeFaceList.begin(), fIt++ );
9925 fIt = freeFaceList.erase( fIt++ );
9928 } // choose one of several free faces of a volume
9930 if ( freeFaceList.size() == 1 ) {
9931 const SMDS_MeshElement* aFreeFace = freeFaceList.front();
9932 faceSet->insert( aFreeFace );
9933 // complete a node set with nodes of a found free face
9934 // for ( iNode = 0; iNode < ; iNode++ )
9935 // nodeSet->insert( fNodes[ iNode ] );
9938 } // loop on volumes of a side
9940 // // complete a set of faces if new nodes in a nodeSet appeared
9941 // // ----------------------------------------------------------
9942 // if ( nodeSetSize != nodeSet->size() ) {
9943 // for ( ; nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
9944 // SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
9945 // while ( fIt->more() ) { // loop on faces sharing a node
9946 // const SMDS_MeshElement* f = fIt->next();
9947 // if ( faceSet->find( f ) == faceSet->end() ) {
9948 // // check if all nodes are in nodeSet and
9949 // // complete setOfFaceNodeSet if they are
9950 // set <const SMDS_MeshNode*> faceNodeSet;
9951 // SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
9952 // bool allInSet = true;
9953 // while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
9954 // const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9955 // if ( nodeSet->find( n ) == nodeSet->end() )
9956 // allInSet = false;
9958 // faceNodeSet.insert( n );
9960 // if ( allInSet ) {
9961 // faceSet->insert( f );
9962 // setOfFaceNodeSet.insert( faceNodeSet );
9968 } // Create temporary faces, if there are volumes given
9971 if ( faceSet1.size() != faceSet2.size() ) {
9972 // delete temporary faces: they are in reverseElements of actual nodes
9973 // SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
9974 // while ( tmpFaceIt->more() )
9975 // aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
9976 // list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
9977 // for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
9978 // aMesh->RemoveElement(*tmpFaceIt);
9979 MESSAGE("Diff nb of faces");
9980 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9983 // ============================================================
9984 // 2. Find nodes to merge:
9985 // bind a node to remove to a node to put instead
9986 // ============================================================
9988 TNodeNodeMap nReplaceMap; // bind a node to remove to a node to put instead
9989 if ( theFirstNode1 != theFirstNode2 )
9990 nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
9991 if ( theSecondNode1 != theSecondNode2 )
9992 nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
9994 LinkID_Gen aLinkID_Gen( GetMeshDS() );
9995 set< long > linkIdSet; // links to process
9996 linkIdSet.insert( aLinkID_Gen.GetLinkID( theFirstNode1, theSecondNode1 ));
9998 typedef pair< const SMDS_MeshNode*, const SMDS_MeshNode* > NLink;
9999 list< NLink > linkList[2];
10000 linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
10001 linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
10002 // loop on links in linkList; find faces by links and append links
10003 // of the found faces to linkList
10004 list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
10005 for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ )
10007 NLink link[] = { *linkIt[0], *linkIt[1] };
10008 long linkID = aLinkID_Gen.GetLinkID( link[0].first, link[0].second );
10009 if ( !linkIdSet.count( linkID ) )
10012 // by links, find faces in the face sets,
10013 // and find indices of link nodes in the found faces;
10014 // in a face set, there is only one or no face sharing a link
10015 // ---------------------------------------------------------------
10017 const SMDS_MeshElement* face[] = { 0, 0 };
10018 vector<const SMDS_MeshNode*> fnodes[2];
10019 int iLinkNode[2][2];
10020 TIDSortedElemSet avoidSet;
10021 for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
10022 const SMDS_MeshNode* n1 = link[iSide].first;
10023 const SMDS_MeshNode* n2 = link[iSide].second;
10024 //cout << "Side " << iSide << " ";
10025 //cout << "L( " << n1->GetID() << ", " << n2->GetID() << " ) " << endl;
10026 // find a face by two link nodes
10027 face[ iSide ] = SMESH_MeshAlgos::FindFaceInSet( n1, n2,
10028 *faceSetPtr[ iSide ], avoidSet,
10029 &iLinkNode[iSide][0],
10030 &iLinkNode[iSide][1] );
10031 if ( face[ iSide ])
10033 //cout << " F " << face[ iSide]->GetID() <<endl;
10034 faceSetPtr[ iSide ]->erase( face[ iSide ]);
10035 // put face nodes to fnodes
10036 if ( face[ iSide ]->IsQuadratic() )
10038 // use interlaced nodes iterator
10039 const SMDS_VtkFace* F = dynamic_cast<const SMDS_VtkFace*>( face[ iSide ]);
10040 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
10041 SMDS_ElemIteratorPtr nIter = F->interlacedNodesElemIterator();
10042 while ( nIter->more() )
10043 fnodes[ iSide ].push_back( cast2Node( nIter->next() ));
10047 fnodes[ iSide ].assign( face[ iSide ]->begin_nodes(),
10048 face[ iSide ]->end_nodes() );
10050 fnodes[ iSide ].push_back( fnodes[ iSide ].front());
10054 // check similarity of elements of the sides
10055 if (aResult == SEW_OK && (( face[0] && !face[1] ) || ( !face[0] && face[1] ))) {
10056 MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
10057 if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
10058 aResult = ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10061 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10063 break; // do not return because it's necessary to remove tmp faces
10066 // set nodes to merge
10067 // -------------------
10069 if ( face[0] && face[1] ) {
10070 const int nbNodes = face[0]->NbNodes();
10071 if ( nbNodes != face[1]->NbNodes() ) {
10072 MESSAGE("Diff nb of face nodes");
10073 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10074 break; // do not return because it s necessary to remove tmp faces
10076 bool reverse[] = { false, false }; // order of nodes in the link
10077 for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
10078 // analyse link orientation in faces
10079 int i1 = iLinkNode[ iSide ][ 0 ];
10080 int i2 = iLinkNode[ iSide ][ 1 ];
10081 reverse[ iSide ] = Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1;
10083 int di1 = reverse[0] ? -1 : +1, i1 = iLinkNode[0][1] + di1;
10084 int di2 = reverse[1] ? -1 : +1, i2 = iLinkNode[1][1] + di2;
10085 for ( int i = nbNodes - 2; i > 0; --i, i1 += di1, i2 += di2 )
10087 nReplaceMap.insert ( make_pair ( fnodes[0][ ( i1 + nbNodes ) % nbNodes ],
10088 fnodes[1][ ( i2 + nbNodes ) % nbNodes ]));
10091 // add other links of the faces to linkList
10092 // -----------------------------------------
10094 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
10095 linkID = aLinkID_Gen.GetLinkID( fnodes[0][iNode], fnodes[0][iNode+1] );
10096 pair< set<long>::iterator, bool > iter_isnew = linkIdSet.insert( linkID );
10097 if ( !iter_isnew.second ) { // already in a set: no need to process
10098 linkIdSet.erase( iter_isnew.first );
10100 else // new in set == encountered for the first time: add
10102 const SMDS_MeshNode* n1 = fnodes[0][ iNode ];
10103 const SMDS_MeshNode* n2 = fnodes[0][ iNode + 1];
10104 linkList[0].push_back ( NLink( n1, n2 ));
10105 linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10110 if ( faceSetPtr[0]->empty() || faceSetPtr[1]->empty() )
10113 } // loop on link lists
10115 if ( aResult == SEW_OK &&
10116 ( //linkIt[0] != linkList[0].end() ||
10117 !faceSetPtr[0]->empty() || !faceSetPtr[1]->empty() )) {
10118 MESSAGE( (linkIt[0] != linkList[0].end()) <<" "<< (faceSetPtr[0]->empty()) <<
10119 " " << (faceSetPtr[1]->empty()));
10120 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10123 // ====================================================================
10124 // 3. Replace nodes in elements of the side 1 and remove replaced nodes
10125 // ====================================================================
10127 // delete temporary faces
10128 // SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
10129 // while ( tmpFaceIt->more() )
10130 // aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
10131 list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
10132 for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
10133 aMesh->RemoveElement(*tmpFaceIt);
10135 if ( aResult != SEW_OK)
10138 list< int > nodeIDsToRemove/*, elemIDsToRemove*/;
10139 // loop on nodes replacement map
10140 TNodeNodeMap::iterator nReplaceMapIt = nReplaceMap.begin(), nnIt;
10141 for ( ; nReplaceMapIt != nReplaceMap.end(); nReplaceMapIt++ )
10142 if ( (*nReplaceMapIt).first != (*nReplaceMapIt).second ) {
10143 const SMDS_MeshNode* nToRemove = (*nReplaceMapIt).first;
10144 nodeIDsToRemove.push_back( nToRemove->GetID() );
10145 // loop on elements sharing nToRemove
10146 SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
10147 while ( invElemIt->more() ) {
10148 const SMDS_MeshElement* e = invElemIt->next();
10149 // get a new suite of nodes: make replacement
10150 int nbReplaced = 0, i = 0, nbNodes = e->NbNodes();
10151 vector< const SMDS_MeshNode*> nodes( nbNodes );
10152 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
10153 while ( nIt->more() ) {
10154 const SMDS_MeshNode* n =
10155 static_cast<const SMDS_MeshNode*>( nIt->next() );
10156 nnIt = nReplaceMap.find( n );
10157 if ( nnIt != nReplaceMap.end() ) {
10159 n = (*nnIt).second;
10163 // if ( nbReplaced == nbNodes && e->GetType() == SMDSAbs_Face )
10164 // elemIDsToRemove.push_back( e->GetID() );
10168 SMDSAbs_ElementType etyp = e->GetType();
10169 SMDS_MeshElement* newElem = this->AddElement(nodes, etyp, false);
10172 myLastCreatedElems.Append(newElem);
10173 AddToSameGroups(newElem, e, aMesh);
10174 int aShapeId = e->getshapeId();
10177 aMesh->SetMeshElementOnShape( newElem, aShapeId );
10180 aMesh->RemoveElement(e);
10185 Remove( nodeIDsToRemove, true );
10190 //================================================================================
10192 * \brief Find corresponding nodes in two sets of faces
10193 * \param theSide1 - first face set
10194 * \param theSide2 - second first face
10195 * \param theFirstNode1 - a boundary node of set 1
10196 * \param theFirstNode2 - a node of set 2 corresponding to theFirstNode1
10197 * \param theSecondNode1 - a boundary node of set 1 linked with theFirstNode1
10198 * \param theSecondNode2 - a node of set 2 corresponding to theSecondNode1
10199 * \param nReplaceMap - output map of corresponding nodes
10200 * \return bool - is a success or not
10202 //================================================================================
10205 //#define DEBUG_MATCHING_NODES
10208 SMESH_MeshEditor::Sew_Error
10209 SMESH_MeshEditor::FindMatchingNodes(set<const SMDS_MeshElement*>& theSide1,
10210 set<const SMDS_MeshElement*>& theSide2,
10211 const SMDS_MeshNode* theFirstNode1,
10212 const SMDS_MeshNode* theFirstNode2,
10213 const SMDS_MeshNode* theSecondNode1,
10214 const SMDS_MeshNode* theSecondNode2,
10215 TNodeNodeMap & nReplaceMap)
10217 set<const SMDS_MeshElement*> * faceSetPtr[] = { &theSide1, &theSide2 };
10219 nReplaceMap.clear();
10220 if ( theFirstNode1 != theFirstNode2 )
10221 nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
10222 if ( theSecondNode1 != theSecondNode2 )
10223 nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
10225 set< SMESH_TLink > linkSet; // set of nodes where order of nodes is ignored
10226 linkSet.insert( SMESH_TLink( theFirstNode1, theSecondNode1 ));
10228 list< NLink > linkList[2];
10229 linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
10230 linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
10232 // loop on links in linkList; find faces by links and append links
10233 // of the found faces to linkList
10234 list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
10235 for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ ) {
10236 NLink link[] = { *linkIt[0], *linkIt[1] };
10237 if ( linkSet.find( link[0] ) == linkSet.end() )
10240 // by links, find faces in the face sets,
10241 // and find indices of link nodes in the found faces;
10242 // in a face set, there is only one or no face sharing a link
10243 // ---------------------------------------------------------------
10245 const SMDS_MeshElement* face[] = { 0, 0 };
10246 list<const SMDS_MeshNode*> notLinkNodes[2];
10247 //bool reverse[] = { false, false }; // order of notLinkNodes
10249 for ( int iSide = 0; iSide < 2; iSide++ ) // loop on 2 sides
10251 const SMDS_MeshNode* n1 = link[iSide].first;
10252 const SMDS_MeshNode* n2 = link[iSide].second;
10253 set<const SMDS_MeshElement*> * faceSet = faceSetPtr[ iSide ];
10254 set< const SMDS_MeshElement* > facesOfNode1;
10255 for ( int iNode = 0; iNode < 2; iNode++ ) // loop on 2 nodes of a link
10257 // during a loop of the first node, we find all faces around n1,
10258 // during a loop of the second node, we find one face sharing both n1 and n2
10259 const SMDS_MeshNode* n = iNode ? n1 : n2; // a node of a link
10260 SMDS_ElemIteratorPtr fIt = n->GetInverseElementIterator(SMDSAbs_Face);
10261 while ( fIt->more() ) { // loop on faces sharing a node
10262 const SMDS_MeshElement* f = fIt->next();
10263 if (faceSet->find( f ) != faceSet->end() && // f is in face set
10264 ! facesOfNode1.insert( f ).second ) // f encounters twice
10266 if ( face[ iSide ] ) {
10267 MESSAGE( "2 faces per link " );
10268 return ( iSide ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10271 faceSet->erase( f );
10273 // get not link nodes
10274 int nbN = f->NbNodes();
10275 if ( f->IsQuadratic() )
10277 nbNodes[ iSide ] = nbN;
10278 list< const SMDS_MeshNode* > & nodes = notLinkNodes[ iSide ];
10279 int i1 = f->GetNodeIndex( n1 );
10280 int i2 = f->GetNodeIndex( n2 );
10281 int iEnd = nbN, iBeg = -1, iDelta = 1;
10282 bool reverse = ( Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1 );
10284 std::swap( iEnd, iBeg ); iDelta = -1;
10289 if ( i == iEnd ) i = iBeg + iDelta;
10290 if ( i == i1 ) break;
10291 nodes.push_back ( f->GetNode( i ) );
10297 // check similarity of elements of the sides
10298 if (( face[0] && !face[1] ) || ( !face[0] && face[1] )) {
10299 MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
10300 if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
10301 return ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10304 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10308 // set nodes to merge
10309 // -------------------
10311 if ( face[0] && face[1] ) {
10312 if ( nbNodes[0] != nbNodes[1] ) {
10313 MESSAGE("Diff nb of face nodes");
10314 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10316 #ifdef DEBUG_MATCHING_NODES
10317 MESSAGE ( " Link 1: " << link[0].first->GetID() <<" "<< link[0].second->GetID()
10318 << " F 1: " << face[0] << "| Link 2: " << link[1].first->GetID() <<" "
10319 << link[1].second->GetID() << " F 2: " << face[1] << " | Bind: " ) ;
10321 int nbN = nbNodes[0];
10323 list<const SMDS_MeshNode*>::iterator n1 = notLinkNodes[0].begin();
10324 list<const SMDS_MeshNode*>::iterator n2 = notLinkNodes[1].begin();
10325 for ( int i = 0 ; i < nbN - 2; ++i ) {
10326 #ifdef DEBUG_MATCHING_NODES
10327 MESSAGE ( (*n1)->GetID() << " to " << (*n2)->GetID() );
10329 nReplaceMap.insert( make_pair( *(n1++), *(n2++) ));
10333 // add other links of the face 1 to linkList
10334 // -----------------------------------------
10336 const SMDS_MeshElement* f0 = face[0];
10337 const SMDS_MeshNode* n1 = f0->GetNode( nbN - 1 );
10338 for ( int i = 0; i < nbN; i++ )
10340 const SMDS_MeshNode* n2 = f0->GetNode( i );
10341 pair< set< SMESH_TLink >::iterator, bool > iter_isnew =
10342 linkSet.insert( SMESH_TLink( n1, n2 ));
10343 if ( !iter_isnew.second ) { // already in a set: no need to process
10344 linkSet.erase( iter_isnew.first );
10346 else // new in set == encountered for the first time: add
10348 #ifdef DEBUG_MATCHING_NODES
10349 MESSAGE ( "Add link 1: " << n1->GetID() << " " << n2->GetID() << " "
10350 << " | link 2: " << nReplaceMap[n1]->GetID() << " " << nReplaceMap[n2]->GetID() << " " );
10352 linkList[0].push_back ( NLink( n1, n2 ));
10353 linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10358 } // loop on link lists
10363 //================================================================================
10365 * \brief Create elements equal (on same nodes) to given ones
10366 * \param [in] theElements - a set of elems to duplicate. If it is empty, all
10367 * elements of the uppest dimension are duplicated.
10369 //================================================================================
10371 void SMESH_MeshEditor::DoubleElements( const TIDSortedElemSet& theElements )
10373 ClearLastCreated();
10374 SMESHDS_Mesh* mesh = GetMeshDS();
10376 // get an element type and an iterator over elements
10378 SMDSAbs_ElementType type;
10379 SMDS_ElemIteratorPtr elemIt;
10380 vector< const SMDS_MeshElement* > allElems;
10381 if ( theElements.empty() )
10383 if ( mesh->NbNodes() == 0 )
10385 // get most complex type
10386 SMDSAbs_ElementType types[SMDSAbs_NbElementTypes] = {
10387 SMDSAbs_Volume, SMDSAbs_Face, SMDSAbs_Edge,
10388 SMDSAbs_0DElement, SMDSAbs_Ball, SMDSAbs_Node
10390 for ( int i = 0; i < SMDSAbs_NbElementTypes; ++i )
10391 if ( mesh->GetMeshInfo().NbElements( types[i] ))
10396 // put all elements in the vector <allElems>
10397 allElems.reserve( mesh->GetMeshInfo().NbElements( type ));
10398 elemIt = mesh->elementsIterator( type );
10399 while ( elemIt->more() )
10400 allElems.push_back( elemIt->next());
10401 elemIt = elemSetIterator( allElems );
10405 type = (*theElements.begin())->GetType();
10406 elemIt = elemSetIterator( theElements );
10409 // duplicate elements
10411 if ( type == SMDSAbs_Ball )
10413 SMDS_UnstructuredGrid* vtkGrid = mesh->getGrid();
10414 while ( elemIt->more() )
10416 const SMDS_MeshElement* elem = elemIt->next();
10417 if ( elem->GetType() != SMDSAbs_Ball )
10419 if (( elem = mesh->AddBall( elem->GetNode(0),
10420 vtkGrid->GetBallDiameter( elem->getVtkId() ))))
10421 myLastCreatedElems.Append( elem );
10426 vector< const SMDS_MeshNode* > nodes;
10427 while ( elemIt->more() )
10429 const SMDS_MeshElement* elem = elemIt->next();
10430 if ( elem->GetType() != type )
10433 nodes.assign( elem->begin_nodes(), elem->end_nodes() );
10435 if ( type == SMDSAbs_Volume && elem->GetVtkType() == VTK_POLYHEDRON )
10437 std::vector<int> quantities =
10438 static_cast< const SMDS_VtkVolume* >( elem )->GetQuantities();
10439 elem = mesh->AddPolyhedralVolume( nodes, quantities );
10443 AddElement( nodes, type, elem->IsPoly() );
10444 elem = 0; // myLastCreatedElems is already filled
10447 myLastCreatedElems.Append( elem );
10452 //================================================================================
10454 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10455 \param theElems - the list of elements (edges or faces) to be replicated
10456 The nodes for duplication could be found from these elements
10457 \param theNodesNot - list of nodes to NOT replicate
10458 \param theAffectedElems - the list of elements (cells and edges) to which the
10459 replicated nodes should be associated to.
10460 \return TRUE if operation has been completed successfully, FALSE otherwise
10462 //================================================================================
10464 bool SMESH_MeshEditor::DoubleNodes( const TIDSortedElemSet& theElems,
10465 const TIDSortedElemSet& theNodesNot,
10466 const TIDSortedElemSet& theAffectedElems )
10468 myLastCreatedElems.Clear();
10469 myLastCreatedNodes.Clear();
10471 if ( theElems.size() == 0 )
10474 SMESHDS_Mesh* aMeshDS = GetMeshDS();
10479 std::map< const SMDS_MeshNode*, const SMDS_MeshNode* > anOldNodeToNewNode;
10480 // duplicate elements and nodes
10481 res = doubleNodes( aMeshDS, theElems, theNodesNot, anOldNodeToNewNode, true );
10482 // replce nodes by duplications
10483 res = doubleNodes( aMeshDS, theAffectedElems, theNodesNot, anOldNodeToNewNode, false );
10487 //================================================================================
10489 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10490 \param theMeshDS - mesh instance
10491 \param theElems - the elements replicated or modified (nodes should be changed)
10492 \param theNodesNot - nodes to NOT replicate
10493 \param theNodeNodeMap - relation of old node to new created node
10494 \param theIsDoubleElem - flag os to replicate element or modify
10495 \return TRUE if operation has been completed successfully, FALSE otherwise
10497 //================================================================================
10499 bool SMESH_MeshEditor::doubleNodes( SMESHDS_Mesh* theMeshDS,
10500 const TIDSortedElemSet& theElems,
10501 const TIDSortedElemSet& theNodesNot,
10502 std::map< const SMDS_MeshNode*,
10503 const SMDS_MeshNode* >& theNodeNodeMap,
10504 const bool theIsDoubleElem )
10506 MESSAGE("doubleNodes");
10507 // iterate on through element and duplicate them (by nodes duplication)
10509 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10510 for ( ; elemItr != theElems.end(); ++elemItr )
10512 const SMDS_MeshElement* anElem = *elemItr;
10516 bool isDuplicate = false;
10517 // duplicate nodes to duplicate element
10518 std::vector<const SMDS_MeshNode*> newNodes( anElem->NbNodes() );
10519 SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
10521 while ( anIter->more() )
10524 SMDS_MeshNode* aCurrNode = (SMDS_MeshNode*)anIter->next();
10525 SMDS_MeshNode* aNewNode = aCurrNode;
10526 if ( theNodeNodeMap.find( aCurrNode ) != theNodeNodeMap.end() )
10527 aNewNode = (SMDS_MeshNode*)theNodeNodeMap[ aCurrNode ];
10528 else if ( theIsDoubleElem && theNodesNot.find( aCurrNode ) == theNodesNot.end() )
10531 aNewNode = theMeshDS->AddNode( aCurrNode->X(), aCurrNode->Y(), aCurrNode->Z() );
10532 copyPosition( aCurrNode, aNewNode );
10533 theNodeNodeMap[ aCurrNode ] = aNewNode;
10534 myLastCreatedNodes.Append( aNewNode );
10536 isDuplicate |= (aCurrNode != aNewNode);
10537 newNodes[ ind++ ] = aNewNode;
10539 if ( !isDuplicate )
10542 if ( theIsDoubleElem )
10543 AddElement(newNodes, anElem->GetType(), anElem->IsPoly());
10545 theMeshDS->ChangeElementNodes( anElem, &newNodes[ 0 ], anElem->NbNodes() );
10552 //================================================================================
10554 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10555 \param theNodes - identifiers of nodes to be doubled
10556 \param theModifiedElems - identifiers of elements to be updated by the new (doubled)
10557 nodes. If list of element identifiers is empty then nodes are doubled but
10558 they not assigned to elements
10559 \return TRUE if operation has been completed successfully, FALSE otherwise
10561 //================================================================================
10563 bool SMESH_MeshEditor::DoubleNodes( const std::list< int >& theListOfNodes,
10564 const std::list< int >& theListOfModifiedElems )
10566 MESSAGE("DoubleNodes");
10567 myLastCreatedElems.Clear();
10568 myLastCreatedNodes.Clear();
10570 if ( theListOfNodes.size() == 0 )
10573 SMESHDS_Mesh* aMeshDS = GetMeshDS();
10577 // iterate through nodes and duplicate them
10579 std::map< const SMDS_MeshNode*, const SMDS_MeshNode* > anOldNodeToNewNode;
10581 std::list< int >::const_iterator aNodeIter;
10582 for ( aNodeIter = theListOfNodes.begin(); aNodeIter != theListOfNodes.end(); ++aNodeIter )
10584 int aCurr = *aNodeIter;
10585 SMDS_MeshNode* aNode = (SMDS_MeshNode*)aMeshDS->FindNode( aCurr );
10591 const SMDS_MeshNode* aNewNode = aMeshDS->AddNode( aNode->X(), aNode->Y(), aNode->Z() );
10594 copyPosition( aNode, aNewNode );
10595 anOldNodeToNewNode[ aNode ] = aNewNode;
10596 myLastCreatedNodes.Append( aNewNode );
10600 // Create map of new nodes for modified elements
10602 std::map< SMDS_MeshElement*, vector<const SMDS_MeshNode*> > anElemToNodes;
10604 std::list< int >::const_iterator anElemIter;
10605 for ( anElemIter = theListOfModifiedElems.begin();
10606 anElemIter != theListOfModifiedElems.end(); ++anElemIter )
10608 int aCurr = *anElemIter;
10609 SMDS_MeshElement* anElem = (SMDS_MeshElement*)aMeshDS->FindElement( aCurr );
10613 vector<const SMDS_MeshNode*> aNodeArr( anElem->NbNodes() );
10615 SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
10617 while ( anIter->more() )
10619 SMDS_MeshNode* aCurrNode = (SMDS_MeshNode*)anIter->next();
10620 if ( aCurr && anOldNodeToNewNode.find( aCurrNode ) != anOldNodeToNewNode.end() )
10622 const SMDS_MeshNode* aNewNode = anOldNodeToNewNode[ aCurrNode ];
10623 aNodeArr[ ind++ ] = aNewNode;
10626 aNodeArr[ ind++ ] = aCurrNode;
10628 anElemToNodes[ anElem ] = aNodeArr;
10631 // Change nodes of elements
10633 std::map< SMDS_MeshElement*, vector<const SMDS_MeshNode*> >::iterator
10634 anElemToNodesIter = anElemToNodes.begin();
10635 for ( ; anElemToNodesIter != anElemToNodes.end(); ++anElemToNodesIter )
10637 const SMDS_MeshElement* anElem = anElemToNodesIter->first;
10638 vector<const SMDS_MeshNode*> aNodeArr = anElemToNodesIter->second;
10641 MESSAGE("ChangeElementNodes");
10642 aMeshDS->ChangeElementNodes( anElem, &aNodeArr[ 0 ], anElem->NbNodes() );
10651 //================================================================================
10653 \brief Check if element located inside shape
10654 \return TRUE if IN or ON shape, FALSE otherwise
10656 //================================================================================
10658 template<class Classifier>
10659 bool isInside(const SMDS_MeshElement* theElem,
10660 Classifier& theClassifier,
10661 const double theTol)
10663 gp_XYZ centerXYZ (0, 0, 0);
10664 SMDS_ElemIteratorPtr aNodeItr = theElem->nodesIterator();
10665 while (aNodeItr->more())
10666 centerXYZ += SMESH_TNodeXYZ(cast2Node( aNodeItr->next()));
10668 gp_Pnt aPnt = centerXYZ / theElem->NbNodes();
10669 theClassifier.Perform(aPnt, theTol);
10670 TopAbs_State aState = theClassifier.State();
10671 return (aState == TopAbs_IN || aState == TopAbs_ON );
10674 //================================================================================
10676 * \brief Classifier of the 3D point on the TopoDS_Face
10677 * with interaface suitable for isInside()
10679 //================================================================================
10681 struct _FaceClassifier
10683 Extrema_ExtPS _extremum;
10684 BRepAdaptor_Surface _surface;
10685 TopAbs_State _state;
10687 _FaceClassifier(const TopoDS_Face& face):_extremum(),_surface(face),_state(TopAbs_OUT)
10689 _extremum.Initialize( _surface,
10690 _surface.FirstUParameter(), _surface.LastUParameter(),
10691 _surface.FirstVParameter(), _surface.LastVParameter(),
10692 _surface.Tolerance(), _surface.Tolerance() );
10694 void Perform(const gp_Pnt& aPnt, double theTol)
10697 _state = TopAbs_OUT;
10698 _extremum.Perform(aPnt);
10699 if ( _extremum.IsDone() )
10700 for ( int iSol = 1; iSol <= _extremum.NbExt() && _state == TopAbs_OUT; ++iSol)
10701 _state = ( _extremum.SquareDistance(iSol) <= theTol ? TopAbs_IN : TopAbs_OUT );
10703 TopAbs_State State() const
10710 //================================================================================
10712 \brief Identify the elements that will be affected by node duplication (actual duplication is not performed).
10713 This method is the first step of DoubleNodeElemGroupsInRegion.
10714 \param theElems - list of groups of elements (edges or faces) to be replicated
10715 \param theNodesNot - list of groups of nodes not to replicated
10716 \param theShape - shape to detect affected elements (element which geometric center
10717 located on or inside shape). If the shape is null, detection is done on faces orientations
10718 (select elements with a gravity center on the side given by faces normals).
10719 This mode (null shape) is faster, but works only when theElems are faces, with coherents orientations.
10720 The replicated nodes should be associated to affected elements.
10721 \return groups of affected elements
10722 \sa DoubleNodeElemGroupsInRegion()
10724 //================================================================================
10726 bool SMESH_MeshEditor::AffectedElemGroupsInRegion( const TIDSortedElemSet& theElems,
10727 const TIDSortedElemSet& theNodesNot,
10728 const TopoDS_Shape& theShape,
10729 TIDSortedElemSet& theAffectedElems)
10731 if ( theShape.IsNull() )
10733 std::set<const SMDS_MeshNode*> alreadyCheckedNodes;
10734 std::set<const SMDS_MeshElement*> alreadyCheckedElems;
10735 std::set<const SMDS_MeshElement*> edgesToCheck;
10736 alreadyCheckedNodes.clear();
10737 alreadyCheckedElems.clear();
10738 edgesToCheck.clear();
10740 // --- iterates on elements to be replicated and get elements by back references from their nodes
10742 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10744 for ( ielem=1; elemItr != theElems.end(); ++elemItr )
10746 SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
10747 if (!anElem || (anElem->GetType() != SMDSAbs_Face))
10750 SMESH_MeshAlgos::FaceNormal( anElem, normal, /*normalized=*/true );
10751 MESSAGE("element " << ielem++ << " normal " << normal.X() << " " << normal.Y() << " " << normal.Z());
10752 std::set<const SMDS_MeshNode*> nodesElem;
10754 SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
10755 while ( nodeItr->more() )
10757 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10758 nodesElem.insert(aNode);
10760 std::set<const SMDS_MeshNode*>::iterator nodit = nodesElem.begin();
10761 for (; nodit != nodesElem.end(); nodit++)
10763 MESSAGE(" noeud ");
10764 const SMDS_MeshNode* aNode = *nodit;
10765 if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
10767 if (alreadyCheckedNodes.find(aNode) != alreadyCheckedNodes.end())
10769 alreadyCheckedNodes.insert(aNode);
10770 SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
10771 while ( backElemItr->more() )
10773 MESSAGE(" backelem ");
10774 const SMDS_MeshElement* curElem = backElemItr->next();
10775 if (alreadyCheckedElems.find(curElem) != alreadyCheckedElems.end())
10777 if (theElems.find(curElem) != theElems.end())
10779 alreadyCheckedElems.insert(curElem);
10780 double x=0, y=0, z=0;
10782 SMDS_ElemIteratorPtr nodeItr2 = curElem->nodesIterator();
10783 while ( nodeItr2->more() )
10785 const SMDS_MeshNode* anotherNode = cast2Node(nodeItr2->next());
10786 x += anotherNode->X();
10787 y += anotherNode->Y();
10788 z += anotherNode->Z();
10792 p.SetCoord( x/nb -aNode->X(),
10794 z/nb -aNode->Z() );
10795 MESSAGE(" check " << p.X() << " " << p.Y() << " " << p.Z());
10798 MESSAGE(" --- inserted")
10799 theAffectedElems.insert( curElem );
10801 else if (curElem->GetType() == SMDSAbs_Edge)
10802 edgesToCheck.insert(curElem);
10806 // --- add also edges lying on the set of faces (all nodes in alreadyCheckedNodes)
10807 std::set<const SMDS_MeshElement*>::iterator eit = edgesToCheck.begin();
10808 for( ; eit != edgesToCheck.end(); eit++)
10810 bool onside = true;
10811 const SMDS_MeshElement* anEdge = *eit;
10812 SMDS_ElemIteratorPtr nodeItr = anEdge->nodesIterator();
10813 while ( nodeItr->more() )
10815 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10816 if (alreadyCheckedNodes.find(aNode) == alreadyCheckedNodes.end())
10824 MESSAGE(" --- edge onside inserted")
10825 theAffectedElems.insert(anEdge);
10831 const double aTol = Precision::Confusion();
10832 auto_ptr< BRepClass3d_SolidClassifier> bsc3d;
10833 auto_ptr<_FaceClassifier> aFaceClassifier;
10834 if ( theShape.ShapeType() == TopAbs_SOLID )
10836 bsc3d.reset( new BRepClass3d_SolidClassifier(theShape));;
10837 bsc3d->PerformInfinitePoint(aTol);
10839 else if (theShape.ShapeType() == TopAbs_FACE )
10841 aFaceClassifier.reset( new _FaceClassifier(TopoDS::Face(theShape)));
10844 // iterates on indicated elements and get elements by back references from their nodes
10845 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10847 for ( ielem = 1; elemItr != theElems.end(); ++elemItr )
10849 MESSAGE("element " << ielem++);
10850 SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
10853 SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
10854 while ( nodeItr->more() )
10856 MESSAGE(" noeud ");
10857 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10858 if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
10860 SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
10861 while ( backElemItr->more() )
10863 MESSAGE(" backelem ");
10864 const SMDS_MeshElement* curElem = backElemItr->next();
10865 if ( curElem && theElems.find(curElem) == theElems.end() &&
10867 isInside( curElem, *bsc3d, aTol ) :
10868 isInside( curElem, *aFaceClassifier, aTol )))
10869 theAffectedElems.insert( curElem );
10877 //================================================================================
10879 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10880 \param theElems - group of of elements (edges or faces) to be replicated
10881 \param theNodesNot - group of nodes not to replicate
10882 \param theShape - shape to detect affected elements (element which geometric center
10883 located on or inside shape).
10884 The replicated nodes should be associated to affected elements.
10885 \return TRUE if operation has been completed successfully, FALSE otherwise
10887 //================================================================================
10889 bool SMESH_MeshEditor::DoubleNodesInRegion( const TIDSortedElemSet& theElems,
10890 const TIDSortedElemSet& theNodesNot,
10891 const TopoDS_Shape& theShape )
10893 if ( theShape.IsNull() )
10896 const double aTol = Precision::Confusion();
10897 auto_ptr< BRepClass3d_SolidClassifier> bsc3d;
10898 auto_ptr<_FaceClassifier> aFaceClassifier;
10899 if ( theShape.ShapeType() == TopAbs_SOLID )
10901 bsc3d.reset( new BRepClass3d_SolidClassifier(theShape));;
10902 bsc3d->PerformInfinitePoint(aTol);
10904 else if (theShape.ShapeType() == TopAbs_FACE )
10906 aFaceClassifier.reset( new _FaceClassifier(TopoDS::Face(theShape)));
10909 // iterates on indicated elements and get elements by back references from their nodes
10910 TIDSortedElemSet anAffected;
10911 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10912 for ( ; elemItr != theElems.end(); ++elemItr )
10914 SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
10918 SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
10919 while ( nodeItr->more() )
10921 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10922 if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
10924 SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
10925 while ( backElemItr->more() )
10927 const SMDS_MeshElement* curElem = backElemItr->next();
10928 if ( curElem && theElems.find(curElem) == theElems.end() &&
10930 isInside( curElem, *bsc3d, aTol ) :
10931 isInside( curElem, *aFaceClassifier, aTol )))
10932 anAffected.insert( curElem );
10936 return DoubleNodes( theElems, theNodesNot, anAffected );
10940 * \brief compute an oriented angle between two planes defined by four points.
10941 * The vector (p0,p1) defines the intersection of the 2 planes (p0,p1,g1) and (p0,p1,g2)
10942 * @param p0 base of the rotation axe
10943 * @param p1 extremity of the rotation axe
10944 * @param g1 belongs to the first plane
10945 * @param g2 belongs to the second plane
10947 double SMESH_MeshEditor::OrientedAngle(const gp_Pnt& p0, const gp_Pnt& p1, const gp_Pnt& g1, const gp_Pnt& g2)
10949 // MESSAGE(" p0: " << p0.X() << " " << p0.Y() << " " << p0.Z());
10950 // MESSAGE(" p1: " << p1.X() << " " << p1.Y() << " " << p1.Z());
10951 // MESSAGE(" g1: " << g1.X() << " " << g1.Y() << " " << g1.Z());
10952 // MESSAGE(" g2: " << g2.X() << " " << g2.Y() << " " << g2.Z());
10953 gp_Vec vref(p0, p1);
10956 gp_Vec n1 = vref.Crossed(v1);
10957 gp_Vec n2 = vref.Crossed(v2);
10959 return n2.AngleWithRef(n1, vref);
10961 catch ( Standard_Failure ) {
10963 return Max( v1.Magnitude(), v2.Magnitude() );
10967 * \brief Double nodes on shared faces between groups of volumes and create flat elements on demand.
10968 * The list of groups must contain at least two groups. The groups have to be disjoint: no common element into two different groups.
10969 * The nodes of the internal faces at the boundaries of the groups are doubled. Optionally, the internal faces are replaced by flat elements.
10970 * Triangles are transformed into prisms, and quadrangles into hexahedrons.
10971 * The flat elements are stored in groups of volumes. These groups are named according to the position of the group in the list:
10972 * 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.
10973 * 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.
10974 * All the flat elements are gathered into the group named "joints3D" (or "joints2D" in 2D situation).
10975 * The flat element of the multiple junctions between the simple junction are stored in a group named "jointsMultiples".
10976 * \param theElems - list of groups of volumes, where a group of volume is a set of
10977 * SMDS_MeshElements sorted by Id.
10978 * \param createJointElems - if TRUE, create the elements
10979 * \param onAllBoundaries - if TRUE, the nodes and elements are also created on
10980 * the boundary between \a theDomains and the rest mesh
10981 * \return TRUE if operation has been completed successfully, FALSE otherwise
10983 bool SMESH_MeshEditor::DoubleNodesOnGroupBoundaries( const std::vector<TIDSortedElemSet>& theElems,
10984 bool createJointElems,
10985 bool onAllBoundaries)
10987 MESSAGE("----------------------------------------------");
10988 MESSAGE("SMESH_MeshEditor::doubleNodesOnGroupBoundaries");
10989 MESSAGE("----------------------------------------------");
10991 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
10992 meshDS->BuildDownWardConnectivity(true);
10994 SMDS_UnstructuredGrid *grid = meshDS->getGrid();
10996 // --- build the list of faces shared by 2 domains (group of elements), with their domain and volume indexes
10997 // build the list of cells with only a node or an edge on the border, with their domain and volume indexes
10998 // build the list of nodes shared by 2 or more domains, with their domain indexes
11000 std::map<DownIdType, std::map<int,int>, DownIdCompare> faceDomains; // face --> (id domain --> id volume)
11001 std::map<int,int>celldom; // cell vtkId --> domain
11002 std::map<DownIdType, std::map<int,int>, DownIdCompare> cellDomains; // oldNode --> (id domain --> id cell)
11003 std::map<int, std::map<int,int> > nodeDomains; // oldId --> (domainId --> newId)
11004 faceDomains.clear();
11006 cellDomains.clear();
11007 nodeDomains.clear();
11008 std::map<int,int> emptyMap;
11009 std::set<int> emptySet;
11012 MESSAGE(".. Number of domains :"<<theElems.size());
11014 TIDSortedElemSet theRestDomElems;
11015 const int iRestDom = -1;
11016 const int idom0 = onAllBoundaries ? iRestDom : 0;
11017 const int nbDomains = theElems.size();
11019 // Check if the domains do not share an element
11020 for (int idom = 0; idom < nbDomains-1; idom++)
11022 // MESSAGE("... Check of domain #" << idom);
11023 const TIDSortedElemSet& domain = theElems[idom];
11024 TIDSortedElemSet::const_iterator elemItr = domain.begin();
11025 for (; elemItr != domain.end(); ++elemItr)
11027 const SMDS_MeshElement* anElem = *elemItr;
11028 int idombisdeb = idom + 1 ;
11029 for (int idombis = idombisdeb; idombis < theElems.size(); idombis++) // check if the element belongs to a domain further in the list
11031 const TIDSortedElemSet& domainbis = theElems[idombis];
11032 if ( domainbis.count(anElem) )
11034 MESSAGE(".... Domain #" << idom);
11035 MESSAGE(".... Domain #" << idombis);
11036 throw SALOME_Exception("The domains are not disjoint.");
11043 for (int idom = 0; idom < nbDomains; idom++)
11046 // --- build a map (face to duplicate --> volume to modify)
11047 // with all the faces shared by 2 domains (group of elements)
11048 // and corresponding volume of this domain, for each shared face.
11049 // a volume has a face shared by 2 domains if it has a neighbor which is not in his domain.
11051 MESSAGE("... Neighbors of domain #" << idom);
11052 const TIDSortedElemSet& domain = theElems[idom];
11053 TIDSortedElemSet::const_iterator elemItr = domain.begin();
11054 for (; elemItr != domain.end(); ++elemItr)
11056 const SMDS_MeshElement* anElem = *elemItr;
11059 int vtkId = anElem->getVtkId();
11060 //MESSAGE(" vtkId " << vtkId << " smdsId " << anElem->GetID());
11061 int neighborsVtkIds[NBMAXNEIGHBORS];
11062 int downIds[NBMAXNEIGHBORS];
11063 unsigned char downTypes[NBMAXNEIGHBORS];
11064 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
11065 for (int n = 0; n < nbNeighbors; n++)
11067 int smdsId = meshDS->fromVtkToSmds(neighborsVtkIds[n]);
11068 const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
11069 if (elem && ! domain.count(elem)) // neighbor is in another domain : face is shared
11072 for (int idombis = 0; idombis < theElems.size() && !ok; idombis++) // check if the neighbor belongs to another domain of the list
11074 // MESSAGE("Domain " << idombis);
11075 const TIDSortedElemSet& domainbis = theElems[idombis];
11076 if ( domainbis.count(elem)) ok = true ; // neighbor is in a correct domain : face is kept
11078 if ( ok || onAllBoundaries ) // the characteristics of the face is stored
11080 DownIdType face(downIds[n], downTypes[n]);
11081 if (!faceDomains[face].count(idom))
11083 faceDomains[face][idom] = vtkId; // volume associated to face in this domain
11084 celldom[vtkId] = idom;
11085 //MESSAGE(" cell with a border " << vtkId << " domain " << idom);
11089 theRestDomElems.insert( elem );
11090 faceDomains[face][iRestDom] = neighborsVtkIds[n];
11091 celldom[neighborsVtkIds[n]] = iRestDom;
11099 //MESSAGE("Number of shared faces " << faceDomains.size());
11100 std::map<DownIdType, std::map<int, int>, DownIdCompare>::iterator itface;
11102 // --- explore the shared faces domain by domain,
11103 // explore the nodes of the face and see if they belong to a cell in the domain,
11104 // which has only a node or an edge on the border (not a shared face)
11106 for (int idomain = idom0; idomain < nbDomains; idomain++)
11108 //MESSAGE("Domain " << idomain);
11109 const TIDSortedElemSet& domain = (idomain == iRestDom) ? theRestDomElems : theElems[idomain];
11110 itface = faceDomains.begin();
11111 for (; itface != faceDomains.end(); ++itface)
11113 const std::map<int, int>& domvol = itface->second;
11114 if (!domvol.count(idomain))
11116 DownIdType face = itface->first;
11117 //MESSAGE(" --- face " << face.cellId);
11118 std::set<int> oldNodes;
11120 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11121 std::set<int>::iterator itn = oldNodes.begin();
11122 for (; itn != oldNodes.end(); ++itn)
11125 //MESSAGE(" node " << oldId);
11126 vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
11127 for (int i=0; i<l.ncells; i++)
11129 int vtkId = l.cells[i];
11130 const SMDS_MeshElement* anElem = GetMeshDS()->FindElement(GetMeshDS()->fromVtkToSmds(vtkId));
11131 if (!domain.count(anElem))
11133 int vtkType = grid->GetCellType(vtkId);
11134 int downId = grid->CellIdToDownId(vtkId);
11137 MESSAGE("doubleNodesOnGroupBoundaries: internal algorithm problem");
11138 continue; // not OK at this stage of the algorithm:
11139 //no cells created after BuildDownWardConnectivity
11141 DownIdType aCell(downId, vtkType);
11142 cellDomains[aCell][idomain] = vtkId;
11143 celldom[vtkId] = idomain;
11144 //MESSAGE(" cell " << vtkId << " domain " << idomain);
11150 // --- explore the shared faces domain by domain, to duplicate the nodes in a coherent way
11151 // for each shared face, get the nodes
11152 // for each node, for each domain of the face, create a clone of the node
11154 // --- edges at the intersection of 3 or 4 domains, with the order of domains to build
11155 // junction elements of type prism or hexa. the key is the pair of nodesId (lower first)
11156 // the value is the ordered domain ids. (more than 4 domains not taken into account)
11158 std::map<std::vector<int>, std::vector<int> > edgesMultiDomains; // nodes of edge --> ordered domains
11159 std::map<int, std::vector<int> > mutipleNodes; // nodes multi domains with domain order
11160 std::map<int, std::vector<int> > mutipleNodesToFace; // nodes multi domains with domain order to transform in Face (junction between 3 or more 2D domains)
11162 MESSAGE(".. Duplication of the nodes");
11163 for (int idomain = idom0; idomain < nbDomains; idomain++)
11165 itface = faceDomains.begin();
11166 for (; itface != faceDomains.end(); ++itface)
11168 const std::map<int, int>& domvol = itface->second;
11169 if (!domvol.count(idomain))
11171 DownIdType face = itface->first;
11172 //MESSAGE(" --- face " << face.cellId);
11173 std::set<int> oldNodes;
11175 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11176 std::set<int>::iterator itn = oldNodes.begin();
11177 for (; itn != oldNodes.end(); ++itn)
11180 if (nodeDomains[oldId].empty())
11182 nodeDomains[oldId][idomain] = oldId; // keep the old node in the first domain
11183 //MESSAGE("-+-+-b oldNode " << oldId << " domain " << idomain);
11185 std::map<int, int>::const_iterator itdom = domvol.begin();
11186 for (; itdom != domvol.end(); ++itdom)
11188 int idom = itdom->first;
11189 //MESSAGE(" domain " << idom);
11190 if (!nodeDomains[oldId].count(idom)) // --- node to clone
11192 if (nodeDomains[oldId].size() >= 2) // a multiple node
11194 vector<int> orderedDoms;
11195 //MESSAGE("multiple node " << oldId);
11196 if (mutipleNodes.count(oldId))
11197 orderedDoms = mutipleNodes[oldId];
11200 map<int,int>::iterator it = nodeDomains[oldId].begin();
11201 for (; it != nodeDomains[oldId].end(); ++it)
11202 orderedDoms.push_back(it->first);
11204 orderedDoms.push_back(idom); // TODO order ==> push_front or back
11205 //stringstream txt;
11206 //for (int i=0; i<orderedDoms.size(); i++)
11207 // txt << orderedDoms[i] << " ";
11208 //MESSAGE("orderedDoms " << txt.str());
11209 mutipleNodes[oldId] = orderedDoms;
11211 double *coords = grid->GetPoint(oldId);
11212 SMDS_MeshNode *newNode = meshDS->AddNode(coords[0], coords[1], coords[2]);
11213 copyPosition( meshDS->FindNodeVtk( oldId ), newNode );
11214 int newId = newNode->getVtkId();
11215 nodeDomains[oldId][idom] = newId; // cloned node for other domains
11216 //MESSAGE("-+-+-c oldNode " << oldId << " domain " << idomain << " newNode " << newId << " domain " << idom << " size=" <<nodeDomains[oldId].size());
11223 MESSAGE(".. Creation of elements");
11224 for (int idomain = idom0; idomain < nbDomains; idomain++)
11226 itface = faceDomains.begin();
11227 for (; itface != faceDomains.end(); ++itface)
11229 std::map<int, int> domvol = itface->second;
11230 if (!domvol.count(idomain))
11232 DownIdType face = itface->first;
11233 //MESSAGE(" --- face " << face.cellId);
11234 std::set<int> oldNodes;
11236 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11237 int nbMultipleNodes = 0;
11238 std::set<int>::iterator itn = oldNodes.begin();
11239 for (; itn != oldNodes.end(); ++itn)
11242 if (mutipleNodes.count(oldId))
11245 if (nbMultipleNodes > 1) // check if an edge of the face is shared between 3 or more domains
11247 //MESSAGE("multiple Nodes detected on a shared face");
11248 int downId = itface->first.cellId;
11249 unsigned char cellType = itface->first.cellType;
11250 // --- shared edge or shared face ?
11251 if ((cellType == VTK_LINE) || (cellType == VTK_QUADRATIC_EDGE)) // shared edge (between two faces)
11254 int nbNodes = grid->getDownArray(cellType)->getNodes(downId, nodes);
11255 for (int i=0; i< nbNodes; i=i+nbNodes-1) // i=0 , i=nbNodes-1
11256 if (mutipleNodes.count(nodes[i]))
11257 if (!mutipleNodesToFace.count(nodes[i]))
11258 mutipleNodesToFace[nodes[i]] = mutipleNodes[nodes[i]];
11260 else // shared face (between two volumes)
11262 int nbEdges = grid->getDownArray(cellType)->getNumberOfDownCells(downId);
11263 const int* downEdgeIds = grid->getDownArray(cellType)->getDownCells(downId);
11264 const unsigned char* edgeType = grid->getDownArray(cellType)->getDownTypes(downId);
11265 for (int ie =0; ie < nbEdges; ie++)
11268 int nbNodes = grid->getDownArray(edgeType[ie])->getNodes(downEdgeIds[ie], nodes);
11269 if (mutipleNodes.count(nodes[0]) && mutipleNodes.count(nodes[nbNodes-1]))
11271 vector<int> vn0 = mutipleNodes[nodes[0]];
11272 vector<int> vn1 = mutipleNodes[nodes[nbNodes - 1]];
11274 for (int i0 = 0; i0 < vn0.size(); i0++)
11275 for (int i1 = 0; i1 < vn1.size(); i1++)
11276 if (vn0[i0] == vn1[i1])
11277 doms.push_back(vn0[i0]);
11278 if (doms.size() >2)
11280 //MESSAGE(" detect edgesMultiDomains " << nodes[0] << " " << nodes[nbNodes - 1]);
11281 double *coords = grid->GetPoint(nodes[0]);
11282 gp_Pnt p0(coords[0], coords[1], coords[2]);
11283 coords = grid->GetPoint(nodes[nbNodes - 1]);
11284 gp_Pnt p1(coords[0], coords[1], coords[2]);
11286 int vtkVolIds[1000]; // an edge can belong to a lot of volumes
11287 map<int, SMDS_VtkVolume*> domvol; // domain --> a volume with the edge
11288 map<int, double> angleDom; // oriented angles between planes defined by edge and volume centers
11289 int nbvol = grid->GetParentVolumes(vtkVolIds, downEdgeIds[ie], edgeType[ie]);
11290 for (int id=0; id < doms.size(); id++)
11292 int idom = doms[id];
11293 const TIDSortedElemSet& domain = (idom == iRestDom) ? theRestDomElems : theElems[idom];
11294 for (int ivol=0; ivol<nbvol; ivol++)
11296 int smdsId = meshDS->fromVtkToSmds(vtkVolIds[ivol]);
11297 SMDS_MeshElement* elem = (SMDS_MeshElement*)meshDS->FindElement(smdsId);
11298 if (domain.count(elem))
11300 SMDS_VtkVolume* svol = dynamic_cast<SMDS_VtkVolume*>(elem);
11301 domvol[idom] = svol;
11302 //MESSAGE(" domain " << idom << " volume " << elem->GetID());
11304 vtkIdType npts = 0;
11305 vtkIdType* pts = 0;
11306 grid->GetCellPoints(vtkVolIds[ivol], npts, pts);
11307 SMDS_VtkVolume::gravityCenter(grid, pts, npts, values);
11310 gref.SetXYZ(gp_XYZ(values[0], values[1], values[2]));
11311 angleDom[idom] = 0;
11315 gp_Pnt g(values[0], values[1], values[2]);
11316 angleDom[idom] = OrientedAngle(p0, p1, gref, g); // -pi<angle<+pi
11317 //MESSAGE(" angle=" << angleDom[idom]);
11323 map<double, int> sortedDom; // sort domains by angle
11324 for (map<int, double>::iterator ia = angleDom.begin(); ia != angleDom.end(); ++ia)
11325 sortedDom[ia->second] = ia->first;
11326 vector<int> vnodes;
11328 for (map<double, int>::iterator ib = sortedDom.begin(); ib != sortedDom.end(); ++ib)
11330 vdom.push_back(ib->second);
11331 //MESSAGE(" ordered domain " << ib->second << " angle " << ib->first);
11333 for (int ino = 0; ino < nbNodes; ino++)
11334 vnodes.push_back(nodes[ino]);
11335 edgesMultiDomains[vnodes] = vdom; // nodes vector --> ordered domains
11344 // --- iterate on shared faces (volumes to modify, face to extrude)
11345 // get node id's of the face (id SMDS = id VTK)
11346 // create flat element with old and new nodes if requested
11348 // --- new quad nodes on flat quad elements: oldId --> ((domain1 X domain2) --> newId)
11349 // (domain1 X domain2) = domain1 + MAXINT*domain2
11351 std::map<int, std::map<long,int> > nodeQuadDomains;
11352 std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11354 MESSAGE(".. Creation of elements: simple junction");
11355 if (createJointElems)
11358 string joints2DName = "joints2D";
11359 mapOfJunctionGroups[joints2DName] = this->myMesh->AddGroup(SMDSAbs_Face, joints2DName.c_str(), idg);
11360 SMESHDS_Group *joints2DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints2DName]->GetGroupDS());
11361 string joints3DName = "joints3D";
11362 mapOfJunctionGroups[joints3DName] = this->myMesh->AddGroup(SMDSAbs_Volume, joints3DName.c_str(), idg);
11363 SMESHDS_Group *joints3DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints3DName]->GetGroupDS());
11365 itface = faceDomains.begin();
11366 for (; itface != faceDomains.end(); ++itface)
11368 DownIdType face = itface->first;
11369 std::set<int> oldNodes;
11370 std::set<int>::iterator itn;
11372 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11374 std::map<int, int> domvol = itface->second;
11375 std::map<int, int>::iterator itdom = domvol.begin();
11376 int dom1 = itdom->first;
11377 int vtkVolId = itdom->second;
11379 int dom2 = itdom->first;
11380 SMDS_MeshCell *vol = grid->extrudeVolumeFromFace(vtkVolId, dom1, dom2, oldNodes, nodeDomains,
11382 stringstream grpname;
11385 grpname << dom1 << "_" << dom2;
11387 grpname << dom2 << "_" << dom1;
11388 string namegrp = grpname.str();
11389 if (!mapOfJunctionGroups.count(namegrp))
11390 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(vol->GetType(), namegrp.c_str(), idg);
11391 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11393 sgrp->Add(vol->GetID());
11394 if (vol->GetType() == SMDSAbs_Volume)
11395 joints3DGrp->Add(vol->GetID());
11396 else if (vol->GetType() == SMDSAbs_Face)
11397 joints2DGrp->Add(vol->GetID());
11401 // --- create volumes on multiple domain intersection if requested
11402 // iterate on mutipleNodesToFace
11403 // iterate on edgesMultiDomains
11405 MESSAGE(".. Creation of elements: multiple junction");
11406 if (createJointElems)
11408 // --- iterate on mutipleNodesToFace
11410 std::map<int, std::vector<int> >::iterator itn = mutipleNodesToFace.begin();
11411 for (; itn != mutipleNodesToFace.end(); ++itn)
11413 int node = itn->first;
11414 vector<int> orderDom = itn->second;
11415 vector<vtkIdType> orderedNodes;
11416 for (int idom = 0; idom <orderDom.size(); idom++)
11417 orderedNodes.push_back( nodeDomains[node][orderDom[idom]] );
11418 SMDS_MeshFace* face = this->GetMeshDS()->AddFaceFromVtkIds(orderedNodes);
11420 stringstream grpname;
11422 grpname << 0 << "_" << 0;
11424 string namegrp = grpname.str();
11425 if (!mapOfJunctionGroups.count(namegrp))
11426 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Face, namegrp.c_str(), idg);
11427 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11429 sgrp->Add(face->GetID());
11432 // --- iterate on edgesMultiDomains
11434 std::map<std::vector<int>, std::vector<int> >::iterator ite = edgesMultiDomains.begin();
11435 for (; ite != edgesMultiDomains.end(); ++ite)
11437 vector<int> nodes = ite->first;
11438 vector<int> orderDom = ite->second;
11439 vector<vtkIdType> orderedNodes;
11440 if (nodes.size() == 2)
11442 //MESSAGE(" use edgesMultiDomains " << nodes[0] << " " << nodes[1]);
11443 for (int ino=0; ino < nodes.size(); ino++)
11444 if (orderDom.size() == 3)
11445 for (int idom = 0; idom <orderDom.size(); idom++)
11446 orderedNodes.push_back( nodeDomains[nodes[ino]][orderDom[idom]] );
11448 for (int idom = orderDom.size()-1; idom >=0; idom--)
11449 orderedNodes.push_back( nodeDomains[nodes[ino]][orderDom[idom]] );
11450 SMDS_MeshVolume* vol = this->GetMeshDS()->AddVolumeFromVtkIds(orderedNodes);
11453 string namegrp = "jointsMultiples";
11454 if (!mapOfJunctionGroups.count(namegrp))
11455 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
11456 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11458 sgrp->Add(vol->GetID());
11462 INFOS("Quadratic multiple joints not implemented");
11463 // TODO quadratic nodes
11468 // --- list the explicit faces and edges of the mesh that need to be modified,
11469 // i.e. faces and edges built with one or more duplicated nodes.
11470 // associate these faces or edges to their corresponding domain.
11471 // only the first domain found is kept when a face or edge is shared
11473 std::map<DownIdType, std::map<int,int>, DownIdCompare> faceOrEdgeDom; // cellToModify --> (id domain --> id cell)
11474 std::map<int,int> feDom; // vtk id of cell to modify --> id domain
11475 faceOrEdgeDom.clear();
11478 MESSAGE(".. Modification of elements");
11479 for (int idomain = idom0; idomain < nbDomains; idomain++)
11481 std::map<int, std::map<int, int> >::const_iterator itnod = nodeDomains.begin();
11482 for (; itnod != nodeDomains.end(); ++itnod)
11484 int oldId = itnod->first;
11485 //MESSAGE(" node " << oldId);
11486 vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
11487 for (int i = 0; i < l.ncells; i++)
11489 int vtkId = l.cells[i];
11490 int vtkType = grid->GetCellType(vtkId);
11491 int downId = grid->CellIdToDownId(vtkId);
11493 continue; // new cells: not to be modified
11494 DownIdType aCell(downId, vtkType);
11495 int volParents[1000];
11496 int nbvol = grid->GetParentVolumes(volParents, vtkId);
11497 for (int j = 0; j < nbvol; j++)
11498 if (celldom.count(volParents[j]) && (celldom[volParents[j]] == idomain))
11499 if (!feDom.count(vtkId))
11501 feDom[vtkId] = idomain;
11502 faceOrEdgeDom[aCell] = emptyMap;
11503 faceOrEdgeDom[aCell][idomain] = vtkId; // affect face or edge to the first domain only
11504 //MESSAGE("affect cell " << this->GetMeshDS()->fromVtkToSmds(vtkId) << " domain " << idomain
11505 // << " type " << vtkType << " downId " << downId);
11511 // --- iterate on shared faces (volumes to modify, face to extrude)
11512 // get node id's of the face
11513 // replace old nodes by new nodes in volumes, and update inverse connectivity
11515 std::map<DownIdType, std::map<int,int>, DownIdCompare>* maps[3] = {&faceDomains, &cellDomains, &faceOrEdgeDom};
11516 for (int m=0; m<3; m++)
11518 std::map<DownIdType, std::map<int,int>, DownIdCompare>* amap = maps[m];
11519 itface = (*amap).begin();
11520 for (; itface != (*amap).end(); ++itface)
11522 DownIdType face = itface->first;
11523 std::set<int> oldNodes;
11524 std::set<int>::iterator itn;
11526 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11527 //MESSAGE("examine cell, downId " << face.cellId << " type " << int(face.cellType));
11528 std::map<int, int> localClonedNodeIds;
11530 std::map<int, int> domvol = itface->second;
11531 std::map<int, int>::iterator itdom = domvol.begin();
11532 for (; itdom != domvol.end(); ++itdom)
11534 int idom = itdom->first;
11535 int vtkVolId = itdom->second;
11536 //MESSAGE("modify nodes of cell " << this->GetMeshDS()->fromVtkToSmds(vtkVolId) << " domain " << idom);
11537 localClonedNodeIds.clear();
11538 for (itn = oldNodes.begin(); itn != oldNodes.end(); ++itn)
11541 if (nodeDomains[oldId].count(idom))
11543 localClonedNodeIds[oldId] = nodeDomains[oldId][idom];
11544 //MESSAGE(" node " << oldId << " --> " << localClonedNodeIds[oldId]);
11547 meshDS->ModifyCellNodes(vtkVolId, localClonedNodeIds);
11552 // Remove empty groups (issue 0022812)
11553 std::map<std::string, SMESH_Group*>::iterator name_group = mapOfJunctionGroups.begin();
11554 for ( ; name_group != mapOfJunctionGroups.end(); ++name_group )
11556 if ( name_group->second && name_group->second->GetGroupDS()->IsEmpty() )
11557 myMesh->RemoveGroup( name_group->second->GetGroupDS()->GetID() );
11560 meshDS->CleanDownWardConnectivity(); // Mesh has been modified, downward connectivity is no more usable, free memory
11561 grid->BuildLinks();
11569 * \brief Double nodes on some external faces and create flat elements.
11570 * Flat elements are mainly used by some types of mechanic calculations.
11572 * Each group of the list must be constituted of faces.
11573 * Triangles are transformed in prisms, and quadrangles in hexahedrons.
11574 * @param theElems - list of groups of faces, where a group of faces is a set of
11575 * SMDS_MeshElements sorted by Id.
11576 * @return TRUE if operation has been completed successfully, FALSE otherwise
11578 bool SMESH_MeshEditor::CreateFlatElementsOnFacesGroups(const std::vector<TIDSortedElemSet>& theElems)
11580 MESSAGE("-------------------------------------------------");
11581 MESSAGE("SMESH_MeshEditor::CreateFlatElementsOnFacesGroups");
11582 MESSAGE("-------------------------------------------------");
11584 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11586 // --- For each group of faces
11587 // duplicate the nodes, create a flat element based on the face
11588 // replace the nodes of the faces by their clones
11590 std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> clonedNodes;
11591 std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> intermediateNodes;
11592 clonedNodes.clear();
11593 intermediateNodes.clear();
11594 std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11595 mapOfJunctionGroups.clear();
11597 for (int idom = 0; idom < theElems.size(); idom++)
11599 const TIDSortedElemSet& domain = theElems[idom];
11600 TIDSortedElemSet::const_iterator elemItr = domain.begin();
11601 for (; elemItr != domain.end(); ++elemItr)
11603 SMDS_MeshElement* anElem = (SMDS_MeshElement*) *elemItr;
11604 SMDS_MeshFace* aFace = dynamic_cast<SMDS_MeshFace*> (anElem);
11607 // MESSAGE("aFace=" << aFace->GetID());
11608 bool isQuad = aFace->IsQuadratic();
11609 vector<const SMDS_MeshNode*> ln0, ln1, ln2, ln3, ln4;
11611 // --- clone the nodes, create intermediate nodes for non medium nodes of a quad face
11613 SMDS_ElemIteratorPtr nodeIt = aFace->nodesIterator();
11614 while (nodeIt->more())
11616 const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*> (nodeIt->next());
11617 bool isMedium = isQuad && (aFace->IsMediumNode(node));
11619 ln2.push_back(node);
11621 ln0.push_back(node);
11623 const SMDS_MeshNode* clone = 0;
11624 if (!clonedNodes.count(node))
11626 clone = meshDS->AddNode(node->X(), node->Y(), node->Z());
11627 copyPosition( node, clone );
11628 clonedNodes[node] = clone;
11631 clone = clonedNodes[node];
11634 ln3.push_back(clone);
11636 ln1.push_back(clone);
11638 const SMDS_MeshNode* inter = 0;
11639 if (isQuad && (!isMedium))
11641 if (!intermediateNodes.count(node))
11643 inter = meshDS->AddNode(node->X(), node->Y(), node->Z());
11644 copyPosition( node, inter );
11645 intermediateNodes[node] = inter;
11648 inter = intermediateNodes[node];
11649 ln4.push_back(inter);
11653 // --- extrude the face
11655 vector<const SMDS_MeshNode*> ln;
11656 SMDS_MeshVolume* vol = 0;
11657 vtkIdType aType = aFace->GetVtkType();
11661 vol = meshDS->AddVolume(ln0[2], ln0[1], ln0[0], ln1[2], ln1[1], ln1[0]);
11662 // MESSAGE("vol prism " << vol->GetID());
11663 ln.push_back(ln1[0]);
11664 ln.push_back(ln1[1]);
11665 ln.push_back(ln1[2]);
11668 vol = meshDS->AddVolume(ln0[3], ln0[2], ln0[1], ln0[0], ln1[3], ln1[2], ln1[1], ln1[0]);
11669 // MESSAGE("vol hexa " << vol->GetID());
11670 ln.push_back(ln1[0]);
11671 ln.push_back(ln1[1]);
11672 ln.push_back(ln1[2]);
11673 ln.push_back(ln1[3]);
11675 case VTK_QUADRATIC_TRIANGLE:
11676 vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln0[0], ln0[1], ln0[2], ln3[0], ln3[1], ln3[2],
11677 ln2[0], ln2[1], ln2[2], ln4[0], ln4[1], ln4[2]);
11678 // MESSAGE("vol quad prism " << vol->GetID());
11679 ln.push_back(ln1[0]);
11680 ln.push_back(ln1[1]);
11681 ln.push_back(ln1[2]);
11682 ln.push_back(ln3[0]);
11683 ln.push_back(ln3[1]);
11684 ln.push_back(ln3[2]);
11686 case VTK_QUADRATIC_QUAD:
11687 // vol = meshDS->AddVolume(ln0[0], ln0[1], ln0[2], ln0[3], ln1[0], ln1[1], ln1[2], ln1[3],
11688 // ln2[0], ln2[1], ln2[2], ln2[3], ln3[0], ln3[1], ln3[2], ln3[3],
11689 // ln4[0], ln4[1], ln4[2], ln4[3]);
11690 vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln1[3], ln0[0], ln0[1], ln0[2], ln0[3],
11691 ln3[0], ln3[1], ln3[2], ln3[3], ln2[0], ln2[1], ln2[2], ln2[3],
11692 ln4[0], ln4[1], ln4[2], ln4[3]);
11693 // MESSAGE("vol quad hexa " << vol->GetID());
11694 ln.push_back(ln1[0]);
11695 ln.push_back(ln1[1]);
11696 ln.push_back(ln1[2]);
11697 ln.push_back(ln1[3]);
11698 ln.push_back(ln3[0]);
11699 ln.push_back(ln3[1]);
11700 ln.push_back(ln3[2]);
11701 ln.push_back(ln3[3]);
11711 stringstream grpname;
11715 string namegrp = grpname.str();
11716 if (!mapOfJunctionGroups.count(namegrp))
11717 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
11718 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11720 sgrp->Add(vol->GetID());
11723 // --- modify the face
11725 aFace->ChangeNodes(&ln[0], ln.size());
11732 * \brief identify all the elements around a geom shape, get the faces delimiting the hole
11733 * Build groups of volume to remove, groups of faces to replace on the skin of the object,
11734 * groups of faces to remove inside the object, (idem edges).
11735 * Build ordered list of nodes at the border of each group of faces to replace (to be used to build a geom subshape)
11737 void SMESH_MeshEditor::CreateHoleSkin(double radius,
11738 const TopoDS_Shape& theShape,
11739 SMESH_NodeSearcher* theNodeSearcher,
11740 const char* groupName,
11741 std::vector<double>& nodesCoords,
11742 std::vector<std::vector<int> >& listOfListOfNodes)
11744 MESSAGE("--------------------------------");
11745 MESSAGE("SMESH_MeshEditor::CreateHoleSkin");
11746 MESSAGE("--------------------------------");
11748 // --- zone of volumes to remove is given :
11749 // 1 either by a geom shape (one or more vertices) and a radius,
11750 // 2 either by a group of nodes (representative of the shape)to use with the radius,
11751 // 3 either by a group of nodes where all the elements build on one of this nodes are to remove,
11752 // In the case 2, the group of nodes is an external group of nodes from another mesh,
11753 // In the case 3, the group of nodes is an internal group of the mesh (obtained for instance by a filter),
11754 // defined by it's name.
11756 SMESHDS_GroupBase* groupDS = 0;
11757 SMESH_Mesh::GroupIteratorPtr groupIt = this->myMesh->GetGroups();
11758 while ( groupIt->more() )
11761 SMESH_Group * group = groupIt->next();
11762 if ( !group ) continue;
11763 groupDS = group->GetGroupDS();
11764 if ( !groupDS || groupDS->IsEmpty() ) continue;
11765 std::string grpName = group->GetName();
11766 //MESSAGE("grpName=" << grpName);
11767 if (grpName == groupName)
11773 bool isNodeGroup = false;
11774 bool isNodeCoords = false;
11777 if (groupDS->GetType() != SMDSAbs_Node)
11779 isNodeGroup = true; // a group of nodes exists and it is in this mesh
11782 if (nodesCoords.size() > 0)
11783 isNodeCoords = true; // a list o nodes given by their coordinates
11784 //MESSAGE("---" << isNodeGroup << " " << isNodeCoords);
11786 // --- define groups to build
11788 int idg; // --- group of SMDS volumes
11789 string grpvName = groupName;
11790 grpvName += "_vol";
11791 SMESH_Group *grp = this->myMesh->AddGroup(SMDSAbs_Volume, grpvName.c_str(), idg);
11794 MESSAGE("group not created " << grpvName);
11797 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(grp->GetGroupDS());
11799 int idgs; // --- group of SMDS faces on the skin
11800 string grpsName = groupName;
11801 grpsName += "_skin";
11802 SMESH_Group *grps = this->myMesh->AddGroup(SMDSAbs_Face, grpsName.c_str(), idgs);
11805 MESSAGE("group not created " << grpsName);
11808 SMESHDS_Group *sgrps = dynamic_cast<SMESHDS_Group*>(grps->GetGroupDS());
11810 int idgi; // --- group of SMDS faces internal (several shapes)
11811 string grpiName = groupName;
11812 grpiName += "_internalFaces";
11813 SMESH_Group *grpi = this->myMesh->AddGroup(SMDSAbs_Face, grpiName.c_str(), idgi);
11816 MESSAGE("group not created " << grpiName);
11819 SMESHDS_Group *sgrpi = dynamic_cast<SMESHDS_Group*>(grpi->GetGroupDS());
11821 int idgei; // --- group of SMDS faces internal (several shapes)
11822 string grpeiName = groupName;
11823 grpeiName += "_internalEdges";
11824 SMESH_Group *grpei = this->myMesh->AddGroup(SMDSAbs_Edge, grpeiName.c_str(), idgei);
11827 MESSAGE("group not created " << grpeiName);
11830 SMESHDS_Group *sgrpei = dynamic_cast<SMESHDS_Group*>(grpei->GetGroupDS());
11832 // --- build downward connectivity
11834 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11835 meshDS->BuildDownWardConnectivity(true);
11836 SMDS_UnstructuredGrid* grid = meshDS->getGrid();
11838 // --- set of volumes detected inside
11840 std::set<int> setOfInsideVol;
11841 std::set<int> setOfVolToCheck;
11843 std::vector<gp_Pnt> gpnts;
11846 if (isNodeGroup) // --- a group of nodes is provided : find all the volumes using one or more of this nodes
11848 MESSAGE("group of nodes provided");
11849 SMDS_ElemIteratorPtr elemIt = groupDS->GetElements();
11850 while ( elemIt->more() )
11852 const SMDS_MeshElement* elem = elemIt->next();
11855 const SMDS_MeshNode* node = dynamic_cast<const SMDS_MeshNode*>(elem);
11858 SMDS_MeshElement* vol = 0;
11859 SMDS_ElemIteratorPtr volItr = node->GetInverseElementIterator(SMDSAbs_Volume);
11860 while (volItr->more())
11862 vol = (SMDS_MeshElement*)volItr->next();
11863 setOfInsideVol.insert(vol->getVtkId());
11864 sgrp->Add(vol->GetID());
11868 else if (isNodeCoords)
11870 MESSAGE("list of nodes coordinates provided");
11873 while (i < nodesCoords.size()-2)
11875 double x = nodesCoords[i++];
11876 double y = nodesCoords[i++];
11877 double z = nodesCoords[i++];
11878 gp_Pnt p = gp_Pnt(x, y ,z);
11879 gpnts.push_back(p);
11880 MESSAGE("TopoDS_Vertex " << k << " " << p.X() << " " << p.Y() << " " << p.Z());
11884 else // --- no group, no coordinates : use the vertices of the geom shape provided, and radius
11886 MESSAGE("no group of nodes provided, using vertices from geom shape, and radius");
11887 TopTools_IndexedMapOfShape vertexMap;
11888 TopExp::MapShapes( theShape, TopAbs_VERTEX, vertexMap );
11889 gp_Pnt p = gp_Pnt(0,0,0);
11890 if (vertexMap.Extent() < 1)
11893 for ( int i = 1; i <= vertexMap.Extent(); ++i )
11895 const TopoDS_Vertex& vertex = TopoDS::Vertex( vertexMap( i ));
11896 p = BRep_Tool::Pnt(vertex);
11897 gpnts.push_back(p);
11898 MESSAGE("TopoDS_Vertex " << i << " " << p.X() << " " << p.Y() << " " << p.Z());
11902 if (gpnts.size() > 0)
11905 const SMDS_MeshNode* startNode = theNodeSearcher->FindClosestTo(gpnts[0]);
11907 nodeId = startNode->GetID();
11908 MESSAGE("nodeId " << nodeId);
11910 double radius2 = radius*radius;
11911 MESSAGE("radius2 " << radius2);
11913 // --- volumes on start node
11915 setOfVolToCheck.clear();
11916 SMDS_MeshElement* startVol = 0;
11917 SMDS_ElemIteratorPtr volItr = startNode->GetInverseElementIterator(SMDSAbs_Volume);
11918 while (volItr->more())
11920 startVol = (SMDS_MeshElement*)volItr->next();
11921 setOfVolToCheck.insert(startVol->getVtkId());
11923 if (setOfVolToCheck.empty())
11925 MESSAGE("No volumes found");
11929 // --- starting with central volumes then their neighbors, check if they are inside
11930 // or outside the domain, until no more new neighbor volume is inside.
11931 // Fill the group of inside volumes
11933 std::map<int, double> mapOfNodeDistance2;
11934 mapOfNodeDistance2.clear();
11935 std::set<int> setOfOutsideVol;
11936 while (!setOfVolToCheck.empty())
11938 std::set<int>::iterator it = setOfVolToCheck.begin();
11940 MESSAGE("volume to check, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
11941 bool volInside = false;
11942 vtkIdType npts = 0;
11943 vtkIdType* pts = 0;
11944 grid->GetCellPoints(vtkId, npts, pts);
11945 for (int i=0; i<npts; i++)
11947 double distance2 = 0;
11948 if (mapOfNodeDistance2.count(pts[i]))
11950 distance2 = mapOfNodeDistance2[pts[i]];
11951 MESSAGE("point " << pts[i] << " distance2 " << distance2);
11955 double *coords = grid->GetPoint(pts[i]);
11956 gp_Pnt aPoint = gp_Pnt(coords[0], coords[1], coords[2]);
11958 for (int j=0; j<gpnts.size(); j++)
11960 double d2 = aPoint.SquareDistance(gpnts[j]);
11961 if (d2 < distance2)
11964 if (distance2 < radius2)
11968 mapOfNodeDistance2[pts[i]] = distance2;
11969 MESSAGE(" point " << pts[i] << " distance2 " << distance2 << " coords " << coords[0] << " " << coords[1] << " " << coords[2]);
11971 if (distance2 < radius2)
11973 volInside = true; // one or more nodes inside the domain
11974 sgrp->Add(meshDS->fromVtkToSmds(vtkId));
11980 setOfInsideVol.insert(vtkId);
11981 MESSAGE(" volume inside, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
11982 int neighborsVtkIds[NBMAXNEIGHBORS];
11983 int downIds[NBMAXNEIGHBORS];
11984 unsigned char downTypes[NBMAXNEIGHBORS];
11985 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
11986 for (int n = 0; n < nbNeighbors; n++)
11987 if (!setOfInsideVol.count(neighborsVtkIds[n]) ||setOfOutsideVol.count(neighborsVtkIds[n]))
11988 setOfVolToCheck.insert(neighborsVtkIds[n]);
11992 setOfOutsideVol.insert(vtkId);
11993 MESSAGE(" volume outside, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
11995 setOfVolToCheck.erase(vtkId);
11999 // --- for outside hexahedrons, check if they have more than one neighbor volume inside
12000 // If yes, add the volume to the inside set
12002 bool addedInside = true;
12003 std::set<int> setOfVolToReCheck;
12004 while (addedInside)
12006 MESSAGE(" --------------------------- re check");
12007 addedInside = false;
12008 std::set<int>::iterator itv = setOfInsideVol.begin();
12009 for (; itv != setOfInsideVol.end(); ++itv)
12012 int neighborsVtkIds[NBMAXNEIGHBORS];
12013 int downIds[NBMAXNEIGHBORS];
12014 unsigned char downTypes[NBMAXNEIGHBORS];
12015 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12016 for (int n = 0; n < nbNeighbors; n++)
12017 if (!setOfInsideVol.count(neighborsVtkIds[n]))
12018 setOfVolToReCheck.insert(neighborsVtkIds[n]);
12020 setOfVolToCheck = setOfVolToReCheck;
12021 setOfVolToReCheck.clear();
12022 while (!setOfVolToCheck.empty())
12024 std::set<int>::iterator it = setOfVolToCheck.begin();
12026 if (grid->GetCellType(vtkId) == VTK_HEXAHEDRON)
12028 MESSAGE("volume to recheck, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12029 int countInside = 0;
12030 int neighborsVtkIds[NBMAXNEIGHBORS];
12031 int downIds[NBMAXNEIGHBORS];
12032 unsigned char downTypes[NBMAXNEIGHBORS];
12033 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12034 for (int n = 0; n < nbNeighbors; n++)
12035 if (setOfInsideVol.count(neighborsVtkIds[n]))
12037 MESSAGE("countInside " << countInside);
12038 if (countInside > 1)
12040 MESSAGE(" volume inside, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12041 setOfInsideVol.insert(vtkId);
12042 sgrp->Add(meshDS->fromVtkToSmds(vtkId));
12043 addedInside = true;
12046 setOfVolToReCheck.insert(vtkId);
12048 setOfVolToCheck.erase(vtkId);
12052 // --- map of Downward faces at the boundary, inside the global volume
12053 // map of Downward faces on the skin of the global volume (equivalent to SMDS faces on the skin)
12054 // fill group of SMDS faces inside the volume (when several volume shapes)
12055 // fill group of SMDS faces on the skin of the global volume (if skin)
12057 std::map<DownIdType, int, DownIdCompare> boundaryFaces; // boundary faces inside the volume --> corresponding cell
12058 std::map<DownIdType, int, DownIdCompare> skinFaces; // faces on the skin of the global volume --> corresponding cell
12059 std::set<int>::iterator it = setOfInsideVol.begin();
12060 for (; it != setOfInsideVol.end(); ++it)
12063 //MESSAGE(" vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12064 int neighborsVtkIds[NBMAXNEIGHBORS];
12065 int downIds[NBMAXNEIGHBORS];
12066 unsigned char downTypes[NBMAXNEIGHBORS];
12067 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId, true);
12068 for (int n = 0; n < nbNeighbors; n++)
12070 int neighborDim = SMDS_Downward::getCellDimension(grid->GetCellType(neighborsVtkIds[n]));
12071 if (neighborDim == 3)
12073 if (! setOfInsideVol.count(neighborsVtkIds[n])) // neighbor volume is not inside : face is boundary
12075 DownIdType face(downIds[n], downTypes[n]);
12076 boundaryFaces[face] = vtkId;
12078 // if the face between to volumes is in the mesh, get it (internal face between shapes)
12079 int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12080 if (vtkFaceId >= 0)
12082 sgrpi->Add(meshDS->fromVtkToSmds(vtkFaceId));
12083 // find also the smds edges on this face
12084 int nbEdges = grid->getDownArray(downTypes[n])->getNumberOfDownCells(downIds[n]);
12085 const int* dEdges = grid->getDownArray(downTypes[n])->getDownCells(downIds[n]);
12086 const unsigned char* dTypes = grid->getDownArray(downTypes[n])->getDownTypes(downIds[n]);
12087 for (int i = 0; i < nbEdges; i++)
12089 int vtkEdgeId = grid->getDownArray(dTypes[i])->getVtkCellId(dEdges[i]);
12090 if (vtkEdgeId >= 0)
12091 sgrpei->Add(meshDS->fromVtkToSmds(vtkEdgeId));
12095 else if (neighborDim == 2) // skin of the volume
12097 DownIdType face(downIds[n], downTypes[n]);
12098 skinFaces[face] = vtkId;
12099 int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12100 if (vtkFaceId >= 0)
12101 sgrps->Add(meshDS->fromVtkToSmds(vtkFaceId));
12106 // --- identify the edges constituting the wire of each subshape on the skin
12107 // define polylines with the nodes of edges, equivalent to wires
12108 // project polylines on subshapes, and partition, to get geom faces
12110 std::map<int, std::set<int> > shapeIdToVtkIdSet; // shapeId --> set of vtkId on skin
12111 std::set<int> emptySet;
12113 std::set<int> shapeIds;
12115 SMDS_ElemIteratorPtr itelem = sgrps->GetElements();
12116 while (itelem->more())
12118 const SMDS_MeshElement *elem = itelem->next();
12119 int shapeId = elem->getshapeId();
12120 int vtkId = elem->getVtkId();
12121 if (!shapeIdToVtkIdSet.count(shapeId))
12123 shapeIdToVtkIdSet[shapeId] = emptySet;
12124 shapeIds.insert(shapeId);
12126 shapeIdToVtkIdSet[shapeId].insert(vtkId);
12129 std::map<int, std::set<DownIdType, DownIdCompare> > shapeIdToEdges; // shapeId --> set of downward edges
12130 std::set<DownIdType, DownIdCompare> emptyEdges;
12131 emptyEdges.clear();
12133 std::map<int, std::set<int> >::iterator itShape = shapeIdToVtkIdSet.begin();
12134 for (; itShape != shapeIdToVtkIdSet.end(); ++itShape)
12136 int shapeId = itShape->first;
12137 MESSAGE(" --- Shape ID --- "<< shapeId);
12138 shapeIdToEdges[shapeId] = emptyEdges;
12140 std::vector<int> nodesEdges;
12142 std::set<int>::iterator its = itShape->second.begin();
12143 for (; its != itShape->second.end(); ++its)
12146 MESSAGE(" " << vtkId);
12147 int neighborsVtkIds[NBMAXNEIGHBORS];
12148 int downIds[NBMAXNEIGHBORS];
12149 unsigned char downTypes[NBMAXNEIGHBORS];
12150 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12151 for (int n = 0; n < nbNeighbors; n++)
12153 if (neighborsVtkIds[n]<0) // only smds faces are considered as neighbors here
12155 int smdsId = meshDS->fromVtkToSmds(neighborsVtkIds[n]);
12156 const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
12157 if ( shapeIds.count(elem->getshapeId()) && !sgrps->Contains(elem)) // edge : neighbor in the set of shape, not in the group
12159 DownIdType edge(downIds[n], downTypes[n]);
12160 if (!shapeIdToEdges[shapeId].count(edge))
12162 shapeIdToEdges[shapeId].insert(edge);
12164 int nbNodes = grid->getDownArray(downTypes[n])->getNodes(downIds[n],vtkNodeId);
12165 nodesEdges.push_back(vtkNodeId[0]);
12166 nodesEdges.push_back(vtkNodeId[nbNodes-1]);
12167 MESSAGE(" --- nodes " << vtkNodeId[0]+1 << " " << vtkNodeId[nbNodes-1]+1);
12173 std::list<int> order;
12175 if (nodesEdges.size() > 0)
12177 order.push_back(nodesEdges[0]); MESSAGE(" --- back " << order.back()+1); // SMDS id = VTK id + 1;
12178 nodesEdges[0] = -1;
12179 order.push_back(nodesEdges[1]); MESSAGE(" --- back " << order.back()+1);
12180 nodesEdges[1] = -1; // do not reuse this edge
12184 int nodeTofind = order.back(); // try first to push back
12186 for (i = 0; i<nodesEdges.size(); i++)
12187 if (nodesEdges[i] == nodeTofind)
12189 if (i == nodesEdges.size())
12190 found = false; // no follower found on back
12193 if (i%2) // odd ==> use the previous one
12194 if (nodesEdges[i-1] < 0)
12198 order.push_back(nodesEdges[i-1]); MESSAGE(" --- back " << order.back()+1);
12199 nodesEdges[i-1] = -1;
12201 else // even ==> use the next one
12202 if (nodesEdges[i+1] < 0)
12206 order.push_back(nodesEdges[i+1]); MESSAGE(" --- back " << order.back()+1);
12207 nodesEdges[i+1] = -1;
12212 // try to push front
12214 nodeTofind = order.front(); // try to push front
12215 for (i = 0; i<nodesEdges.size(); i++)
12216 if (nodesEdges[i] == nodeTofind)
12218 if (i == nodesEdges.size())
12220 found = false; // no predecessor found on front
12223 if (i%2) // odd ==> use the previous one
12224 if (nodesEdges[i-1] < 0)
12228 order.push_front(nodesEdges[i-1]); MESSAGE(" --- front " << order.front()+1);
12229 nodesEdges[i-1] = -1;
12231 else // even ==> use the next one
12232 if (nodesEdges[i+1] < 0)
12236 order.push_front(nodesEdges[i+1]); MESSAGE(" --- front " << order.front()+1);
12237 nodesEdges[i+1] = -1;
12243 std::vector<int> nodes;
12244 nodes.push_back(shapeId);
12245 std::list<int>::iterator itl = order.begin();
12246 for (; itl != order.end(); itl++)
12248 nodes.push_back((*itl) + 1); // SMDS id = VTK id + 1;
12249 MESSAGE(" ordered node " << nodes[nodes.size()-1]);
12251 listOfListOfNodes.push_back(nodes);
12254 // partition geom faces with blocFissure
12255 // mesh blocFissure and geom faces of the skin (external wires given, triangle algo to choose)
12256 // mesh volume around blocFissure (skin triangles and quadrangle given, tetra algo to choose)
12262 //================================================================================
12264 * \brief Generates skin mesh (containing 2D cells) from 3D mesh
12265 * The created 2D mesh elements based on nodes of free faces of boundary volumes
12266 * \return TRUE if operation has been completed successfully, FALSE otherwise
12268 //================================================================================
12270 bool SMESH_MeshEditor::Make2DMeshFrom3D()
12272 // iterates on volume elements and detect all free faces on them
12273 SMESHDS_Mesh* aMesh = GetMeshDS();
12276 //bool res = false;
12277 int nbFree = 0, nbExisted = 0, nbCreated = 0;
12278 SMDS_VolumeIteratorPtr vIt = aMesh->volumesIterator();
12281 const SMDS_MeshVolume* volume = vIt->next();
12282 SMDS_VolumeTool vTool( volume, /*ignoreCentralNodes=*/false );
12283 vTool.SetExternalNormal();
12284 //const bool isPoly = volume->IsPoly();
12285 const int iQuad = volume->IsQuadratic();
12286 for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12288 if (!vTool.IsFreeFace(iface))
12291 vector<const SMDS_MeshNode *> nodes;
12292 int nbFaceNodes = vTool.NbFaceNodes(iface);
12293 const SMDS_MeshNode** faceNodes = vTool.GetFaceNodes(iface);
12295 for ( ; inode < nbFaceNodes; inode += iQuad+1)
12296 nodes.push_back(faceNodes[inode]);
12297 if (iQuad) { // add medium nodes
12298 for ( inode = 1; inode < nbFaceNodes; inode += 2)
12299 nodes.push_back(faceNodes[inode]);
12300 if ( nbFaceNodes == 9 ) // bi-quadratic quad
12301 nodes.push_back(faceNodes[8]);
12303 // add new face based on volume nodes
12304 if (aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false) ) {
12306 continue; // face already exsist
12308 AddElement(nodes, SMDSAbs_Face, ( !iQuad && nbFaceNodes/(iQuad+1) > 4 ));
12312 return ( nbFree==(nbExisted+nbCreated) );
12317 inline const SMDS_MeshNode* getNodeWithSameID(SMESHDS_Mesh* mesh, const SMDS_MeshNode* node)
12319 if ( const SMDS_MeshNode* n = mesh->FindNode( node->GetID() ))
12321 return mesh->AddNodeWithID( node->X(),node->Y(),node->Z(), node->GetID() );
12324 //================================================================================
12326 * \brief Creates missing boundary elements
12327 * \param elements - elements whose boundary is to be checked
12328 * \param dimension - defines type of boundary elements to create
12329 * \param group - a group to store created boundary elements in
12330 * \param targetMesh - a mesh to store created boundary elements in
12331 * \param toCopyElements - if true, the checked elements will be copied into the targetMesh
12332 * \param toCopyExistingBoundary - if true, not only new but also pre-existing
12333 * boundary elements will be copied into the targetMesh
12334 * \param toAddExistingBondary - if true, not only new but also pre-existing
12335 * boundary elements will be added into the new group
12336 * \param aroundElements - if true, elements will be created on boundary of given
12337 * elements else, on boundary of the whole mesh.
12338 * \return nb of added boundary elements
12340 //================================================================================
12342 int SMESH_MeshEditor::MakeBoundaryMesh(const TIDSortedElemSet& elements,
12343 Bnd_Dimension dimension,
12344 SMESH_Group* group/*=0*/,
12345 SMESH_Mesh* targetMesh/*=0*/,
12346 bool toCopyElements/*=false*/,
12347 bool toCopyExistingBoundary/*=false*/,
12348 bool toAddExistingBondary/*= false*/,
12349 bool aroundElements/*= false*/)
12351 SMDSAbs_ElementType missType = (dimension == BND_2DFROM3D) ? SMDSAbs_Face : SMDSAbs_Edge;
12352 SMDSAbs_ElementType elemType = (dimension == BND_1DFROM2D) ? SMDSAbs_Face : SMDSAbs_Volume;
12353 // hope that all elements are of the same type, do not check them all
12354 if ( !elements.empty() && (*elements.begin())->GetType() != elemType )
12355 throw SALOME_Exception(LOCALIZED("wrong element type"));
12358 toCopyElements = toCopyExistingBoundary = false;
12360 SMESH_MeshEditor tgtEditor( targetMesh ? targetMesh : myMesh );
12361 SMESHDS_Mesh* aMesh = GetMeshDS(), *tgtMeshDS = tgtEditor.GetMeshDS();
12362 int nbAddedBnd = 0;
12364 // editor adding present bnd elements and optionally holding elements to add to the group
12365 SMESH_MeshEditor* presentEditor;
12366 SMESH_MeshEditor tgtEditor2( tgtEditor.GetMesh() );
12367 presentEditor = toAddExistingBondary ? &tgtEditor : &tgtEditor2;
12369 SMESH_MesherHelper helper( *myMesh );
12370 const TopAbs_ShapeEnum missShapeType = ( missType==SMDSAbs_Face ? TopAbs_FACE : TopAbs_EDGE );
12371 SMDS_VolumeTool vTool;
12372 TIDSortedElemSet avoidSet;
12373 const TIDSortedElemSet emptySet, *elemSet = aroundElements ? &elements : &emptySet;
12376 typedef vector<const SMDS_MeshNode*> TConnectivity;
12378 SMDS_ElemIteratorPtr eIt;
12379 if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
12380 else eIt = elemSetIterator( elements );
12382 while (eIt->more())
12384 const SMDS_MeshElement* elem = eIt->next();
12385 const int iQuad = elem->IsQuadratic();
12387 // ------------------------------------------------------------------------------------
12388 // 1. For an elem, get present bnd elements and connectivities of missing bnd elements
12389 // ------------------------------------------------------------------------------------
12390 vector<const SMDS_MeshElement*> presentBndElems;
12391 vector<TConnectivity> missingBndElems;
12392 TConnectivity nodes, elemNodes;
12393 if ( vTool.Set(elem, /*ignoreCentralNodes=*/true) ) // elem is a volume --------------
12395 vTool.SetExternalNormal();
12396 const SMDS_MeshElement* otherVol = 0;
12397 for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12399 if ( !vTool.IsFreeFace(iface, &otherVol) &&
12400 ( !aroundElements || elements.count( otherVol )))
12402 const SMDS_MeshNode** nn = vTool.GetFaceNodes(iface);
12403 const int nbFaceNodes = vTool.NbFaceNodes (iface);
12404 if ( missType == SMDSAbs_Edge ) // boundary edges
12406 nodes.resize( 2+iQuad );
12407 for ( int i = 0; i < nbFaceNodes; i += 1+iQuad)
12409 for ( int j = 0; j < nodes.size(); ++j )
12411 if ( const SMDS_MeshElement* edge =
12412 aMesh->FindElement(nodes,SMDSAbs_Edge,/*noMedium=*/false))
12413 presentBndElems.push_back( edge );
12415 missingBndElems.push_back( nodes );
12418 else // boundary face
12421 for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12422 nodes.push_back( nn[inode] ); // add corner nodes
12424 for ( inode = 1; inode < nbFaceNodes; inode += 2)
12425 nodes.push_back( nn[inode] ); // add medium nodes
12426 int iCenter = vTool.GetCenterNodeIndex(iface); // for HEX27
12428 nodes.push_back( vTool.GetNodes()[ iCenter ] );
12430 if (const SMDS_MeshElement * f = aMesh->FindElement( nodes,
12431 SMDSAbs_Face, /*noMedium=*/false ))
12432 presentBndElems.push_back( f );
12434 missingBndElems.push_back( nodes );
12436 if ( targetMesh != myMesh )
12438 // add 1D elements on face boundary to be added to a new mesh
12439 const SMDS_MeshElement* edge;
12440 for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12443 edge = aMesh->FindEdge( nn[inode], nn[inode+1], nn[inode+2]);
12445 edge = aMesh->FindEdge( nn[inode], nn[inode+1]);
12446 if ( edge && avoidSet.insert( edge ).second )
12447 presentBndElems.push_back( edge );
12453 else if ( elem->GetType() == SMDSAbs_Face ) // elem is a face ------------------------
12455 avoidSet.clear(), avoidSet.insert( elem );
12456 elemNodes.assign( SMDS_MeshElement::iterator( elem->interlacedNodesElemIterator() ),
12457 SMDS_MeshElement::iterator() );
12458 elemNodes.push_back( elemNodes[0] );
12459 nodes.resize( 2 + iQuad );
12460 const int nbLinks = elem->NbCornerNodes();
12461 for ( int i = 0, iN = 0; i < nbLinks; i++, iN += 1+iQuad )
12463 nodes[0] = elemNodes[iN];
12464 nodes[1] = elemNodes[iN+1+iQuad];
12465 if ( SMESH_MeshAlgos::FindFaceInSet( nodes[0], nodes[1], *elemSet, avoidSet))
12466 continue; // not free link
12468 if ( iQuad ) nodes[2] = elemNodes[iN+1];
12469 if ( const SMDS_MeshElement* edge =
12470 aMesh->FindElement(nodes,SMDSAbs_Edge,/*noMedium=*/false))
12471 presentBndElems.push_back( edge );
12473 missingBndElems.push_back( nodes );
12477 // ---------------------------------
12478 // 2. Add missing boundary elements
12479 // ---------------------------------
12480 if ( targetMesh != myMesh )
12481 // instead of making a map of nodes in this mesh and targetMesh,
12482 // we create nodes with same IDs.
12483 for ( int i = 0; i < missingBndElems.size(); ++i )
12485 TConnectivity& srcNodes = missingBndElems[i];
12486 TConnectivity nodes( srcNodes.size() );
12487 for ( inode = 0; inode < nodes.size(); ++inode )
12488 nodes[inode] = getNodeWithSameID( tgtMeshDS, srcNodes[inode] );
12489 if ( aroundElements && tgtEditor.GetMeshDS()->FindElement( nodes,
12491 /*noMedium=*/false))
12493 tgtEditor.AddElement(nodes, missType, !iQuad && nodes.size()/(iQuad+1)>4);
12497 for ( int i = 0; i < missingBndElems.size(); ++i )
12499 TConnectivity& nodes = missingBndElems[i];
12500 if ( aroundElements && tgtEditor.GetMeshDS()->FindElement( nodes,
12502 /*noMedium=*/false))
12504 SMDS_MeshElement* elem =
12505 tgtEditor.AddElement(nodes, missType, !iQuad && nodes.size()/(iQuad+1)>4);
12508 // try to set a new element to a shape
12509 if ( myMesh->HasShapeToMesh() )
12512 set< pair<TopAbs_ShapeEnum, int > > mediumShapes;
12513 const int nbN = nodes.size() / (iQuad+1 );
12514 for ( inode = 0; inode < nbN && ok; ++inode )
12516 pair<int, TopAbs_ShapeEnum> i_stype =
12517 helper.GetMediumPos( nodes[inode], nodes[(inode+1)%nbN]);
12518 if (( ok = ( i_stype.first > 0 && i_stype.second >= TopAbs_FACE )))
12519 mediumShapes.insert( make_pair ( i_stype.second, i_stype.first ));
12521 if ( ok && mediumShapes.size() > 1 )
12523 set< pair<TopAbs_ShapeEnum, int > >::iterator stype_i = mediumShapes.begin();
12524 pair<TopAbs_ShapeEnum, int> stype_i_0 = *stype_i;
12525 for ( ++stype_i; stype_i != mediumShapes.end() && ok; ++stype_i )
12527 if (( ok = ( stype_i->first != stype_i_0.first )))
12528 ok = helper.IsSubShape( aMesh->IndexToShape( stype_i->second ),
12529 aMesh->IndexToShape( stype_i_0.second ));
12532 if ( ok && mediumShapes.begin()->first == missShapeType )
12533 aMesh->SetMeshElementOnShape( elem, mediumShapes.begin()->second );
12537 // ----------------------------------
12538 // 3. Copy present boundary elements
12539 // ----------------------------------
12540 if ( toCopyExistingBoundary )
12541 for ( int i = 0 ; i < presentBndElems.size(); ++i )
12543 const SMDS_MeshElement* e = presentBndElems[i];
12544 TConnectivity nodes( e->NbNodes() );
12545 for ( inode = 0; inode < nodes.size(); ++inode )
12546 nodes[inode] = getNodeWithSameID( tgtMeshDS, e->GetNode(inode) );
12547 presentEditor->AddElement(nodes, e->GetType(), e->IsPoly());
12549 else // store present elements to add them to a group
12550 for ( int i = 0 ; i < presentBndElems.size(); ++i )
12552 presentEditor->myLastCreatedElems.Append(presentBndElems[i]);
12555 } // loop on given elements
12557 // ---------------------------------------------
12558 // 4. Fill group with boundary elements
12559 // ---------------------------------------------
12562 if ( SMESHDS_Group* g = dynamic_cast<SMESHDS_Group*>( group->GetGroupDS() ))
12563 for ( int i = 0; i < tgtEditor.myLastCreatedElems.Size(); ++i )
12564 g->SMDSGroup().Add( tgtEditor.myLastCreatedElems( i+1 ));
12566 tgtEditor.myLastCreatedElems.Clear();
12567 tgtEditor2.myLastCreatedElems.Clear();
12569 // -----------------------
12570 // 5. Copy given elements
12571 // -----------------------
12572 if ( toCopyElements && targetMesh != myMesh )
12574 if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
12575 else eIt = elemSetIterator( elements );
12576 while (eIt->more())
12578 const SMDS_MeshElement* elem = eIt->next();
12579 TConnectivity nodes( elem->NbNodes() );
12580 for ( inode = 0; inode < nodes.size(); ++inode )
12581 nodes[inode] = getNodeWithSameID( tgtMeshDS, elem->GetNode(inode) );
12582 tgtEditor.AddElement(nodes, elemType, elem->IsPoly());
12584 tgtEditor.myLastCreatedElems.Clear();
12590 //================================================================================
12592 * \brief Copy node position and set \a to node on the same geometry
12594 //================================================================================
12596 void SMESH_MeshEditor::copyPosition( const SMDS_MeshNode* from,
12597 const SMDS_MeshNode* to )
12599 if ( !from || !to ) return;
12601 SMDS_PositionPtr pos = from->GetPosition();
12602 if ( !pos || from->getshapeId() < 1 ) return;
12604 switch ( pos->GetTypeOfPosition() )
12606 case SMDS_TOP_3DSPACE: break;
12608 case SMDS_TOP_FACE:
12610 const SMDS_FacePosition* fPos = static_cast< const SMDS_FacePosition* >( pos );
12611 GetMeshDS()->SetNodeOnFace( to, from->getshapeId(),
12612 fPos->GetUParameter(), fPos->GetVParameter() );
12615 case SMDS_TOP_EDGE:
12617 // WARNING: it is dangerous to set equal nodes on one EDGE!!!!!!!!
12618 const SMDS_EdgePosition* ePos = static_cast< const SMDS_EdgePosition* >( pos );
12619 GetMeshDS()->SetNodeOnEdge( to, from->getshapeId(), ePos->GetUParameter() );
12622 case SMDS_TOP_VERTEX:
12624 GetMeshDS()->SetNodeOnVertex( to, from->getshapeId() );
12627 case SMDS_TOP_UNSPEC: