1 // Copyright (C) 2007-2014 CEA/DEN, EDF R&D, OPEN CASCADE
3 // Copyright (C) 2003-2007 OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN,
4 // CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS
6 // This library is free software; you can redistribute it and/or
7 // modify it under the terms of the GNU Lesser General Public
8 // License as published by the Free Software Foundation; either
9 // version 2.1 of the License, or (at your option) any later version.
11 // This library is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 // Lesser General Public License for more details.
16 // You should have received a copy of the GNU Lesser General Public
17 // License along with this library; if not, write to the Free Software
18 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
23 // File : SMESH_MeshEditor.cxx
24 // Created : Mon Apr 12 16:10:22 2004
25 // Author : Edward AGAPOV (eap)
27 #include "SMESH_MeshEditor.hxx"
29 #include "SMDS_FaceOfNodes.hxx"
30 #include "SMDS_VolumeTool.hxx"
31 #include "SMDS_EdgePosition.hxx"
32 #include "SMDS_FacePosition.hxx"
33 #include "SMDS_SpacePosition.hxx"
34 #include "SMDS_MeshGroup.hxx"
35 #include "SMDS_LinearEdge.hxx"
36 #include "SMDS_Downward.hxx"
37 #include "SMDS_SetIterator.hxx"
39 #include "SMESHDS_Group.hxx"
40 #include "SMESHDS_Mesh.hxx"
42 #include "SMESH_Algo.hxx"
43 #include "SMESH_ControlsDef.hxx"
44 #include "SMESH_Group.hxx"
45 #include "SMESH_MeshAlgos.hxx"
46 #include "SMESH_MesherHelper.hxx"
47 #include "SMESH_OctreeNode.hxx"
48 #include "SMESH_subMesh.hxx"
50 #include <Basics_OCCTVersion.hxx>
52 #include "utilities.h"
55 #include <BRepAdaptor_Surface.hxx>
56 #include <BRepBuilderAPI_MakeEdge.hxx>
57 #include <BRepClass3d_SolidClassifier.hxx>
58 #include <BRep_Tool.hxx>
60 #include <Extrema_GenExtPS.hxx>
61 #include <Extrema_POnCurv.hxx>
62 #include <Extrema_POnSurf.hxx>
63 #include <Geom2d_Curve.hxx>
64 #include <GeomAdaptor_Surface.hxx>
65 #include <Geom_Curve.hxx>
66 #include <Geom_Surface.hxx>
67 #include <Precision.hxx>
68 #include <TColStd_ListOfInteger.hxx>
69 #include <TopAbs_State.hxx>
71 #include <TopExp_Explorer.hxx>
72 #include <TopTools_ListIteratorOfListOfShape.hxx>
73 #include <TopTools_ListOfShape.hxx>
74 #include <TopTools_SequenceOfShape.hxx>
76 #include <TopoDS_Face.hxx>
77 #include <TopoDS_Solid.hxx>
83 #include <gp_Trsf.hxx>
97 #include <boost/tuple/tuple.hpp>
99 #include <Standard_Failure.hxx>
100 #include <Standard_ErrorHandler.hxx>
102 #define cast2Node(elem) static_cast<const SMDS_MeshNode*>( elem )
105 using namespace SMESH::Controls;
109 template < class ELEM_SET >
110 SMDS_ElemIteratorPtr elemSetIterator( const ELEM_SET& elements )
112 typedef SMDS_SetIterator
113 < SMDS_pElement, typename ELEM_SET::const_iterator> TSetIterator;
114 return SMDS_ElemIteratorPtr( new TSetIterator( elements.begin(), elements.end() ));
118 //=======================================================================
119 //function : SMESH_MeshEditor
121 //=======================================================================
123 SMESH_MeshEditor::SMESH_MeshEditor( SMESH_Mesh* theMesh )
124 :myMesh( theMesh ) // theMesh may be NULL
128 //================================================================================
130 * \brief Clears myLastCreatedNodes and myLastCreatedElems
132 //================================================================================
134 void SMESH_MeshEditor::CrearLastCreated()
136 myLastCreatedNodes.Clear();
137 myLastCreatedElems.Clear();
141 //=======================================================================
145 //=======================================================================
148 SMESH_MeshEditor::AddElement(const vector<const SMDS_MeshNode*> & node,
149 const SMDSAbs_ElementType type,
152 const double ballDiameter)
154 //MESSAGE("AddElement " <<node.size() << " " << type << " " << isPoly << " " << ID);
155 SMDS_MeshElement* e = 0;
156 int nbnode = node.size();
157 SMESHDS_Mesh* mesh = GetMeshDS();
162 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], ID);
163 else e = mesh->AddFace (node[0], node[1], node[2] );
165 else if (nbnode == 4) {
166 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3], ID);
167 else e = mesh->AddFace (node[0], node[1], node[2], node[3] );
169 else if (nbnode == 6) {
170 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
171 node[4], node[5], ID);
172 else e = mesh->AddFace (node[0], node[1], node[2], node[3],
175 else if (nbnode == 7) {
176 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
177 node[4], node[5], node[6], ID);
178 else e = mesh->AddFace (node[0], node[1], node[2], node[3],
179 node[4], node[5], node[6] );
181 else if (nbnode == 8) {
182 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
183 node[4], node[5], node[6], node[7], ID);
184 else e = mesh->AddFace (node[0], node[1], node[2], node[3],
185 node[4], node[5], node[6], node[7] );
187 else if (nbnode == 9) {
188 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
189 node[4], node[5], node[6], node[7], node[8], ID);
190 else e = mesh->AddFace (node[0], node[1], node[2], node[3],
191 node[4], node[5], node[6], node[7], node[8] );
194 if ( ID >= 1 ) e = mesh->AddPolygonalFaceWithID(node, ID);
195 else e = mesh->AddPolygonalFace (node );
202 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3], ID);
203 else e = mesh->AddVolume (node[0], node[1], node[2], node[3] );
205 else if (nbnode == 5) {
206 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
208 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
211 else if (nbnode == 6) {
212 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
213 node[4], node[5], ID);
214 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
217 else if (nbnode == 8) {
218 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
219 node[4], node[5], node[6], node[7], ID);
220 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
221 node[4], node[5], node[6], node[7] );
223 else if (nbnode == 10) {
224 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
225 node[4], node[5], node[6], node[7],
226 node[8], node[9], ID);
227 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
228 node[4], node[5], node[6], node[7],
231 else if (nbnode == 12) {
232 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
233 node[4], node[5], node[6], node[7],
234 node[8], node[9], node[10], node[11], ID);
235 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
236 node[4], node[5], node[6], node[7],
237 node[8], node[9], node[10], node[11] );
239 else if (nbnode == 13) {
240 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
241 node[4], node[5], node[6], node[7],
242 node[8], node[9], node[10],node[11],
244 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
245 node[4], node[5], node[6], node[7],
246 node[8], node[9], node[10],node[11],
249 else if (nbnode == 15) {
250 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
251 node[4], node[5], node[6], node[7],
252 node[8], node[9], node[10],node[11],
253 node[12],node[13],node[14],ID);
254 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
255 node[4], node[5], node[6], node[7],
256 node[8], node[9], node[10],node[11],
257 node[12],node[13],node[14] );
259 else if (nbnode == 20) {
260 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
261 node[4], node[5], node[6], node[7],
262 node[8], node[9], node[10],node[11],
263 node[12],node[13],node[14],node[15],
264 node[16],node[17],node[18],node[19],ID);
265 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
266 node[4], node[5], node[6], node[7],
267 node[8], node[9], node[10],node[11],
268 node[12],node[13],node[14],node[15],
269 node[16],node[17],node[18],node[19] );
271 else if (nbnode == 27) {
272 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
273 node[4], node[5], node[6], node[7],
274 node[8], node[9], node[10],node[11],
275 node[12],node[13],node[14],node[15],
276 node[16],node[17],node[18],node[19],
277 node[20],node[21],node[22],node[23],
278 node[24],node[25],node[26], ID);
279 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
280 node[4], node[5], node[6], node[7],
281 node[8], node[9], node[10],node[11],
282 node[12],node[13],node[14],node[15],
283 node[16],node[17],node[18],node[19],
284 node[20],node[21],node[22],node[23],
285 node[24],node[25],node[26] );
292 if ( ID >= 1 ) e = mesh->AddEdgeWithID(node[0], node[1], ID);
293 else e = mesh->AddEdge (node[0], node[1] );
295 else if ( nbnode == 3 ) {
296 if ( ID >= 1 ) e = mesh->AddEdgeWithID(node[0], node[1], node[2], ID);
297 else e = mesh->AddEdge (node[0], node[1], node[2] );
301 case SMDSAbs_0DElement:
303 if ( ID >= 1 ) e = mesh->Add0DElementWithID(node[0], ID);
304 else e = mesh->Add0DElement (node[0] );
309 if ( ID >= 1 ) e = mesh->AddNodeWithID(node[0]->X(), node[0]->Y(), node[0]->Z(), ID);
310 else e = mesh->AddNode (node[0]->X(), node[0]->Y(), node[0]->Z());
314 if ( ID >= 1 ) e = mesh->AddBallWithID(node[0], ballDiameter, ID);
315 else e = mesh->AddBall (node[0], ballDiameter);
320 if ( e ) myLastCreatedElems.Append( e );
324 //=======================================================================
328 //=======================================================================
330 SMDS_MeshElement* SMESH_MeshEditor::AddElement(const vector<int> & nodeIDs,
331 const SMDSAbs_ElementType type,
335 vector<const SMDS_MeshNode*> nodes;
336 nodes.reserve( nodeIDs.size() );
337 vector<int>::const_iterator id = nodeIDs.begin();
338 while ( id != nodeIDs.end() ) {
339 if ( const SMDS_MeshNode* node = GetMeshDS()->FindNode( *id++ ))
340 nodes.push_back( node );
344 return AddElement( nodes, type, isPoly, ID );
347 //=======================================================================
349 //purpose : Remove a node or an element.
350 // Modify a compute state of sub-meshes which become empty
351 //=======================================================================
353 int SMESH_MeshEditor::Remove (const list< int >& theIDs,
356 myLastCreatedElems.Clear();
357 myLastCreatedNodes.Clear();
359 SMESHDS_Mesh* aMesh = GetMeshDS();
360 set< SMESH_subMesh *> smmap;
363 list<int>::const_iterator it = theIDs.begin();
364 for ( ; it != theIDs.end(); it++ ) {
365 const SMDS_MeshElement * elem;
367 elem = aMesh->FindNode( *it );
369 elem = aMesh->FindElement( *it );
373 // Notify VERTEX sub-meshes about modification
375 const SMDS_MeshNode* node = cast2Node( elem );
376 if ( node->GetPosition()->GetTypeOfPosition() == SMDS_TOP_VERTEX )
377 if ( int aShapeID = node->getshapeId() )
378 if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( aShapeID ) )
381 // Find sub-meshes to notify about modification
382 // SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
383 // while ( nodeIt->more() ) {
384 // const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
385 // const SMDS_PositionPtr& aPosition = node->GetPosition();
386 // if ( aPosition.get() ) {
387 // if ( int aShapeID = aPosition->GetShapeId() ) {
388 // if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( aShapeID ) )
389 // smmap.insert( sm );
396 aMesh->RemoveNode( static_cast< const SMDS_MeshNode* >( elem ));
398 aMesh->RemoveElement( elem );
402 // Notify sub-meshes about modification
403 if ( !smmap.empty() ) {
404 set< SMESH_subMesh *>::iterator smIt;
405 for ( smIt = smmap.begin(); smIt != smmap.end(); smIt++ )
406 (*smIt)->ComputeStateEngine( SMESH_subMesh::MESH_ENTITY_REMOVED );
409 // // Check if the whole mesh becomes empty
410 // if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( 1 ) )
411 // sm->ComputeStateEngine( SMESH_subMesh::CHECK_COMPUTE_STATE );
416 //================================================================================
418 * \brief Create 0D elements on all nodes of the given object except those
419 * nodes on which a 0D element already exists.
420 * \param elements - Elements on whose nodes to create 0D elements; if empty,
421 * the all mesh is treated
422 * \param all0DElems - returns all 0D elements found or created on nodes of \a elements
424 //================================================================================
426 void SMESH_MeshEditor::Create0DElementsOnAllNodes( const TIDSortedElemSet& elements,
427 TIDSortedElemSet& all0DElems )
429 SMDS_ElemIteratorPtr elemIt;
430 vector< const SMDS_MeshElement* > allNodes;
431 if ( elements.empty() )
433 allNodes.reserve( GetMeshDS()->NbNodes() );
434 elemIt = GetMeshDS()->elementsIterator( SMDSAbs_Node );
435 while ( elemIt->more() )
436 allNodes.push_back( elemIt->next() );
438 elemIt = elemSetIterator( allNodes );
442 elemIt = elemSetIterator( elements );
445 while ( elemIt->more() )
447 const SMDS_MeshElement* e = elemIt->next();
448 SMDS_ElemIteratorPtr nodeIt = e->nodesIterator();
449 while ( nodeIt->more() )
451 const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
452 SMDS_ElemIteratorPtr it0D = n->GetInverseElementIterator( SMDSAbs_0DElement );
454 all0DElems.insert( it0D->next() );
456 myLastCreatedElems.Append( GetMeshDS()->Add0DElement( n ));
457 all0DElems.insert( myLastCreatedElems.Last() );
463 //=======================================================================
464 //function : FindShape
465 //purpose : Return an index of the shape theElem is on
466 // or zero if a shape not found
467 //=======================================================================
469 int SMESH_MeshEditor::FindShape (const SMDS_MeshElement * theElem)
471 myLastCreatedElems.Clear();
472 myLastCreatedNodes.Clear();
474 SMESHDS_Mesh * aMesh = GetMeshDS();
475 if ( aMesh->ShapeToMesh().IsNull() )
478 int aShapeID = theElem->getshapeId();
482 if ( SMESHDS_SubMesh * sm = aMesh->MeshElements( aShapeID ))
483 if ( sm->Contains( theElem ))
486 if ( theElem->GetType() == SMDSAbs_Node ) {
487 MESSAGE( ":( Error: invalid myShapeId of node " << theElem->GetID() );
490 MESSAGE( ":( Error: invalid myShapeId of element " << theElem->GetID() );
493 TopoDS_Shape aShape; // the shape a node of theElem is on
494 if ( theElem->GetType() != SMDSAbs_Node )
496 SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator();
497 while ( nodeIt->more() ) {
498 const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
499 if ((aShapeID = node->getshapeId()) > 0) {
500 if ( SMESHDS_SubMesh * sm = aMesh->MeshElements( aShapeID ) ) {
501 if ( sm->Contains( theElem ))
503 if ( aShape.IsNull() )
504 aShape = aMesh->IndexToShape( aShapeID );
510 // None of nodes is on a proper shape,
511 // find the shape among ancestors of aShape on which a node is
512 if ( !aShape.IsNull() ) {
513 TopTools_ListIteratorOfListOfShape ancIt( GetMesh()->GetAncestors( aShape ));
514 for ( ; ancIt.More(); ancIt.Next() ) {
515 SMESHDS_SubMesh * sm = aMesh->MeshElements( ancIt.Value() );
516 if ( sm && sm->Contains( theElem ))
517 return aMesh->ShapeToIndex( ancIt.Value() );
522 SMESHDS_SubMeshIteratorPtr smIt = GetMeshDS()->SubMeshes();
523 while ( const SMESHDS_SubMesh* sm = smIt->next() )
524 if ( sm->Contains( theElem ))
531 //=======================================================================
532 //function : IsMedium
534 //=======================================================================
536 bool SMESH_MeshEditor::IsMedium(const SMDS_MeshNode* node,
537 const SMDSAbs_ElementType typeToCheck)
539 bool isMedium = false;
540 SMDS_ElemIteratorPtr it = node->GetInverseElementIterator(typeToCheck);
541 while (it->more() && !isMedium ) {
542 const SMDS_MeshElement* elem = it->next();
543 isMedium = elem->IsMediumNode(node);
548 //=======================================================================
549 //function : shiftNodesQuadTria
550 //purpose : Shift nodes in the array corresponded to quadratic triangle
551 // example: (0,1,2,3,4,5) -> (1,2,0,4,5,3)
552 //=======================================================================
554 static void shiftNodesQuadTria(vector< const SMDS_MeshNode* >& aNodes)
556 const SMDS_MeshNode* nd1 = aNodes[0];
557 aNodes[0] = aNodes[1];
558 aNodes[1] = aNodes[2];
560 const SMDS_MeshNode* nd2 = aNodes[3];
561 aNodes[3] = aNodes[4];
562 aNodes[4] = aNodes[5];
566 //=======================================================================
567 //function : nbEdgeConnectivity
568 //purpose : return number of the edges connected with the theNode.
569 // if theEdges has connections with the other type of the
570 // elements, return -1
571 //=======================================================================
573 static int nbEdgeConnectivity(const SMDS_MeshNode* theNode)
575 // SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator();
577 // while(elemIt->more()) {
582 return theNode->NbInverseElements();
585 //=======================================================================
586 //function : getNodesFromTwoTria
588 //=======================================================================
590 static bool getNodesFromTwoTria(const SMDS_MeshElement * theTria1,
591 const SMDS_MeshElement * theTria2,
592 vector< const SMDS_MeshNode*>& N1,
593 vector< const SMDS_MeshNode*>& N2)
595 N1.assign( theTria1->begin_nodes(), theTria1->end_nodes() );
596 if ( N1.size() < 6 ) return false;
597 N2.assign( theTria2->begin_nodes(), theTria2->end_nodes() );
598 if ( N2.size() < 6 ) return false;
600 int sames[3] = {-1,-1,-1};
612 if(nbsames!=2) return false;
614 shiftNodesQuadTria(N1);
616 shiftNodesQuadTria(N1);
619 i = sames[0] + sames[1] + sames[2];
621 shiftNodesQuadTria(N2);
623 // now we receive following N1 and N2 (using numeration as in the image below)
624 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
625 // i.e. first nodes from both arrays form a new diagonal
629 //=======================================================================
630 //function : InverseDiag
631 //purpose : Replace two neighbour triangles with ones built on the same 4 nodes
632 // but having other common link.
633 // Return False if args are improper
634 //=======================================================================
636 bool SMESH_MeshEditor::InverseDiag (const SMDS_MeshElement * theTria1,
637 const SMDS_MeshElement * theTria2 )
639 MESSAGE("InverseDiag");
640 myLastCreatedElems.Clear();
641 myLastCreatedNodes.Clear();
643 if (!theTria1 || !theTria2)
646 const SMDS_VtkFace* F1 = dynamic_cast<const SMDS_VtkFace*>( theTria1 );
647 if (!F1) return false;
648 const SMDS_VtkFace* F2 = dynamic_cast<const SMDS_VtkFace*>( theTria2 );
649 if (!F2) return false;
650 if ((theTria1->GetEntityType() == SMDSEntity_Triangle) &&
651 (theTria2->GetEntityType() == SMDSEntity_Triangle)) {
653 // 1 +--+ A theTria1: ( 1 A B ) A->2 ( 1 2 B ) 1 +--+ A
654 // | /| theTria2: ( B A 2 ) B->1 ( 1 A 2 ) |\ |
658 // put nodes in array and find out indices of the same ones
659 const SMDS_MeshNode* aNodes [6];
660 int sameInd [] = { -1, -1, -1, -1, -1, -1 };
662 SMDS_ElemIteratorPtr it = theTria1->nodesIterator();
663 while ( it->more() ) {
664 aNodes[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
666 if ( i > 2 ) // theTria2
667 // find same node of theTria1
668 for ( int j = 0; j < 3; j++ )
669 if ( aNodes[ i ] == aNodes[ j ]) {
678 return false; // theTria1 is not a triangle
679 it = theTria2->nodesIterator();
681 if ( i == 6 && it->more() )
682 return false; // theTria2 is not a triangle
685 // find indices of 1,2 and of A,B in theTria1
686 int iA = -1, iB = 0, i1 = 0, i2 = 0;
687 for ( i = 0; i < 6; i++ ) {
688 if ( sameInd [ i ] == -1 ) {
693 if ( iA >= 0) iB = i;
697 // nodes 1 and 2 should not be the same
698 if ( aNodes[ i1 ] == aNodes[ i2 ] )
702 aNodes[ iA ] = aNodes[ i2 ];
704 aNodes[ sameInd[ iB ]] = aNodes[ i1 ];
706 GetMeshDS()->ChangeElementNodes( theTria1, aNodes, 3 );
707 GetMeshDS()->ChangeElementNodes( theTria2, &aNodes[ 3 ], 3 );
711 } // end if(F1 && F2)
713 // check case of quadratic faces
714 if (theTria1->GetEntityType() != SMDSEntity_Quad_Triangle &&
715 theTria1->GetEntityType() != SMDSEntity_BiQuad_Triangle)
717 if (theTria2->GetEntityType() != SMDSEntity_Quad_Triangle&&
718 theTria2->GetEntityType() != SMDSEntity_BiQuad_Triangle)
722 // 1 +--+--+ 2 theTria1: (1 2 4 5 9 7) or (2 4 1 9 7 5) or (4 1 2 7 5 9)
723 // | /| theTria2: (2 3 4 6 8 9) or (3 4 2 8 9 6) or (4 2 3 9 6 8)
731 vector< const SMDS_MeshNode* > N1;
732 vector< const SMDS_MeshNode* > N2;
733 if(!getNodesFromTwoTria(theTria1,theTria2,N1,N2))
735 // now we receive following N1 and N2 (using numeration as above image)
736 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
737 // i.e. first nodes from both arrays determ new diagonal
739 vector< const SMDS_MeshNode*> N1new( N1.size() );
740 vector< const SMDS_MeshNode*> N2new( N2.size() );
741 N1new.back() = N1.back(); // central node of biquadratic
742 N2new.back() = N2.back();
743 N1new[0] = N1[0]; N2new[0] = N1[0];
744 N1new[1] = N2[0]; N2new[1] = N1[1];
745 N1new[2] = N2[1]; N2new[2] = N2[0];
746 N1new[3] = N1[4]; N2new[3] = N1[3];
747 N1new[4] = N2[3]; N2new[4] = N2[5];
748 N1new[5] = N1[5]; N2new[5] = N1[4];
749 // change nodes in faces
750 GetMeshDS()->ChangeElementNodes( theTria1, &N1new[0], N1new.size() );
751 GetMeshDS()->ChangeElementNodes( theTria2, &N2new[0], N2new.size() );
753 // move the central node of biquadratic triangle
754 SMESH_MesherHelper helper( *GetMesh() );
755 for ( int is2nd = 0; is2nd < 2; ++is2nd )
757 const SMDS_MeshElement* tria = is2nd ? theTria2 : theTria1;
758 vector< const SMDS_MeshNode*>& nodes = is2nd ? N2new : N1new;
759 if ( nodes.size() < 7 )
761 helper.SetSubShape( tria->getshapeId() );
762 const TopoDS_Face& F = TopoDS::Face( helper.GetSubShape() );
766 xyz = ( SMESH_TNodeXYZ( nodes[3] ) +
767 SMESH_TNodeXYZ( nodes[4] ) +
768 SMESH_TNodeXYZ( nodes[5] )) / 3.;
773 gp_XY uv = ( helper.GetNodeUV( F, nodes[3], nodes[2], &checkUV ) +
774 helper.GetNodeUV( F, nodes[4], nodes[0], &checkUV ) +
775 helper.GetNodeUV( F, nodes[5], nodes[1], &checkUV )) / 3.;
777 Handle(Geom_Surface) S = BRep_Tool::Surface(F,loc);
778 xyz = S->Value( uv.X(), uv.Y() );
779 xyz.Transform( loc );
780 if ( nodes[6]->GetPosition()->GetTypeOfPosition() == SMDS_TOP_FACE && // set UV
781 nodes[6]->getshapeId() > 0 )
782 GetMeshDS()->SetNodeOnFace( nodes[6], nodes[6]->getshapeId(), uv.X(), uv.Y() );
784 GetMeshDS()->MoveNode( nodes[6], xyz.X(), xyz.Y(), xyz.Z() );
789 //=======================================================================
790 //function : findTriangles
791 //purpose : find triangles sharing theNode1-theNode2 link
792 //=======================================================================
794 static bool findTriangles(const SMDS_MeshNode * theNode1,
795 const SMDS_MeshNode * theNode2,
796 const SMDS_MeshElement*& theTria1,
797 const SMDS_MeshElement*& theTria2)
799 if ( !theNode1 || !theNode2 ) return false;
801 theTria1 = theTria2 = 0;
803 set< const SMDS_MeshElement* > emap;
804 SMDS_ElemIteratorPtr it = theNode1->GetInverseElementIterator(SMDSAbs_Face);
806 const SMDS_MeshElement* elem = it->next();
807 if ( elem->NbCornerNodes() == 3 )
810 it = theNode2->GetInverseElementIterator(SMDSAbs_Face);
812 const SMDS_MeshElement* elem = it->next();
813 if ( emap.count( elem )) {
821 // theTria1 must be element with minimum ID
822 if ( theTria2->GetID() < theTria1->GetID() )
823 std::swap( theTria2, theTria1 );
831 //=======================================================================
832 //function : InverseDiag
833 //purpose : Replace two neighbour triangles sharing theNode1-theNode2 link
834 // with ones built on the same 4 nodes but having other common link.
835 // Return false if proper faces not found
836 //=======================================================================
838 bool SMESH_MeshEditor::InverseDiag (const SMDS_MeshNode * theNode1,
839 const SMDS_MeshNode * theNode2)
841 myLastCreatedElems.Clear();
842 myLastCreatedNodes.Clear();
844 MESSAGE( "::InverseDiag()" );
846 const SMDS_MeshElement *tr1, *tr2;
847 if ( !findTriangles( theNode1, theNode2, tr1, tr2 ))
850 const SMDS_VtkFace* F1 = dynamic_cast<const SMDS_VtkFace*>( tr1 );
851 if (!F1) return false;
852 const SMDS_VtkFace* F2 = dynamic_cast<const SMDS_VtkFace*>( tr2 );
853 if (!F2) return false;
854 if ((tr1->GetEntityType() == SMDSEntity_Triangle) &&
855 (tr2->GetEntityType() == SMDSEntity_Triangle)) {
857 // 1 +--+ A tr1: ( 1 A B ) A->2 ( 1 2 B ) 1 +--+ A
858 // | /| tr2: ( B A 2 ) B->1 ( 1 A 2 ) |\ |
862 // put nodes in array
863 // and find indices of 1,2 and of A in tr1 and of B in tr2
864 int i, iA1 = 0, i1 = 0;
865 const SMDS_MeshNode* aNodes1 [3];
866 SMDS_ElemIteratorPtr it;
867 for (i = 0, it = tr1->nodesIterator(); it->more(); i++ ) {
868 aNodes1[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
869 if ( aNodes1[ i ] == theNode1 )
870 iA1 = i; // node A in tr1
871 else if ( aNodes1[ i ] != theNode2 )
875 const SMDS_MeshNode* aNodes2 [3];
876 for (i = 0, it = tr2->nodesIterator(); it->more(); i++ ) {
877 aNodes2[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
878 if ( aNodes2[ i ] == theNode2 )
879 iB2 = i; // node B in tr2
880 else if ( aNodes2[ i ] != theNode1 )
884 // nodes 1 and 2 should not be the same
885 if ( aNodes1[ i1 ] == aNodes2[ i2 ] )
889 aNodes1[ iA1 ] = aNodes2[ i2 ];
891 aNodes2[ iB2 ] = aNodes1[ i1 ];
893 GetMeshDS()->ChangeElementNodes( tr1, aNodes1, 3 );
894 GetMeshDS()->ChangeElementNodes( tr2, aNodes2, 3 );
899 // check case of quadratic faces
900 return InverseDiag(tr1,tr2);
903 //=======================================================================
904 //function : getQuadrangleNodes
905 //purpose : fill theQuadNodes - nodes of a quadrangle resulting from
906 // fusion of triangles tr1 and tr2 having shared link on
907 // theNode1 and theNode2
908 //=======================================================================
910 bool getQuadrangleNodes(const SMDS_MeshNode * theQuadNodes [],
911 const SMDS_MeshNode * theNode1,
912 const SMDS_MeshNode * theNode2,
913 const SMDS_MeshElement * tr1,
914 const SMDS_MeshElement * tr2 )
916 if( tr1->NbNodes() != tr2->NbNodes() )
918 // find the 4-th node to insert into tr1
919 const SMDS_MeshNode* n4 = 0;
920 SMDS_ElemIteratorPtr it = tr2->nodesIterator();
922 while ( !n4 && i<3 ) {
923 const SMDS_MeshNode * n = cast2Node( it->next() );
925 bool isDiag = ( n == theNode1 || n == theNode2 );
929 // Make an array of nodes to be in a quadrangle
930 int iNode = 0, iFirstDiag = -1;
931 it = tr1->nodesIterator();
934 const SMDS_MeshNode * n = cast2Node( it->next() );
936 bool isDiag = ( n == theNode1 || n == theNode2 );
938 if ( iFirstDiag < 0 )
940 else if ( iNode - iFirstDiag == 1 )
941 theQuadNodes[ iNode++ ] = n4; // insert the 4-th node between diagonal nodes
943 else if ( n == n4 ) {
944 return false; // tr1 and tr2 should not have all the same nodes
946 theQuadNodes[ iNode++ ] = n;
948 if ( iNode == 3 ) // diagonal nodes have 0 and 2 indices
949 theQuadNodes[ iNode ] = n4;
954 //=======================================================================
955 //function : DeleteDiag
956 //purpose : Replace two neighbour triangles sharing theNode1-theNode2 link
957 // with a quadrangle built on the same 4 nodes.
958 // Return false if proper faces not found
959 //=======================================================================
961 bool SMESH_MeshEditor::DeleteDiag (const SMDS_MeshNode * theNode1,
962 const SMDS_MeshNode * theNode2)
964 myLastCreatedElems.Clear();
965 myLastCreatedNodes.Clear();
967 MESSAGE( "::DeleteDiag()" );
969 const SMDS_MeshElement *tr1, *tr2;
970 if ( !findTriangles( theNode1, theNode2, tr1, tr2 ))
973 const SMDS_VtkFace* F1 = dynamic_cast<const SMDS_VtkFace*>( tr1 );
974 if (!F1) return false;
975 const SMDS_VtkFace* F2 = dynamic_cast<const SMDS_VtkFace*>( tr2 );
976 if (!F2) return false;
977 SMESHDS_Mesh * aMesh = GetMeshDS();
979 if ((tr1->GetEntityType() == SMDSEntity_Triangle) &&
980 (tr2->GetEntityType() == SMDSEntity_Triangle)) {
982 const SMDS_MeshNode* aNodes [ 4 ];
983 if ( ! getQuadrangleNodes( aNodes, theNode1, theNode2, tr1, tr2 ))
986 const SMDS_MeshElement* newElem = 0;
987 newElem = aMesh->AddFace( aNodes[0], aNodes[1], aNodes[2], aNodes[3] );
988 myLastCreatedElems.Append(newElem);
989 AddToSameGroups( newElem, tr1, aMesh );
990 int aShapeId = tr1->getshapeId();
993 aMesh->SetMeshElementOnShape( newElem, aShapeId );
995 aMesh->RemoveElement( tr1 );
996 aMesh->RemoveElement( tr2 );
1001 // check case of quadratic faces
1002 if (tr1->GetEntityType() != SMDSEntity_Quad_Triangle)
1004 if (tr2->GetEntityType() != SMDSEntity_Quad_Triangle)
1008 // 1 +--+--+ 2 tr1: (1 2 4 5 9 7) or (2 4 1 9 7 5) or (4 1 2 7 5 9)
1009 // | /| tr2: (2 3 4 6 8 9) or (3 4 2 8 9 6) or (4 2 3 9 6 8)
1017 vector< const SMDS_MeshNode* > N1;
1018 vector< const SMDS_MeshNode* > N2;
1019 if(!getNodesFromTwoTria(tr1,tr2,N1,N2))
1021 // now we receive following N1 and N2 (using numeration as above image)
1022 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
1023 // i.e. first nodes from both arrays determ new diagonal
1025 const SMDS_MeshNode* aNodes[8];
1035 const SMDS_MeshElement* newElem = 0;
1036 newElem = aMesh->AddFace( aNodes[0], aNodes[1], aNodes[2], aNodes[3],
1037 aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
1038 myLastCreatedElems.Append(newElem);
1039 AddToSameGroups( newElem, tr1, aMesh );
1040 int aShapeId = tr1->getshapeId();
1043 aMesh->SetMeshElementOnShape( newElem, aShapeId );
1045 aMesh->RemoveElement( tr1 );
1046 aMesh->RemoveElement( tr2 );
1048 // remove middle node (9)
1049 GetMeshDS()->RemoveNode( N1[4] );
1054 //=======================================================================
1055 //function : Reorient
1056 //purpose : Reverse theElement orientation
1057 //=======================================================================
1059 bool SMESH_MeshEditor::Reorient (const SMDS_MeshElement * theElem)
1061 MESSAGE("Reorient");
1062 myLastCreatedElems.Clear();
1063 myLastCreatedNodes.Clear();
1067 SMDS_ElemIteratorPtr it = theElem->nodesIterator();
1068 if ( !it || !it->more() )
1071 const SMDSAbs_ElementType type = theElem->GetType();
1072 if ( type < SMDSAbs_Edge || type > SMDSAbs_Volume )
1075 const SMDSAbs_EntityType geomType = theElem->GetEntityType();
1076 if ( geomType == SMDSEntity_Polyhedra ) // polyhedron
1078 const SMDS_VtkVolume* aPolyedre =
1079 dynamic_cast<const SMDS_VtkVolume*>( theElem );
1081 MESSAGE("Warning: bad volumic element");
1084 const int nbFaces = aPolyedre->NbFaces();
1085 vector<const SMDS_MeshNode *> poly_nodes;
1086 vector<int> quantities (nbFaces);
1088 // reverse each face of the polyedre
1089 for (int iface = 1; iface <= nbFaces; iface++) {
1090 int inode, nbFaceNodes = aPolyedre->NbFaceNodes(iface);
1091 quantities[iface - 1] = nbFaceNodes;
1093 for (inode = nbFaceNodes; inode >= 1; inode--) {
1094 const SMDS_MeshNode* curNode = aPolyedre->GetFaceNode(iface, inode);
1095 poly_nodes.push_back(curNode);
1098 return GetMeshDS()->ChangePolyhedronNodes( theElem, poly_nodes, quantities );
1100 else // other elements
1102 vector<const SMDS_MeshNode*> nodes( theElem->begin_nodes(), theElem->end_nodes() );
1103 const std::vector<int>& interlace = SMDS_MeshCell::reverseSmdsOrder( geomType );
1104 if ( interlace.empty() )
1106 std::reverse( nodes.begin(), nodes.end() ); // polygon
1108 else if ( interlace.size() > 1 )
1110 SMDS_MeshCell::applyInterlace( interlace, nodes );
1112 return GetMeshDS()->ChangeElementNodes( theElem, &nodes[0], nodes.size() );
1117 //================================================================================
1119 * \brief Reorient faces.
1120 * \param theFaces - the faces to reorient. If empty the whole mesh is meant
1121 * \param theDirection - desired direction of normal of \a theFace
1122 * \param theFace - one of \a theFaces that sould be oriented according to
1123 * \a theDirection and whose orientation defines orientation of other faces
1124 * \return number of reoriented faces.
1126 //================================================================================
1128 int SMESH_MeshEditor::Reorient2D (TIDSortedElemSet & theFaces,
1129 const gp_Dir& theDirection,
1130 const SMDS_MeshElement * theFace)
1133 if ( !theFace || theFace->GetType() != SMDSAbs_Face ) return nbReori;
1135 if ( theFaces.empty() )
1137 SMDS_FaceIteratorPtr fIt = GetMeshDS()->facesIterator(/*idInceasingOrder=*/true);
1138 while ( fIt->more() )
1139 theFaces.insert( theFaces.end(), fIt->next() );
1142 // orient theFace according to theDirection
1144 SMESH_MeshAlgos::FaceNormal( theFace, normal, /*normalized=*/false );
1145 if ( normal * theDirection.XYZ() < 0 )
1146 nbReori += Reorient( theFace );
1148 // Orient other faces
1150 set< const SMDS_MeshElement* > startFaces, visitedFaces;
1151 TIDSortedElemSet avoidSet;
1152 set< SMESH_TLink > checkedLinks;
1153 pair< set< SMESH_TLink >::iterator, bool > linkIt_isNew;
1155 if ( theFaces.size() > 1 )// leave 1 face to prevent finding not selected faces
1156 theFaces.erase( theFace );
1157 startFaces.insert( theFace );
1159 int nodeInd1, nodeInd2;
1160 const SMDS_MeshElement* otherFace;
1161 vector< const SMDS_MeshElement* > facesNearLink;
1162 vector< std::pair< int, int > > nodeIndsOfFace;
1164 set< const SMDS_MeshElement* >::iterator startFace = startFaces.begin();
1165 while ( !startFaces.empty() )
1167 startFace = startFaces.begin();
1168 theFace = *startFace;
1169 startFaces.erase( startFace );
1170 if ( !visitedFaces.insert( theFace ).second )
1174 avoidSet.insert(theFace);
1176 NLink link( theFace->GetNode( 0 ), (SMDS_MeshNode *) 0 );
1178 const int nbNodes = theFace->NbCornerNodes();
1179 for ( int i = 0; i < nbNodes; ++i ) // loop on links of theFace
1181 link.second = theFace->GetNode(( i+1 ) % nbNodes );
1182 linkIt_isNew = checkedLinks.insert( link );
1183 if ( !linkIt_isNew.second )
1185 // link has already been checked and won't be encountered more
1186 // if the group (theFaces) is manifold
1187 //checkedLinks.erase( linkIt_isNew.first );
1191 facesNearLink.clear();
1192 nodeIndsOfFace.clear();
1193 while (( otherFace = SMESH_MeshAlgos::FindFaceInSet( link.first, link.second,
1195 &nodeInd1, &nodeInd2 )))
1196 if ( otherFace != theFace)
1198 facesNearLink.push_back( otherFace );
1199 nodeIndsOfFace.push_back( make_pair( nodeInd1, nodeInd2 ));
1200 avoidSet.insert( otherFace );
1202 if ( facesNearLink.size() > 1 )
1204 // NON-MANIFOLD mesh shell !
1205 // select a face most co-directed with theFace,
1206 // other faces won't be visited this time
1208 SMESH_MeshAlgos::FaceNormal( theFace, NF, /*normalized=*/false );
1209 double proj, maxProj = -1;
1210 for ( size_t i = 0; i < facesNearLink.size(); ++i ) {
1211 SMESH_MeshAlgos::FaceNormal( facesNearLink[i], NOF, /*normalized=*/false );
1212 if (( proj = Abs( NF * NOF )) > maxProj ) {
1214 otherFace = facesNearLink[i];
1215 nodeInd1 = nodeIndsOfFace[i].first;
1216 nodeInd2 = nodeIndsOfFace[i].second;
1219 // not to visit rejected faces
1220 for ( size_t i = 0; i < facesNearLink.size(); ++i )
1221 if ( facesNearLink[i] != otherFace && theFaces.size() > 1 )
1222 visitedFaces.insert( facesNearLink[i] );
1224 else if ( facesNearLink.size() == 1 )
1226 otherFace = facesNearLink[0];
1227 nodeInd1 = nodeIndsOfFace.back().first;
1228 nodeInd2 = nodeIndsOfFace.back().second;
1230 if ( otherFace && otherFace != theFace)
1232 // link must be reverse in otherFace if orientation ot otherFace
1233 // is same as that of theFace
1234 if ( abs(nodeInd2-nodeInd1) == 1 ? nodeInd2 > nodeInd1 : nodeInd1 > nodeInd2 )
1236 nbReori += Reorient( otherFace );
1238 startFaces.insert( otherFace );
1241 std::swap( link.first, link.second ); // reverse the link
1247 //================================================================================
1249 * \brief Reorient faces basing on orientation of adjacent volumes.
1250 * \param theFaces - faces to reorient. If empty, all mesh faces are treated.
1251 * \param theVolumes - reference volumes.
1252 * \param theOutsideNormal - to orient faces to have their normal
1253 * pointing either \a outside or \a inside the adjacent volumes.
1254 * \return number of reoriented faces.
1256 //================================================================================
1258 int SMESH_MeshEditor::Reorient2DBy3D (TIDSortedElemSet & theFaces,
1259 TIDSortedElemSet & theVolumes,
1260 const bool theOutsideNormal)
1264 SMDS_ElemIteratorPtr faceIt;
1265 if ( theFaces.empty() )
1266 faceIt = GetMeshDS()->elementsIterator( SMDSAbs_Face );
1268 faceIt = elemSetIterator( theFaces );
1270 vector< const SMDS_MeshNode* > faceNodes;
1271 TIDSortedElemSet checkedVolumes;
1272 set< const SMDS_MeshNode* > faceNodesSet;
1273 SMDS_VolumeTool volumeTool;
1275 while ( faceIt->more() ) // loop on given faces
1277 const SMDS_MeshElement* face = faceIt->next();
1278 if ( face->GetType() != SMDSAbs_Face )
1281 const int nbCornersNodes = face->NbCornerNodes();
1282 faceNodes.assign( face->begin_nodes(), face->end_nodes() );
1284 checkedVolumes.clear();
1285 SMDS_ElemIteratorPtr vIt = faceNodes[ 0 ]->GetInverseElementIterator( SMDSAbs_Volume );
1286 while ( vIt->more() )
1288 const SMDS_MeshElement* volume = vIt->next();
1290 if ( !checkedVolumes.insert( volume ).second )
1292 if ( !theVolumes.empty() && !theVolumes.count( volume ))
1295 // is volume adjacent?
1296 bool allNodesCommon = true;
1297 for ( int iN = 1; iN < nbCornersNodes && allNodesCommon; ++iN )
1298 allNodesCommon = ( volume->GetNodeIndex( faceNodes[ iN ]) > -1 );
1299 if ( !allNodesCommon )
1302 // get nodes of a corresponding volume facet
1303 faceNodesSet.clear();
1304 faceNodesSet.insert( faceNodes.begin(), faceNodes.end() );
1305 volumeTool.Set( volume );
1306 int facetID = volumeTool.GetFaceIndex( faceNodesSet );
1307 if ( facetID < 0 ) continue;
1308 volumeTool.SetExternalNormal();
1309 const SMDS_MeshNode** facetNodes = volumeTool.GetFaceNodes( facetID );
1311 // compare order of faceNodes and facetNodes
1312 const int iQ = 1 + ( nbCornersNodes < faceNodes.size() );
1314 for ( int i = 0; i < 2; ++i )
1316 const SMDS_MeshNode* n = facetNodes[ i*iQ ];
1317 for ( int iN = 0; iN < nbCornersNodes; ++iN )
1318 if ( faceNodes[ iN ] == n )
1324 bool isOutside = Abs( iNN[0]-iNN[1] ) == 1 ? iNN[0] < iNN[1] : iNN[0] > iNN[1];
1325 if ( isOutside != theOutsideNormal )
1326 nbReori += Reorient( face );
1328 } // loop on given faces
1333 //=======================================================================
1334 //function : getBadRate
1336 //=======================================================================
1338 static double getBadRate (const SMDS_MeshElement* theElem,
1339 SMESH::Controls::NumericalFunctorPtr& theCrit)
1341 SMESH::Controls::TSequenceOfXYZ P;
1342 if ( !theElem || !theCrit->GetPoints( theElem, P ))
1344 return theCrit->GetBadRate( theCrit->GetValue( P ), theElem->NbNodes() );
1345 //return theCrit->GetBadRate( theCrit->GetValue( theElem->GetID() ), theElem->NbNodes() );
1348 //=======================================================================
1349 //function : QuadToTri
1350 //purpose : Cut quadrangles into triangles.
1351 // theCrit is used to select a diagonal to cut
1352 //=======================================================================
1354 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet & theElems,
1355 SMESH::Controls::NumericalFunctorPtr theCrit)
1357 myLastCreatedElems.Clear();
1358 myLastCreatedNodes.Clear();
1360 if ( !theCrit.get() )
1363 SMESHDS_Mesh * aMesh = GetMeshDS();
1365 Handle(Geom_Surface) surface;
1366 SMESH_MesherHelper helper( *GetMesh() );
1368 TIDSortedElemSet::iterator itElem;
1369 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
1371 const SMDS_MeshElement* elem = *itElem;
1372 if ( !elem || elem->GetType() != SMDSAbs_Face )
1374 if ( elem->NbCornerNodes() != 4 )
1377 // retrieve element nodes
1378 vector< const SMDS_MeshNode* > aNodes( elem->begin_nodes(), elem->end_nodes() );
1380 // compare two sets of possible triangles
1381 double aBadRate1, aBadRate2; // to what extent a set is bad
1382 SMDS_FaceOfNodes tr1 ( aNodes[0], aNodes[1], aNodes[2] );
1383 SMDS_FaceOfNodes tr2 ( aNodes[2], aNodes[3], aNodes[0] );
1384 aBadRate1 = getBadRate( &tr1, theCrit ) + getBadRate( &tr2, theCrit );
1386 SMDS_FaceOfNodes tr3 ( aNodes[1], aNodes[2], aNodes[3] );
1387 SMDS_FaceOfNodes tr4 ( aNodes[3], aNodes[0], aNodes[1] );
1388 aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit );
1390 const int aShapeId = FindShape( elem );
1391 const SMDS_MeshElement* newElem1 = 0;
1392 const SMDS_MeshElement* newElem2 = 0;
1394 if ( !elem->IsQuadratic() ) // split liner quadrangle
1396 // for MaxElementLength2D functor we return minimum diagonal for splitting,
1397 // because aBadRate1=2*len(diagonal 1-3); aBadRate2=2*len(diagonal 2-4)
1398 if ( aBadRate1 <= aBadRate2 ) {
1399 // tr1 + tr2 is better
1400 newElem1 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
1401 newElem2 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
1404 // tr3 + tr4 is better
1405 newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
1406 newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
1409 else // split quadratic quadrangle
1411 helper.SetIsQuadratic( true );
1412 helper.SetIsBiQuadratic( aNodes.size() == 9 );
1414 helper.AddTLinks( static_cast< const SMDS_MeshFace* >( elem ));
1415 if ( aNodes.size() == 9 )
1417 helper.SetIsBiQuadratic( true );
1418 if ( aBadRate1 <= aBadRate2 )
1419 helper.AddTLinkNode( aNodes[0], aNodes[2], aNodes[8] );
1421 helper.AddTLinkNode( aNodes[1], aNodes[3], aNodes[8] );
1423 // create a new element
1424 if ( aBadRate1 <= aBadRate2 ) {
1425 newElem1 = helper.AddFace( aNodes[2], aNodes[3], aNodes[0] );
1426 newElem2 = helper.AddFace( aNodes[2], aNodes[0], aNodes[1] );
1429 newElem1 = helper.AddFace( aNodes[3], aNodes[0], aNodes[1] );
1430 newElem2 = helper.AddFace( aNodes[3], aNodes[1], aNodes[2] );
1434 // care of a new element
1436 myLastCreatedElems.Append(newElem1);
1437 myLastCreatedElems.Append(newElem2);
1438 AddToSameGroups( newElem1, elem, aMesh );
1439 AddToSameGroups( newElem2, elem, aMesh );
1441 // put a new triangle on the same shape
1443 aMesh->SetMeshElementOnShape( newElem1, aShapeId );
1444 aMesh->SetMeshElementOnShape( newElem2, aShapeId );
1446 aMesh->RemoveElement( elem );
1451 //=======================================================================
1453 * \brief Split each of given quadrangles into 4 triangles.
1454 * \param theElems - The faces to be splitted. If empty all faces are split.
1456 //=======================================================================
1458 void SMESH_MeshEditor::QuadTo4Tri (TIDSortedElemSet & theElems)
1460 myLastCreatedElems.Clear();
1461 myLastCreatedNodes.Clear();
1463 SMESH_MesherHelper helper( *GetMesh() );
1464 helper.SetElementsOnShape( true );
1466 SMDS_ElemIteratorPtr faceIt;
1467 if ( theElems.empty() ) faceIt = GetMeshDS()->elementsIterator(SMDSAbs_Face);
1468 else faceIt = elemSetIterator( theElems );
1471 gp_XY uv [9]; uv[8] = gp_XY(0,0);
1473 vector< const SMDS_MeshNode* > nodes;
1474 SMESHDS_SubMesh* subMeshDS;
1476 Handle(Geom_Surface) surface;
1477 TopLoc_Location loc;
1479 while ( faceIt->more() )
1481 const SMDS_MeshElement* quad = faceIt->next();
1482 if ( !quad || quad->NbCornerNodes() != 4 )
1485 // get a surface the quad is on
1487 if ( quad->getshapeId() < 1 )
1490 helper.SetSubShape( 0 );
1493 else if ( quad->getshapeId() != helper.GetSubShapeID() )
1495 helper.SetSubShape( quad->getshapeId() );
1496 if ( !helper.GetSubShape().IsNull() &&
1497 helper.GetSubShape().ShapeType() == TopAbs_FACE )
1499 F = TopoDS::Face( helper.GetSubShape() );
1500 surface = BRep_Tool::Surface( F, loc );
1501 subMeshDS = GetMeshDS()->MeshElements( quad->getshapeId() );
1505 helper.SetSubShape( 0 );
1510 // create a central node
1512 const SMDS_MeshNode* nCentral;
1513 nodes.assign( quad->begin_nodes(), quad->end_nodes() );
1515 if ( nodes.size() == 9 )
1517 nCentral = nodes.back();
1524 for ( ; iN < nodes.size(); ++iN )
1525 xyz[ iN ] = SMESH_TNodeXYZ( nodes[ iN ] );
1527 for ( ; iN < 8; ++iN ) // mid-side points of a linear qudrangle
1528 xyz[ iN ] = 0.5 * ( xyz[ iN - 4 ] + xyz[( iN - 3 )%4 ] );
1530 xyz[ 8 ] = helper.calcTFI( 0.5, 0.5,
1531 xyz[0], xyz[1], xyz[2], xyz[3],
1532 xyz[4], xyz[5], xyz[6], xyz[7] );
1536 for ( ; iN < nodes.size(); ++iN )
1537 uv[ iN ] = helper.GetNodeUV( F, nodes[iN], nodes[(iN+2)%4], &checkUV );
1539 for ( ; iN < 8; ++iN ) // UV of mid-side points of a linear qudrangle
1540 uv[ iN ] = helper.GetMiddleUV( surface, uv[ iN - 4 ], uv[( iN - 3 )%4 ] );
1542 uv[ 8 ] = helper.calcTFI( 0.5, 0.5,
1543 uv[0], uv[1], uv[2], uv[3],
1544 uv[4], uv[5], uv[6], uv[7] );
1546 gp_Pnt p = surface->Value( uv[8].X(), uv[8].Y() ).Transformed( loc );
1550 nCentral = helper.AddNode( xyz[8].X(), xyz[8].Y(), xyz[8].Z(), /*id=*/0,
1551 uv[8].X(), uv[8].Y() );
1552 myLastCreatedNodes.Append( nCentral );
1555 // create 4 triangles
1557 GetMeshDS()->RemoveFreeElement( quad, subMeshDS, /*fromGroups=*/false );
1559 helper.SetIsQuadratic ( nodes.size() > 4 );
1560 helper.SetIsBiQuadratic( nodes.size() == 9 );
1561 if ( helper.GetIsQuadratic() )
1562 helper.AddTLinks( static_cast< const SMDS_MeshFace*>( quad ));
1564 for ( int i = 0; i < 4; ++i )
1566 SMDS_MeshElement* tria = helper.AddFace( nodes[ i ],
1569 ReplaceElemInGroups( tria, quad, GetMeshDS() );
1570 myLastCreatedElems.Append( tria );
1575 //=======================================================================
1576 //function : BestSplit
1577 //purpose : Find better diagonal for cutting.
1578 //=======================================================================
1580 int SMESH_MeshEditor::BestSplit (const SMDS_MeshElement* theQuad,
1581 SMESH::Controls::NumericalFunctorPtr theCrit)
1583 myLastCreatedElems.Clear();
1584 myLastCreatedNodes.Clear();
1589 if (!theQuad || theQuad->GetType() != SMDSAbs_Face )
1592 if( theQuad->NbNodes()==4 ||
1593 (theQuad->NbNodes()==8 && theQuad->IsQuadratic()) ) {
1595 // retrieve element nodes
1596 const SMDS_MeshNode* aNodes [4];
1597 SMDS_ElemIteratorPtr itN = theQuad->nodesIterator();
1599 //while (itN->more())
1601 aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
1603 // compare two sets of possible triangles
1604 double aBadRate1, aBadRate2; // to what extent a set is bad
1605 SMDS_FaceOfNodes tr1 ( aNodes[0], aNodes[1], aNodes[2] );
1606 SMDS_FaceOfNodes tr2 ( aNodes[2], aNodes[3], aNodes[0] );
1607 aBadRate1 = getBadRate( &tr1, theCrit ) + getBadRate( &tr2, theCrit );
1609 SMDS_FaceOfNodes tr3 ( aNodes[1], aNodes[2], aNodes[3] );
1610 SMDS_FaceOfNodes tr4 ( aNodes[3], aNodes[0], aNodes[1] );
1611 aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit );
1612 // for MaxElementLength2D functor we return minimum diagonal for splitting,
1613 // because aBadRate1=2*len(diagonal 1-3); aBadRate2=2*len(diagonal 2-4)
1614 if (aBadRate1 <= aBadRate2) // tr1 + tr2 is better
1615 return 1; // diagonal 1-3
1617 return 2; // diagonal 2-4
1624 // Methods of splitting volumes into tetra
1626 const int theHexTo5_1[5*4+1] =
1628 0, 1, 2, 5, 0, 4, 5, 7, 0, 2, 3, 7, 2, 5, 6, 7, 0, 5, 2, 7, -1
1630 const int theHexTo5_2[5*4+1] =
1632 1, 2, 3, 6, 1, 4, 5, 6, 0, 1, 3, 4, 3, 4, 6, 7, 1, 3, 4, 6, -1
1634 const int* theHexTo5[2] = { theHexTo5_1, theHexTo5_2 };
1636 const int theHexTo6_1[6*4+1] =
1638 1, 5, 6, 0, 0, 1, 2, 6, 0, 4, 5, 6, 0, 4, 6, 7, 0, 2, 3, 6, 0, 3, 7, 6, -1
1640 const int theHexTo6_2[6*4+1] =
1642 2, 6, 7, 1, 1, 2, 3, 7, 1, 5, 6, 7, 1, 5, 7, 4, 1, 3, 0, 7, 1, 0, 4, 7, -1
1644 const int theHexTo6_3[6*4+1] =
1646 3, 7, 4, 2, 2, 3, 0, 4, 2, 6, 7, 4, 2, 6, 4, 5, 2, 0, 1, 4, 2, 1, 5, 4, -1
1648 const int theHexTo6_4[6*4+1] =
1650 0, 4, 5, 3, 3, 0, 1, 5, 3, 7, 4, 5, 3, 7, 5, 6, 3, 1, 2, 5, 3, 2, 6, 5, -1
1652 const int* theHexTo6[4] = { theHexTo6_1, theHexTo6_2, theHexTo6_3, theHexTo6_4 };
1654 const int thePyraTo2_1[2*4+1] =
1656 0, 1, 2, 4, 0, 2, 3, 4, -1
1658 const int thePyraTo2_2[2*4+1] =
1660 1, 2, 3, 4, 1, 3, 0, 4, -1
1662 const int* thePyraTo2[2] = { thePyraTo2_1, thePyraTo2_2 };
1664 const int thePentaTo3_1[3*4+1] =
1666 0, 1, 2, 3, 1, 3, 4, 2, 2, 3, 4, 5, -1
1668 const int thePentaTo3_2[3*4+1] =
1670 1, 2, 0, 4, 2, 4, 5, 0, 0, 4, 5, 3, -1
1672 const int thePentaTo3_3[3*4+1] =
1674 2, 0, 1, 5, 0, 5, 3, 1, 1, 5, 3, 4, -1
1676 const int thePentaTo3_4[3*4+1] =
1678 0, 1, 2, 3, 1, 3, 4, 5, 2, 3, 1, 5, -1
1680 const int thePentaTo3_5[3*4+1] =
1682 1, 2, 0, 4, 2, 4, 5, 3, 0, 4, 2, 3, -1
1684 const int thePentaTo3_6[3*4+1] =
1686 2, 0, 1, 5, 0, 5, 3, 4, 1, 5, 0, 4, -1
1688 const int* thePentaTo3[6] = { thePentaTo3_1, thePentaTo3_2, thePentaTo3_3,
1689 thePentaTo3_4, thePentaTo3_5, thePentaTo3_6 };
1691 // Methods of splitting hexahedron into prisms
1693 const int theHexTo4Prisms_BT[6*4+1] = // bottom-top
1695 0, 1, 8, 4, 5, 9, 1, 2, 8, 5, 6, 9, 2, 3, 8, 6, 7, 9, 3, 0, 8, 7, 4, 9, -1
1697 const int theHexTo4Prisms_LR[6*4+1] = // left-right
1699 1, 0, 8, 2, 3, 9, 0, 4, 8, 3, 7, 9, 4, 5, 8, 7, 6, 9, 5, 1, 8, 6, 2, 9, -1
1701 const int theHexTo4Prisms_FB[6*4+1] = // front-back
1703 0, 3, 9, 1, 2, 8, 3, 7, 9, 2, 6, 8, 7, 4, 9, 6, 5, 8, 4, 0, 9, 5, 1, 8, -1
1706 const int theHexTo2Prisms_BT_1[6*2+1] =
1708 0, 1, 3, 4, 5, 7, 1, 2, 3, 5, 6, 7, -1
1710 const int theHexTo2Prisms_BT_2[6*2+1] =
1712 0, 1, 2, 4, 5, 6, 0, 2, 3, 4, 6, 7, -1
1714 const int* theHexTo2Prisms_BT[2] = { theHexTo2Prisms_BT_1, theHexTo2Prisms_BT_2 };
1716 const int theHexTo2Prisms_LR_1[6*2+1] =
1718 1, 0, 4, 2, 3, 7, 1, 4, 5, 2, 7, 6, -1
1720 const int theHexTo2Prisms_LR_2[6*2+1] =
1722 1, 0, 4, 2, 3, 7, 1, 4, 5, 2, 7, 6, -1
1724 const int* theHexTo2Prisms_LR[2] = { theHexTo2Prisms_LR_1, theHexTo2Prisms_LR_2 };
1726 const int theHexTo2Prisms_FB_1[6*2+1] =
1728 0, 3, 4, 1, 2, 5, 3, 7, 4, 2, 6, 5, -1
1730 const int theHexTo2Prisms_FB_2[6*2+1] =
1732 0, 3, 7, 1, 2, 7, 0, 7, 4, 1, 6, 5, -1
1734 const int* theHexTo2Prisms_FB[2] = { theHexTo2Prisms_FB_1, theHexTo2Prisms_FB_2 };
1737 struct TTriangleFacet //!< stores indices of three nodes of tetra facet
1740 TTriangleFacet(int n1, int n2, int n3): _n1(n1), _n2(n2), _n3(n3) {}
1741 bool contains(int n) const { return ( n == _n1 || n == _n2 || n == _n3 ); }
1742 bool hasAdjacentVol( const SMDS_MeshElement* elem,
1743 const SMDSAbs_GeometryType geom = SMDSGeom_TETRA) const;
1749 const int* _connectivity; //!< foursomes of tetra connectivy finished by -1
1750 bool _baryNode; //!< additional node is to be created at cell barycenter
1751 bool _ownConn; //!< to delete _connectivity in destructor
1752 map<int, const SMDS_MeshNode*> _faceBaryNode; //!< map face index to node at BC of face
1754 TSplitMethod( int nbTet=0, const int* conn=0, bool addNode=false)
1755 : _nbSplits(nbTet), _nbCorners(4), _connectivity(conn), _baryNode(addNode), _ownConn(false) {}
1756 ~TSplitMethod() { if ( _ownConn ) delete [] _connectivity; _connectivity = 0; }
1757 bool hasFacet( const TTriangleFacet& facet ) const
1759 if ( _nbCorners == 4 )
1761 const int* tetConn = _connectivity;
1762 for ( ; tetConn[0] >= 0; tetConn += 4 )
1763 if (( facet.contains( tetConn[0] ) +
1764 facet.contains( tetConn[1] ) +
1765 facet.contains( tetConn[2] ) +
1766 facet.contains( tetConn[3] )) == 3 )
1769 else // prism, _nbCorners == 6
1771 const int* prismConn = _connectivity;
1772 for ( ; prismConn[0] >= 0; prismConn += 6 )
1774 if (( facet.contains( prismConn[0] ) &&
1775 facet.contains( prismConn[1] ) &&
1776 facet.contains( prismConn[2] ))
1778 ( facet.contains( prismConn[3] ) &&
1779 facet.contains( prismConn[4] ) &&
1780 facet.contains( prismConn[5] )))
1788 //=======================================================================
1790 * \brief return TSplitMethod for the given element to split into tetrahedra
1792 //=======================================================================
1794 TSplitMethod getTetraSplitMethod( SMDS_VolumeTool& vol, const int theMethodFlags)
1796 const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
1798 // at HEXA_TO_24 method, each face of volume is split into triangles each based on
1799 // an edge and a face barycenter; tertaherdons are based on triangles and
1800 // a volume barycenter
1801 const bool is24TetMode = ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_24 );
1803 // Find out how adjacent volumes are split
1805 vector < list< TTriangleFacet > > triaSplitsByFace( vol.NbFaces() ); // splits of each side
1806 int hasAdjacentSplits = 0, maxTetConnSize = 0;
1807 for ( int iF = 0; iF < vol.NbFaces(); ++iF )
1809 int nbNodes = vol.NbFaceNodes( iF ) / iQ;
1810 maxTetConnSize += 4 * ( nbNodes - (is24TetMode ? 0 : 2));
1811 if ( nbNodes < 4 ) continue;
1813 list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
1814 const int* nInd = vol.GetFaceNodesIndices( iF );
1817 TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
1818 TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
1819 if ( t012.hasAdjacentVol( vol.Element() )) triaSplits.push_back( t012 );
1820 else if ( t123.hasAdjacentVol( vol.Element() )) triaSplits.push_back( t123 );
1824 int iCom = 0; // common node of triangle faces to split into
1825 for ( int iVar = 0; iVar < nbNodes; ++iVar, ++iCom )
1827 TTriangleFacet t012( nInd[ iQ * ( iCom )],
1828 nInd[ iQ * ( (iCom+1)%nbNodes )],
1829 nInd[ iQ * ( (iCom+2)%nbNodes )]);
1830 TTriangleFacet t023( nInd[ iQ * ( iCom )],
1831 nInd[ iQ * ( (iCom+2)%nbNodes )],
1832 nInd[ iQ * ( (iCom+3)%nbNodes )]);
1833 if ( t012.hasAdjacentVol( vol.Element() ) && t023.hasAdjacentVol( vol.Element() ))
1835 triaSplits.push_back( t012 );
1836 triaSplits.push_back( t023 );
1841 if ( !triaSplits.empty() )
1842 hasAdjacentSplits = true;
1845 // Among variants of split method select one compliant with adjacent volumes
1847 TSplitMethod method;
1848 if ( !vol.Element()->IsPoly() && !is24TetMode )
1850 int nbVariants = 2, nbTet = 0;
1851 const int** connVariants = 0;
1852 switch ( vol.Element()->GetEntityType() )
1854 case SMDSEntity_Hexa:
1855 case SMDSEntity_Quad_Hexa:
1856 case SMDSEntity_TriQuad_Hexa:
1857 if ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_5 )
1858 connVariants = theHexTo5, nbTet = 5;
1860 connVariants = theHexTo6, nbTet = 6, nbVariants = 4;
1862 case SMDSEntity_Pyramid:
1863 case SMDSEntity_Quad_Pyramid:
1864 connVariants = thePyraTo2; nbTet = 2;
1866 case SMDSEntity_Penta:
1867 case SMDSEntity_Quad_Penta:
1868 connVariants = thePentaTo3; nbTet = 3; nbVariants = 6;
1873 for ( int variant = 0; variant < nbVariants && method._nbSplits == 0; ++variant )
1875 // check method compliancy with adjacent tetras,
1876 // all found splits must be among facets of tetras described by this method
1877 method = TSplitMethod( nbTet, connVariants[variant] );
1878 if ( hasAdjacentSplits && method._nbSplits > 0 )
1880 bool facetCreated = true;
1881 for ( int iF = 0; facetCreated && iF < triaSplitsByFace.size(); ++iF )
1883 list< TTriangleFacet >::const_iterator facet = triaSplitsByFace[iF].begin();
1884 for ( ; facetCreated && facet != triaSplitsByFace[iF].end(); ++facet )
1885 facetCreated = method.hasFacet( *facet );
1887 if ( !facetCreated )
1888 method = TSplitMethod(0); // incompatible method
1892 if ( method._nbSplits < 1 )
1894 // No standard method is applicable, use a generic solution:
1895 // each facet of a volume is split into triangles and
1896 // each of triangles and a volume barycenter form a tetrahedron.
1898 const bool isHex27 = ( vol.Element()->GetEntityType() == SMDSEntity_TriQuad_Hexa );
1900 int* connectivity = new int[ maxTetConnSize + 1 ];
1901 method._connectivity = connectivity;
1902 method._ownConn = true;
1903 method._baryNode = !isHex27; // to create central node or not
1906 int baryCenInd = vol.NbNodes() - int( isHex27 );
1907 for ( int iF = 0; iF < vol.NbFaces(); ++iF )
1909 const int nbNodes = vol.NbFaceNodes( iF ) / iQ;
1910 const int* nInd = vol.GetFaceNodesIndices( iF );
1911 // find common node of triangle facets of tetra to create
1912 int iCommon = 0; // index in linear numeration
1913 const list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
1914 if ( !triaSplits.empty() )
1917 const TTriangleFacet* facet = &triaSplits.front();
1918 for ( ; iCommon < nbNodes-1 ; ++iCommon )
1919 if ( facet->contains( nInd[ iQ * iCommon ]) &&
1920 facet->contains( nInd[ iQ * ((iCommon+2)%nbNodes) ]))
1923 else if ( nbNodes > 3 && !is24TetMode )
1925 // find the best method of splitting into triangles by aspect ratio
1926 SMESH::Controls::NumericalFunctorPtr aspectRatio( new SMESH::Controls::AspectRatio);
1927 map< double, int > badness2iCommon;
1928 const SMDS_MeshNode** nodes = vol.GetFaceNodes( iF );
1929 int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
1930 for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCommon )
1933 for ( int iLast = iCommon+2; iLast < iCommon+nbNodes; ++iLast )
1935 SMDS_FaceOfNodes tria ( nodes[ iQ*( iCommon )],
1936 nodes[ iQ*((iLast-1)%nbNodes)],
1937 nodes[ iQ*((iLast )%nbNodes)]);
1938 badness += getBadRate( &tria, aspectRatio );
1940 badness2iCommon.insert( make_pair( badness, iCommon ));
1942 // use iCommon with lowest badness
1943 iCommon = badness2iCommon.begin()->second;
1945 if ( iCommon >= nbNodes )
1946 iCommon = 0; // something wrong
1948 // fill connectivity of tetrahedra based on a current face
1949 int nbTet = nbNodes - 2;
1950 if ( is24TetMode && nbNodes > 3 && triaSplits.empty())
1955 faceBaryCenInd = vol.GetCenterNodeIndex( iF );
1956 method._faceBaryNode[ iF ] = vol.GetNodes()[ faceBaryCenInd ];
1960 method._faceBaryNode[ iF ] = 0;
1961 faceBaryCenInd = baryCenInd + method._faceBaryNode.size();
1964 for ( int i = 0; i < nbTet; ++i )
1966 int i1 = i, i2 = (i+1) % nbNodes;
1967 if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
1968 connectivity[ connSize++ ] = nInd[ iQ * i1 ];
1969 connectivity[ connSize++ ] = nInd[ iQ * i2 ];
1970 connectivity[ connSize++ ] = faceBaryCenInd;
1971 connectivity[ connSize++ ] = baryCenInd;
1976 for ( int i = 0; i < nbTet; ++i )
1978 int i1 = (iCommon+1+i) % nbNodes, i2 = (iCommon+2+i) % nbNodes;
1979 if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
1980 connectivity[ connSize++ ] = nInd[ iQ * iCommon ];
1981 connectivity[ connSize++ ] = nInd[ iQ * i1 ];
1982 connectivity[ connSize++ ] = nInd[ iQ * i2 ];
1983 connectivity[ connSize++ ] = baryCenInd;
1986 method._nbSplits += nbTet;
1988 } // loop on volume faces
1990 connectivity[ connSize++ ] = -1;
1992 } // end of generic solution
1996 //=======================================================================
1998 * \brief return TSplitMethod to split haxhedron into prisms
2000 //=======================================================================
2002 TSplitMethod getPrismSplitMethod( SMDS_VolumeTool& vol,
2003 const int methodFlags,
2004 const int facetToSplit)
2006 // order of facets in HEX according to SMDS_VolumeTool::Hexa_F :
2008 const int iF = ( facetToSplit < 2 ) ? 0 : 1 + ( facetToSplit-2 ) % 2; // [0,1,2]
2010 if ( methodFlags == SMESH_MeshEditor::HEXA_TO_4_PRISMS )
2012 static TSplitMethod to4methods[4]; // order BT, LR, FB
2013 if ( to4methods[iF]._nbSplits == 0 )
2017 to4methods[iF]._connectivity = theHexTo4Prisms_BT;
2018 to4methods[iF]._faceBaryNode[ 0 ] = 0;
2019 to4methods[iF]._faceBaryNode[ 1 ] = 0;
2022 to4methods[iF]._connectivity = theHexTo4Prisms_LR;
2023 to4methods[iF]._faceBaryNode[ 2 ] = 0;
2024 to4methods[iF]._faceBaryNode[ 4 ] = 0;
2027 to4methods[iF]._connectivity = theHexTo4Prisms_FB;
2028 to4methods[iF]._faceBaryNode[ 3 ] = 0;
2029 to4methods[iF]._faceBaryNode[ 5 ] = 0;
2031 default: return to4methods[3];
2033 to4methods[iF]._nbSplits = 4;
2034 to4methods[iF]._nbCorners = 6;
2036 return to4methods[iF];
2038 // else if ( methodFlags == HEXA_TO_2_PRISMS )
2040 TSplitMethod method;
2042 const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
2044 const int nbVariants = 2, nbSplits = 2;
2045 const int** connVariants = 0;
2047 case 0: connVariants = theHexTo2Prisms_BT; break;
2048 case 1: connVariants = theHexTo2Prisms_LR; break;
2049 case 2: connVariants = theHexTo2Prisms_FB; break;
2050 default: return method;
2053 // look for prisms adjacent via facetToSplit and an opposite one
2054 for ( int is2nd = 0; is2nd < 2; ++is2nd )
2056 int iFacet = is2nd ? vol.GetOppFaceIndexOfHex( facetToSplit ) : facetToSplit;
2057 int nbNodes = vol.NbFaceNodes( iFacet ) / iQ;
2058 if ( nbNodes != 4 ) return method;
2060 const int* nInd = vol.GetFaceNodesIndices( iFacet );
2061 TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
2062 TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
2064 if ( t012.hasAdjacentVol( vol.Element(), SMDSGeom_PENTA ))
2066 else if ( t123.hasAdjacentVol( vol.Element(), SMDSGeom_PENTA ))
2071 // there are adjacent prism
2072 for ( int variant = 0; variant < nbVariants; ++variant )
2074 // check method compliancy with adjacent prisms,
2075 // the found prism facets must be among facets of prisms described by current method
2076 method._nbSplits = nbSplits;
2077 method._nbCorners = 6;
2078 method._connectivity = connVariants[ variant ];
2079 if ( method.hasFacet( *t ))
2084 // No adjacent prisms. Select a variant with a best aspect ratio.
2086 double badness[2] = { 0, 0 };
2087 static SMESH::Controls::NumericalFunctorPtr aspectRatio( new SMESH::Controls::AspectRatio);
2088 const SMDS_MeshNode** nodes = vol.GetNodes();
2089 for ( int variant = 0; variant < nbVariants; ++variant )
2090 for ( int is2nd = 0; is2nd < 2; ++is2nd )
2092 int iFacet = is2nd ? vol.GetOppFaceIndexOfHex( facetToSplit ) : facetToSplit;
2093 const int* nInd = vol.GetFaceNodesIndices( iFacet );
2095 method._connectivity = connVariants[ variant ];
2096 TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
2097 TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
2098 TTriangleFacet* t = ( method.hasFacet( t012 )) ? & t012 : & t123;
2100 SMDS_FaceOfNodes tria ( nodes[ t->_n1 ],
2103 badness[ variant ] += getBadRate( &tria, aspectRatio );
2105 const int iBetter = ( badness[1] < badness[0] && badness[0]-badness[1] > 0.1 * badness[0] );
2107 method._nbSplits = nbSplits;
2108 method._nbCorners = 6;
2109 method._connectivity = connVariants[ iBetter ];
2114 //================================================================================
2116 * \brief Check if there is a tetraherdon adjacent to the given element via this facet
2118 //================================================================================
2120 bool TTriangleFacet::hasAdjacentVol( const SMDS_MeshElement* elem,
2121 const SMDSAbs_GeometryType geom ) const
2123 // find the tetrahedron including the three nodes of facet
2124 const SMDS_MeshNode* n1 = elem->GetNode(_n1);
2125 const SMDS_MeshNode* n2 = elem->GetNode(_n2);
2126 const SMDS_MeshNode* n3 = elem->GetNode(_n3);
2127 SMDS_ElemIteratorPtr volIt1 = n1->GetInverseElementIterator(SMDSAbs_Volume);
2128 while ( volIt1->more() )
2130 const SMDS_MeshElement* v = volIt1->next();
2131 if ( v->GetGeomType() != geom )
2133 const int lastCornerInd = v->NbCornerNodes() - 1;
2134 if ( v->IsQuadratic() && v->GetNodeIndex( n1 ) > lastCornerInd )
2135 continue; // medium node not allowed
2136 const int ind2 = v->GetNodeIndex( n2 );
2137 if ( ind2 < 0 || lastCornerInd < ind2 )
2139 const int ind3 = v->GetNodeIndex( n3 );
2140 if ( ind3 < 0 || lastCornerInd < ind3 )
2147 //=======================================================================
2149 * \brief A key of a face of volume
2151 //=======================================================================
2153 struct TVolumeFaceKey: pair< pair< int, int>, pair< int, int> >
2155 TVolumeFaceKey( SMDS_VolumeTool& vol, int iF )
2157 TIDSortedNodeSet sortedNodes;
2158 const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
2159 int nbNodes = vol.NbFaceNodes( iF );
2160 const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iF );
2161 for ( int i = 0; i < nbNodes; i += iQ )
2162 sortedNodes.insert( fNodes[i] );
2163 TIDSortedNodeSet::iterator n = sortedNodes.begin();
2164 first.first = (*(n++))->GetID();
2165 first.second = (*(n++))->GetID();
2166 second.first = (*(n++))->GetID();
2167 second.second = ( sortedNodes.size() > 3 ) ? (*(n++))->GetID() : 0;
2172 //=======================================================================
2173 //function : SplitVolumes
2174 //purpose : Split volume elements into tetrahedra or prisms.
2175 // If facet ID < 0, element is split into tetrahedra,
2176 // else a hexahedron is split into prisms so that the given facet is
2177 // split into triangles
2178 //=======================================================================
2180 void SMESH_MeshEditor::SplitVolumes (const TFacetOfElem & theElems,
2181 const int theMethodFlags)
2183 SMDS_VolumeTool volTool;
2184 SMESH_MesherHelper helper( *GetMesh()), fHelper(*GetMesh());
2185 fHelper.ToFixNodeParameters( true );
2187 SMESHDS_SubMesh* subMesh = 0;//GetMeshDS()->MeshElements(1);
2188 SMESHDS_SubMesh* fSubMesh = 0;//subMesh;
2190 SMESH_SequenceOfElemPtr newNodes, newElems;
2192 // map face of volume to it's baricenrtic node
2193 map< TVolumeFaceKey, const SMDS_MeshNode* > volFace2BaryNode;
2196 TFacetOfElem::const_iterator elem2facet = theElems.begin();
2197 for ( ; elem2facet != theElems.end(); ++elem2facet )
2199 const SMDS_MeshElement* elem = elem2facet->first;
2200 const int facetToSplit = elem2facet->second;
2201 if ( elem->GetType() != SMDSAbs_Volume )
2203 const SMDSAbs_EntityType geomType = elem->GetEntityType();
2204 if ( geomType == SMDSEntity_Tetra || geomType == SMDSEntity_Quad_Tetra )
2207 if ( !volTool.Set( elem, /*ignoreCentralNodes=*/false )) continue; // strange...
2209 TSplitMethod splitMethod = ( facetToSplit < 0 ?
2210 getTetraSplitMethod( volTool, theMethodFlags ) :
2211 getPrismSplitMethod( volTool, theMethodFlags, facetToSplit ));
2212 if ( splitMethod._nbSplits < 1 ) continue;
2214 // find submesh to add new tetras to
2215 if ( !subMesh || !subMesh->Contains( elem ))
2217 int shapeID = FindShape( elem );
2218 helper.SetSubShape( shapeID ); // helper will add tetras to the found submesh
2219 subMesh = GetMeshDS()->MeshElements( shapeID );
2222 if ( elem->IsQuadratic() )
2225 // add quadratic links to the helper
2226 for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
2228 const SMDS_MeshNode** fNodes = volTool.GetFaceNodes( iF );
2229 int nbN = volTool.NbFaceNodes( iF ) - bool( volTool.GetCenterNodeIndex(iF) > 0 );
2230 for ( int iN = 0; iN < nbN; iN += iQ )
2231 helper.AddTLinkNode( fNodes[iN], fNodes[iN+2], fNodes[iN+1] );
2233 helper.SetIsQuadratic( true );
2238 helper.SetIsQuadratic( false );
2240 vector<const SMDS_MeshNode*> nodes( volTool.GetNodes(),
2241 volTool.GetNodes() + elem->NbNodes() );
2242 helper.SetElementsOnShape( true );
2243 if ( splitMethod._baryNode )
2245 // make a node at barycenter
2246 volTool.GetBaryCenter( bc[0], bc[1], bc[2] );
2247 SMDS_MeshNode* gcNode = helper.AddNode( bc[0], bc[1], bc[2] );
2248 nodes.push_back( gcNode );
2249 newNodes.Append( gcNode );
2251 if ( !splitMethod._faceBaryNode.empty() )
2253 // make or find baricentric nodes of faces
2254 map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.begin();
2255 for ( ; iF_n != splitMethod._faceBaryNode.end(); ++iF_n )
2257 map< TVolumeFaceKey, const SMDS_MeshNode* >::iterator f_n =
2258 volFace2BaryNode.insert
2259 ( make_pair( TVolumeFaceKey( volTool,iF_n->first ), iF_n->second )).first;
2262 volTool.GetFaceBaryCenter( iF_n->first, bc[0], bc[1], bc[2] );
2263 newNodes.Append( f_n->second = helper.AddNode( bc[0], bc[1], bc[2] ));
2265 nodes.push_back( iF_n->second = f_n->second );
2270 vector<const SMDS_MeshElement* > splitVols( splitMethod._nbSplits ); // splits of a volume
2271 const int* volConn = splitMethod._connectivity;
2272 if ( splitMethod._nbCorners == 4 ) // tetra
2273 for ( int i = 0; i < splitMethod._nbSplits; ++i, volConn += splitMethod._nbCorners )
2274 newElems.Append( splitVols[ i ] = helper.AddVolume( nodes[ volConn[0] ],
2275 nodes[ volConn[1] ],
2276 nodes[ volConn[2] ],
2277 nodes[ volConn[3] ]));
2279 for ( int i = 0; i < splitMethod._nbSplits; ++i, volConn += splitMethod._nbCorners )
2280 newElems.Append( splitVols[ i ] = helper.AddVolume( nodes[ volConn[0] ],
2281 nodes[ volConn[1] ],
2282 nodes[ volConn[2] ],
2283 nodes[ volConn[3] ],
2284 nodes[ volConn[4] ],
2285 nodes[ volConn[5] ]));
2287 ReplaceElemInGroups( elem, splitVols, GetMeshDS() );
2289 // Split faces on sides of the split volume
2291 const SMDS_MeshNode** volNodes = volTool.GetNodes();
2292 for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
2294 const int nbNodes = volTool.NbFaceNodes( iF ) / iQ;
2295 if ( nbNodes < 4 ) continue;
2297 // find an existing face
2298 vector<const SMDS_MeshNode*> fNodes( volTool.GetFaceNodes( iF ),
2299 volTool.GetFaceNodes( iF ) + volTool.NbFaceNodes( iF ));
2300 while ( const SMDS_MeshElement* face = GetMeshDS()->FindElement( fNodes, SMDSAbs_Face,
2301 /*noMedium=*/false))
2304 helper.SetElementsOnShape( false );
2305 vector< const SMDS_MeshElement* > triangles;
2307 // find submesh to add new triangles in
2308 if ( !fSubMesh || !fSubMesh->Contains( face ))
2310 int shapeID = FindShape( face );
2311 fSubMesh = GetMeshDS()->MeshElements( shapeID );
2313 map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.find(iF);
2314 if ( iF_n != splitMethod._faceBaryNode.end() )
2316 const SMDS_MeshNode *baryNode = iF_n->second;
2317 for ( int iN = 0; iN < nbNodes*iQ; iN += iQ )
2319 const SMDS_MeshNode* n1 = fNodes[iN];
2320 const SMDS_MeshNode *n2 = fNodes[(iN+iQ)%(nbNodes*iQ)];
2321 const SMDS_MeshNode *n3 = baryNode;
2322 if ( !volTool.IsFaceExternal( iF ))
2324 triangles.push_back( helper.AddFace( n1,n2,n3 ));
2326 if ( fSubMesh ) // update position of the bary node on geometry
2329 subMesh->RemoveNode( baryNode, false );
2330 GetMeshDS()->SetNodeOnFace( baryNode, fSubMesh->GetID() );
2331 const TopoDS_Shape& s = GetMeshDS()->IndexToShape( fSubMesh->GetID() );
2332 if ( !s.IsNull() && s.ShapeType() == TopAbs_FACE )
2334 fHelper.SetSubShape( s );
2335 gp_XY uv( 1e100, 1e100 );
2337 if ( !fHelper.CheckNodeUV( TopoDS::Face( s ), baryNode,
2338 uv, /*tol=*/1e-7, /*force=*/true, distXYZ ) &&
2341 // node is too far from the surface
2342 GetMeshDS()->MoveNode( baryNode, distXYZ[1], distXYZ[2], distXYZ[3] );
2343 const_cast<SMDS_MeshNode*>( baryNode )->SetPosition
2344 ( SMDS_PositionPtr( new SMDS_FacePosition( uv.X(), uv.Y() )));
2351 // among possible triangles create ones discribed by split method
2352 const int* nInd = volTool.GetFaceNodesIndices( iF );
2353 int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
2354 int iCom = 0; // common node of triangle faces to split into
2355 list< TTriangleFacet > facets;
2356 for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCom )
2358 TTriangleFacet t012( nInd[ iQ * ( iCom )],
2359 nInd[ iQ * ( (iCom+1)%nbNodes )],
2360 nInd[ iQ * ( (iCom+2)%nbNodes )]);
2361 TTriangleFacet t023( nInd[ iQ * ( iCom )],
2362 nInd[ iQ * ( (iCom+2)%nbNodes )],
2363 nInd[ iQ * ( (iCom+3)%nbNodes )]);
2364 if ( splitMethod.hasFacet( t012 ) && splitMethod.hasFacet( t023 ))
2366 facets.push_back( t012 );
2367 facets.push_back( t023 );
2368 for ( int iLast = iCom+4; iLast < iCom+nbNodes; ++iLast )
2369 facets.push_back( TTriangleFacet( nInd[ iQ * ( iCom )],
2370 nInd[ iQ * ((iLast-1)%nbNodes )],
2371 nInd[ iQ * ((iLast )%nbNodes )]));
2375 list< TTriangleFacet >::iterator facet = facets.begin();
2376 if ( facet == facets.end() )
2378 for ( ; facet != facets.end(); ++facet )
2380 if ( !volTool.IsFaceExternal( iF ))
2381 swap( facet->_n2, facet->_n3 );
2382 triangles.push_back( helper.AddFace( volNodes[ facet->_n1 ],
2383 volNodes[ facet->_n2 ],
2384 volNodes[ facet->_n3 ]));
2387 for ( int i = 0; i < triangles.size(); ++i )
2389 if ( !triangles[i] ) continue;
2391 fSubMesh->AddElement( triangles[i]);
2392 newElems.Append( triangles[i] );
2394 ReplaceElemInGroups( face, triangles, GetMeshDS() );
2395 GetMeshDS()->RemoveFreeElement( face, fSubMesh, /*fromGroups=*/false );
2397 } // while a face based on facet nodes exists
2398 } // loop on volume faces to split them into triangles
2400 GetMeshDS()->RemoveFreeElement( elem, subMesh, /*fromGroups=*/false );
2402 if ( geomType == SMDSEntity_TriQuad_Hexa )
2404 // remove medium nodes that could become free
2405 for ( int i = 20; i < volTool.NbNodes(); ++i )
2406 if ( volNodes[i]->NbInverseElements() == 0 )
2407 GetMeshDS()->RemoveNode( volNodes[i] );
2409 } // loop on volumes to split
2411 myLastCreatedNodes = newNodes;
2412 myLastCreatedElems = newElems;
2415 //=======================================================================
2416 //function : GetHexaFacetsToSplit
2417 //purpose : For hexahedra that will be split into prisms, finds facets to
2418 // split into triangles. Only hexahedra adjacent to the one closest
2419 // to theFacetNormal.Location() are returned.
2420 //param [in,out] theHexas - the hexahedra
2421 //param [in] theFacetNormal - facet normal
2422 //param [out] theFacets - the hexahedra and found facet IDs
2423 //=======================================================================
2425 void SMESH_MeshEditor::GetHexaFacetsToSplit( TIDSortedElemSet& theHexas,
2426 const gp_Ax1& theFacetNormal,
2427 TFacetOfElem & theFacets)
2429 #define THIS_METHOD "SMESH_MeshEditor::GetHexaFacetsToSplit(): "
2431 // Find a hexa closest to the location of theFacetNormal
2433 const SMDS_MeshElement* startHex;
2435 // get SMDS_ElemIteratorPtr on theHexas
2436 typedef const SMDS_MeshElement* TValue;
2437 typedef TIDSortedElemSet::iterator TSetIterator;
2438 typedef SMDS::SimpleAccessor<TValue,TSetIterator> TAccesor;
2439 typedef SMDS_MeshElement::GeomFilter TFilter;
2440 typedef SMDS_SetIterator < TValue, TSetIterator, TAccesor, TFilter > TElemSetIter;
2441 SMDS_ElemIteratorPtr elemIt = SMDS_ElemIteratorPtr
2442 ( new TElemSetIter( theHexas.begin(),
2444 SMDS_MeshElement::GeomFilter( SMDSGeom_HEXA )));
2446 SMESH_ElementSearcher* searcher =
2447 SMESH_MeshAlgos::GetElementSearcher( *myMesh->GetMeshDS(), elemIt );
2449 startHex = searcher->FindClosestTo( theFacetNormal.Location(), SMDSAbs_Volume );
2454 throw SALOME_Exception( THIS_METHOD "startHex not found");
2457 // Select a facet of startHex by theFacetNormal
2459 SMDS_VolumeTool vTool( startHex );
2460 double norm[3], dot, maxDot = 0;
2462 for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2463 if ( vTool.GetFaceNormal( iF, norm[0], norm[1], norm[2] ))
2465 dot = Abs( theFacetNormal.Direction().Dot( gp_Dir( norm[0], norm[1], norm[2] )));
2473 throw SALOME_Exception( THIS_METHOD "facet of startHex not found");
2475 // Fill theFacets starting from facetID of startHex
2477 // facets used for seach of volumes adjacent to already treated ones
2478 typedef pair< TFacetOfElem::iterator, int > TElemFacets;
2479 typedef map< TVolumeFaceKey, TElemFacets > TFacetMap;
2480 TFacetMap facetsToCheck;
2482 set<const SMDS_MeshNode*> facetNodes;
2483 const SMDS_MeshElement* curHex;
2485 const bool allHex = ( theHexas.size() == myMesh->NbHexas() );
2489 // move in two directions from startHex via facetID
2490 for ( int is2nd = 0; is2nd < 2; ++is2nd )
2493 int curFacet = facetID;
2494 if ( is2nd ) // do not treat startHex twice
2496 vTool.Set( curHex );
2497 if ( vTool.IsFreeFace( curFacet, &curHex ))
2503 vTool.GetFaceNodes( curFacet, facetNodes );
2504 vTool.Set( curHex );
2505 curFacet = vTool.GetFaceIndex( facetNodes );
2510 // store a facet to split
2511 if ( curHex->GetGeomType() != SMDSGeom_HEXA )
2513 theFacets.insert( make_pair( curHex, -1 ));
2516 if ( !allHex && !theHexas.count( curHex ))
2519 pair< TFacetOfElem::iterator, bool > facetIt2isNew =
2520 theFacets.insert( make_pair( curHex, curFacet ));
2521 if ( !facetIt2isNew.second )
2524 // remember not-to-split facets in facetsToCheck
2525 int oppFacet = vTool.GetOppFaceIndexOfHex( curFacet );
2526 for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2528 if ( iF == curFacet && iF == oppFacet )
2530 TVolumeFaceKey facetKey ( vTool, iF );
2531 TElemFacets elemFacet( facetIt2isNew.first, iF );
2532 pair< TFacetMap::iterator, bool > it2isnew =
2533 facetsToCheck.insert( make_pair( facetKey, elemFacet ));
2534 if ( !it2isnew.second )
2535 facetsToCheck.erase( it2isnew.first ); // adjacent hex already checked
2537 // pass to a volume adjacent via oppFacet
2538 if ( vTool.IsFreeFace( oppFacet, &curHex ))
2544 // get a new curFacet
2545 vTool.GetFaceNodes( oppFacet, facetNodes );
2546 vTool.Set( curHex );
2547 curFacet = vTool.GetFaceIndex( facetNodes, /*hint=*/curFacet );
2550 } // move in two directions from startHex via facetID
2552 // Find a new startHex by facetsToCheck
2556 TFacetMap::iterator fIt = facetsToCheck.begin();
2557 while ( !startHex && fIt != facetsToCheck.end() )
2559 const TElemFacets& elemFacets = fIt->second;
2560 const SMDS_MeshElement* hex = elemFacets.first->first;
2561 int splitFacet = elemFacets.first->second;
2562 int lateralFacet = elemFacets.second;
2563 facetsToCheck.erase( fIt );
2564 fIt = facetsToCheck.begin();
2567 if ( vTool.IsFreeFace( lateralFacet, &curHex ) ||
2568 curHex->GetGeomType() != SMDSGeom_HEXA )
2570 if ( !allHex && !theHexas.count( curHex ))
2575 // find a facet of startHex to split
2577 set<const SMDS_MeshNode*> lateralNodes;
2578 vTool.GetFaceNodes( lateralFacet, lateralNodes );
2579 vTool.GetFaceNodes( splitFacet, facetNodes );
2580 int oppLateralFacet = vTool.GetOppFaceIndexOfHex( lateralFacet );
2581 vTool.Set( startHex );
2582 lateralFacet = vTool.GetFaceIndex( lateralNodes, oppLateralFacet );
2584 // look for a facet of startHex having common nodes with facetNodes
2585 // but not lateralFacet
2586 for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2588 if ( iF == lateralFacet )
2590 int nbCommonNodes = 0;
2591 const SMDS_MeshNode** nn = vTool.GetFaceNodes( iF );
2592 for ( int iN = 0, nbN = vTool.NbFaceNodes( iF ); iN < nbN; ++iN )
2593 nbCommonNodes += facetNodes.count( nn[ iN ]);
2595 if ( nbCommonNodes >= 2 )
2602 throw SALOME_Exception( THIS_METHOD "facet of a new startHex not found");
2604 } // while ( startHex )
2607 //=======================================================================
2608 //function : AddToSameGroups
2609 //purpose : add elemToAdd to the groups the elemInGroups belongs to
2610 //=======================================================================
2612 void SMESH_MeshEditor::AddToSameGroups (const SMDS_MeshElement* elemToAdd,
2613 const SMDS_MeshElement* elemInGroups,
2614 SMESHDS_Mesh * aMesh)
2616 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2617 if (!groups.empty()) {
2618 set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2619 for ( ; grIt != groups.end(); grIt++ ) {
2620 SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2621 if ( group && group->Contains( elemInGroups ))
2622 group->SMDSGroup().Add( elemToAdd );
2628 //=======================================================================
2629 //function : RemoveElemFromGroups
2630 //purpose : Remove removeelem to the groups the elemInGroups belongs to
2631 //=======================================================================
2632 void SMESH_MeshEditor::RemoveElemFromGroups (const SMDS_MeshElement* removeelem,
2633 SMESHDS_Mesh * aMesh)
2635 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2636 if (!groups.empty())
2638 set<SMESHDS_GroupBase*>::const_iterator GrIt = groups.begin();
2639 for (; GrIt != groups.end(); GrIt++)
2641 SMESHDS_Group* grp = dynamic_cast<SMESHDS_Group*>(*GrIt);
2642 if (!grp || grp->IsEmpty()) continue;
2643 grp->SMDSGroup().Remove(removeelem);
2648 //================================================================================
2650 * \brief Replace elemToRm by elemToAdd in the all groups
2652 //================================================================================
2654 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement* elemToRm,
2655 const SMDS_MeshElement* elemToAdd,
2656 SMESHDS_Mesh * aMesh)
2658 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2659 if (!groups.empty()) {
2660 set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2661 for ( ; grIt != groups.end(); grIt++ ) {
2662 SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2663 if ( group && group->SMDSGroup().Remove( elemToRm ) && elemToAdd )
2664 group->SMDSGroup().Add( elemToAdd );
2669 //================================================================================
2671 * \brief Replace elemToRm by elemToAdd in the all groups
2673 //================================================================================
2675 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement* elemToRm,
2676 const vector<const SMDS_MeshElement*>& elemToAdd,
2677 SMESHDS_Mesh * aMesh)
2679 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2680 if (!groups.empty())
2682 set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2683 for ( ; grIt != groups.end(); grIt++ ) {
2684 SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2685 if ( group && group->SMDSGroup().Remove( elemToRm ) )
2686 for ( int i = 0; i < elemToAdd.size(); ++i )
2687 group->SMDSGroup().Add( elemToAdd[ i ] );
2692 //=======================================================================
2693 //function : QuadToTri
2694 //purpose : Cut quadrangles into triangles.
2695 // theCrit is used to select a diagonal to cut
2696 //=======================================================================
2698 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet & theElems,
2699 const bool the13Diag)
2701 myLastCreatedElems.Clear();
2702 myLastCreatedNodes.Clear();
2704 MESSAGE( "::QuadToTri()" );
2706 SMESHDS_Mesh * aMesh = GetMeshDS();
2708 Handle(Geom_Surface) surface;
2709 SMESH_MesherHelper helper( *GetMesh() );
2711 TIDSortedElemSet::iterator itElem;
2712 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
2713 const SMDS_MeshElement* elem = *itElem;
2714 if ( !elem || elem->GetType() != SMDSAbs_Face )
2716 bool isquad = elem->NbNodes()==4 || elem->NbNodes()==8;
2717 if(!isquad) continue;
2719 if(elem->NbNodes()==4) {
2720 // retrieve element nodes
2721 const SMDS_MeshNode* aNodes [4];
2722 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
2724 while ( itN->more() )
2725 aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
2727 int aShapeId = FindShape( elem );
2728 const SMDS_MeshElement* newElem1 = 0;
2729 const SMDS_MeshElement* newElem2 = 0;
2731 newElem1 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
2732 newElem2 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
2735 newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
2736 newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
2738 myLastCreatedElems.Append(newElem1);
2739 myLastCreatedElems.Append(newElem2);
2740 // put a new triangle on the same shape and add to the same groups
2743 aMesh->SetMeshElementOnShape( newElem1, aShapeId );
2744 aMesh->SetMeshElementOnShape( newElem2, aShapeId );
2746 AddToSameGroups( newElem1, elem, aMesh );
2747 AddToSameGroups( newElem2, elem, aMesh );
2748 //aMesh->RemoveFreeElement(elem, aMesh->MeshElements(aShapeId), true);
2749 aMesh->RemoveElement( elem );
2752 // Quadratic quadrangle
2754 if( elem->NbNodes()==8 && elem->IsQuadratic() ) {
2756 // get surface elem is on
2757 int aShapeId = FindShape( elem );
2758 if ( aShapeId != helper.GetSubShapeID() ) {
2762 shape = aMesh->IndexToShape( aShapeId );
2763 if ( !shape.IsNull() && shape.ShapeType() == TopAbs_FACE ) {
2764 TopoDS_Face face = TopoDS::Face( shape );
2765 surface = BRep_Tool::Surface( face );
2766 if ( !surface.IsNull() )
2767 helper.SetSubShape( shape );
2771 const SMDS_MeshNode* aNodes [8];
2772 const SMDS_MeshNode* inFaceNode = 0;
2773 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
2775 while ( itN->more() ) {
2776 aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
2777 if ( !inFaceNode && helper.GetNodeUVneedInFaceNode() &&
2778 aNodes[ i-1 ]->GetPosition()->GetTypeOfPosition() == SMDS_TOP_FACE )
2780 inFaceNode = aNodes[ i-1 ];
2784 // find middle point for (0,1,2,3)
2785 // and create a node in this point;
2787 if ( surface.IsNull() ) {
2789 p += gp_XYZ(aNodes[i]->X(), aNodes[i]->Y(), aNodes[i]->Z() );
2793 TopoDS_Face geomFace = TopoDS::Face( helper.GetSubShape() );
2796 uv += helper.GetNodeUV( geomFace, aNodes[i], inFaceNode );
2798 p = surface->Value( uv.X(), uv.Y() ).XYZ();
2800 const SMDS_MeshNode* newN = aMesh->AddNode( p.X(), p.Y(), p.Z() );
2801 myLastCreatedNodes.Append(newN);
2803 // create a new element
2804 const SMDS_MeshElement* newElem1 = 0;
2805 const SMDS_MeshElement* newElem2 = 0;
2807 newElem1 = aMesh->AddFace(aNodes[2], aNodes[3], aNodes[0],
2808 aNodes[6], aNodes[7], newN );
2809 newElem2 = aMesh->AddFace(aNodes[2], aNodes[0], aNodes[1],
2810 newN, aNodes[4], aNodes[5] );
2813 newElem1 = aMesh->AddFace(aNodes[3], aNodes[0], aNodes[1],
2814 aNodes[7], aNodes[4], newN );
2815 newElem2 = aMesh->AddFace(aNodes[3], aNodes[1], aNodes[2],
2816 newN, aNodes[5], aNodes[6] );
2818 myLastCreatedElems.Append(newElem1);
2819 myLastCreatedElems.Append(newElem2);
2820 // put a new triangle on the same shape and add to the same groups
2823 aMesh->SetMeshElementOnShape( newElem1, aShapeId );
2824 aMesh->SetMeshElementOnShape( newElem2, aShapeId );
2826 AddToSameGroups( newElem1, elem, aMesh );
2827 AddToSameGroups( newElem2, elem, aMesh );
2828 aMesh->RemoveElement( elem );
2835 //=======================================================================
2836 //function : getAngle
2838 //=======================================================================
2840 double getAngle(const SMDS_MeshElement * tr1,
2841 const SMDS_MeshElement * tr2,
2842 const SMDS_MeshNode * n1,
2843 const SMDS_MeshNode * n2)
2845 double angle = 2. * M_PI; // bad angle
2848 SMESH::Controls::TSequenceOfXYZ P1, P2;
2849 if ( !SMESH::Controls::NumericalFunctor::GetPoints( tr1, P1 ) ||
2850 !SMESH::Controls::NumericalFunctor::GetPoints( tr2, P2 ))
2853 if(!tr1->IsQuadratic())
2854 N1 = gp_Vec( P1(2) - P1(1) ) ^ gp_Vec( P1(3) - P1(1) );
2856 N1 = gp_Vec( P1(3) - P1(1) ) ^ gp_Vec( P1(5) - P1(1) );
2857 if ( N1.SquareMagnitude() <= gp::Resolution() )
2859 if(!tr2->IsQuadratic())
2860 N2 = gp_Vec( P2(2) - P2(1) ) ^ gp_Vec( P2(3) - P2(1) );
2862 N2 = gp_Vec( P2(3) - P2(1) ) ^ gp_Vec( P2(5) - P2(1) );
2863 if ( N2.SquareMagnitude() <= gp::Resolution() )
2866 // find the first diagonal node n1 in the triangles:
2867 // take in account a diagonal link orientation
2868 const SMDS_MeshElement *nFirst[2], *tr[] = { tr1, tr2 };
2869 for ( int t = 0; t < 2; t++ ) {
2870 SMDS_ElemIteratorPtr it = tr[ t ]->nodesIterator();
2871 int i = 0, iDiag = -1;
2872 while ( it->more()) {
2873 const SMDS_MeshElement *n = it->next();
2874 if ( n == n1 || n == n2 ) {
2878 if ( i - iDiag == 1 )
2879 nFirst[ t ] = ( n == n1 ? n2 : n1 );
2888 if ( nFirst[ 0 ] == nFirst[ 1 ] )
2891 angle = N1.Angle( N2 );
2896 // =================================================
2897 // class generating a unique ID for a pair of nodes
2898 // and able to return nodes by that ID
2899 // =================================================
2903 LinkID_Gen( const SMESHDS_Mesh* theMesh )
2904 :myMesh( theMesh ), myMaxID( theMesh->MaxNodeID() + 1)
2907 long GetLinkID (const SMDS_MeshNode * n1,
2908 const SMDS_MeshNode * n2) const
2910 return ( Min(n1->GetID(),n2->GetID()) * myMaxID + Max(n1->GetID(),n2->GetID()));
2913 bool GetNodes (const long theLinkID,
2914 const SMDS_MeshNode* & theNode1,
2915 const SMDS_MeshNode* & theNode2) const
2917 theNode1 = myMesh->FindNode( theLinkID / myMaxID );
2918 if ( !theNode1 ) return false;
2919 theNode2 = myMesh->FindNode( theLinkID % myMaxID );
2920 if ( !theNode2 ) return false;
2926 const SMESHDS_Mesh* myMesh;
2931 //=======================================================================
2932 //function : TriToQuad
2933 //purpose : Fuse neighbour triangles into quadrangles.
2934 // theCrit is used to select a neighbour to fuse with.
2935 // theMaxAngle is a max angle between element normals at which
2936 // fusion is still performed.
2937 //=======================================================================
2939 bool SMESH_MeshEditor::TriToQuad (TIDSortedElemSet & theElems,
2940 SMESH::Controls::NumericalFunctorPtr theCrit,
2941 const double theMaxAngle)
2943 myLastCreatedElems.Clear();
2944 myLastCreatedNodes.Clear();
2946 MESSAGE( "::TriToQuad()" );
2948 if ( !theCrit.get() )
2951 SMESHDS_Mesh * aMesh = GetMeshDS();
2953 // Prepare data for algo: build
2954 // 1. map of elements with their linkIDs
2955 // 2. map of linkIDs with their elements
2957 map< SMESH_TLink, list< const SMDS_MeshElement* > > mapLi_listEl;
2958 map< SMESH_TLink, list< const SMDS_MeshElement* > >::iterator itLE;
2959 map< const SMDS_MeshElement*, set< SMESH_TLink > > mapEl_setLi;
2960 map< const SMDS_MeshElement*, set< SMESH_TLink > >::iterator itEL;
2962 TIDSortedElemSet::iterator itElem;
2963 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
2965 const SMDS_MeshElement* elem = *itElem;
2966 if(!elem || elem->GetType() != SMDSAbs_Face ) continue;
2967 bool IsTria = ( elem->NbCornerNodes()==3 );
2968 if (!IsTria) continue;
2970 // retrieve element nodes
2971 const SMDS_MeshNode* aNodes [4];
2972 SMDS_NodeIteratorPtr itN = elem->nodeIterator();
2975 aNodes[ i++ ] = itN->next();
2976 aNodes[ 3 ] = aNodes[ 0 ];
2979 for ( i = 0; i < 3; i++ ) {
2980 SMESH_TLink link( aNodes[i], aNodes[i+1] );
2981 // check if elements sharing a link can be fused
2982 itLE = mapLi_listEl.find( link );
2983 if ( itLE != mapLi_listEl.end() ) {
2984 if ((*itLE).second.size() > 1 ) // consider only 2 elems adjacent by a link
2986 const SMDS_MeshElement* elem2 = (*itLE).second.front();
2987 //if ( FindShape( elem ) != FindShape( elem2 ))
2988 // continue; // do not fuse triangles laying on different shapes
2989 if ( getAngle( elem, elem2, aNodes[i], aNodes[i+1] ) > theMaxAngle )
2990 continue; // avoid making badly shaped quads
2991 (*itLE).second.push_back( elem );
2994 mapLi_listEl[ link ].push_back( elem );
2996 mapEl_setLi [ elem ].insert( link );
2999 // Clean the maps from the links shared by a sole element, ie
3000 // links to which only one element is bound in mapLi_listEl
3002 for ( itLE = mapLi_listEl.begin(); itLE != mapLi_listEl.end(); itLE++ ) {
3003 int nbElems = (*itLE).second.size();
3004 if ( nbElems < 2 ) {
3005 const SMDS_MeshElement* elem = (*itLE).second.front();
3006 SMESH_TLink link = (*itLE).first;
3007 mapEl_setLi[ elem ].erase( link );
3008 if ( mapEl_setLi[ elem ].empty() )
3009 mapEl_setLi.erase( elem );
3013 // Algo: fuse triangles into quadrangles
3015 while ( ! mapEl_setLi.empty() ) {
3016 // Look for the start element:
3017 // the element having the least nb of shared links
3018 const SMDS_MeshElement* startElem = 0;
3020 for ( itEL = mapEl_setLi.begin(); itEL != mapEl_setLi.end(); itEL++ ) {
3021 int nbLinks = (*itEL).second.size();
3022 if ( nbLinks < minNbLinks ) {
3023 startElem = (*itEL).first;
3024 minNbLinks = nbLinks;
3025 if ( minNbLinks == 1 )
3030 // search elements to fuse starting from startElem or links of elements
3031 // fused earlyer - startLinks
3032 list< SMESH_TLink > startLinks;
3033 while ( startElem || !startLinks.empty() ) {
3034 while ( !startElem && !startLinks.empty() ) {
3035 // Get an element to start, by a link
3036 SMESH_TLink linkId = startLinks.front();
3037 startLinks.pop_front();
3038 itLE = mapLi_listEl.find( linkId );
3039 if ( itLE != mapLi_listEl.end() ) {
3040 list< const SMDS_MeshElement* > & listElem = (*itLE).second;
3041 list< const SMDS_MeshElement* >::iterator itE = listElem.begin();
3042 for ( ; itE != listElem.end() ; itE++ )
3043 if ( mapEl_setLi.find( (*itE) ) != mapEl_setLi.end() )
3045 mapLi_listEl.erase( itLE );
3050 // Get candidates to be fused
3051 const SMDS_MeshElement *tr1 = startElem, *tr2 = 0, *tr3 = 0;
3052 const SMESH_TLink *link12, *link13;
3054 ASSERT( mapEl_setLi.find( tr1 ) != mapEl_setLi.end() );
3055 set< SMESH_TLink >& setLi = mapEl_setLi[ tr1 ];
3056 ASSERT( !setLi.empty() );
3057 set< SMESH_TLink >::iterator itLi;
3058 for ( itLi = setLi.begin(); itLi != setLi.end(); itLi++ )
3060 const SMESH_TLink & link = (*itLi);
3061 itLE = mapLi_listEl.find( link );
3062 if ( itLE == mapLi_listEl.end() )
3065 const SMDS_MeshElement* elem = (*itLE).second.front();
3067 elem = (*itLE).second.back();
3068 mapLi_listEl.erase( itLE );
3069 if ( mapEl_setLi.find( elem ) == mapEl_setLi.end())
3080 // add other links of elem to list of links to re-start from
3081 set< SMESH_TLink >& links = mapEl_setLi[ elem ];
3082 set< SMESH_TLink >::iterator it;
3083 for ( it = links.begin(); it != links.end(); it++ ) {
3084 const SMESH_TLink& link2 = (*it);
3085 if ( link2 != link )
3086 startLinks.push_back( link2 );
3090 // Get nodes of possible quadrangles
3091 const SMDS_MeshNode *n12 [4], *n13 [4];
3092 bool Ok12 = false, Ok13 = false;
3093 const SMDS_MeshNode *linkNode1, *linkNode2;
3095 linkNode1 = link12->first;
3096 linkNode2 = link12->second;
3097 if ( tr2 && getQuadrangleNodes( n12, linkNode1, linkNode2, tr1, tr2 ))
3101 linkNode1 = link13->first;
3102 linkNode2 = link13->second;
3103 if ( tr3 && getQuadrangleNodes( n13, linkNode1, linkNode2, tr1, tr3 ))
3107 // Choose a pair to fuse
3108 if ( Ok12 && Ok13 ) {
3109 SMDS_FaceOfNodes quad12 ( n12[ 0 ], n12[ 1 ], n12[ 2 ], n12[ 3 ] );
3110 SMDS_FaceOfNodes quad13 ( n13[ 0 ], n13[ 1 ], n13[ 2 ], n13[ 3 ] );
3111 double aBadRate12 = getBadRate( &quad12, theCrit );
3112 double aBadRate13 = getBadRate( &quad13, theCrit );
3113 if ( aBadRate13 < aBadRate12 )
3120 // and remove fused elems and remove links from the maps
3121 mapEl_setLi.erase( tr1 );
3124 mapEl_setLi.erase( tr2 );
3125 mapLi_listEl.erase( *link12 );
3126 if ( tr1->NbNodes() == 3 )
3128 const SMDS_MeshElement* newElem = 0;
3129 newElem = aMesh->AddFace(n12[0], n12[1], n12[2], n12[3] );
3130 myLastCreatedElems.Append(newElem);
3131 AddToSameGroups( newElem, tr1, aMesh );
3132 int aShapeId = tr1->getshapeId();
3134 aMesh->SetMeshElementOnShape( newElem, aShapeId );
3135 aMesh->RemoveElement( tr1 );
3136 aMesh->RemoveElement( tr2 );
3139 vector< const SMDS_MeshNode* > N1;
3140 vector< const SMDS_MeshNode* > N2;
3141 getNodesFromTwoTria(tr1,tr2,N1,N2);
3142 // now we receive following N1 and N2 (using numeration as in image in InverseDiag())
3143 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
3144 // i.e. first nodes from both arrays form a new diagonal
3145 const SMDS_MeshNode* aNodes[8];
3154 const SMDS_MeshElement* newElem = 0;
3155 if ( N1.size() == 7 || N2.size() == 7 ) // biquadratic
3156 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3157 aNodes[4], aNodes[5], aNodes[6], aNodes[7], N1[4]);
3159 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3160 aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
3161 myLastCreatedElems.Append(newElem);
3162 AddToSameGroups( newElem, tr1, aMesh );
3163 int aShapeId = tr1->getshapeId();
3165 aMesh->SetMeshElementOnShape( newElem, aShapeId );
3166 aMesh->RemoveElement( tr1 );
3167 aMesh->RemoveElement( tr2 );
3168 // remove middle node (9)
3169 if ( N1[4]->NbInverseElements() == 0 )
3170 aMesh->RemoveNode( N1[4] );
3171 if ( N1.size() == 7 && N1[6]->NbInverseElements() == 0 )
3172 aMesh->RemoveNode( N1[6] );
3173 if ( N2.size() == 7 && N2[6]->NbInverseElements() == 0 )
3174 aMesh->RemoveNode( N2[6] );
3179 mapEl_setLi.erase( tr3 );
3180 mapLi_listEl.erase( *link13 );
3181 if ( tr1->NbNodes() == 3 ) {
3182 const SMDS_MeshElement* newElem = 0;
3183 newElem = aMesh->AddFace(n13[0], n13[1], n13[2], n13[3] );
3184 myLastCreatedElems.Append(newElem);
3185 AddToSameGroups( newElem, tr1, aMesh );
3186 int aShapeId = tr1->getshapeId();
3188 aMesh->SetMeshElementOnShape( newElem, aShapeId );
3189 aMesh->RemoveElement( tr1 );
3190 aMesh->RemoveElement( tr3 );
3193 vector< const SMDS_MeshNode* > N1;
3194 vector< const SMDS_MeshNode* > N2;
3195 getNodesFromTwoTria(tr1,tr3,N1,N2);
3196 // now we receive following N1 and N2 (using numeration as above image)
3197 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
3198 // i.e. first nodes from both arrays form a new diagonal
3199 const SMDS_MeshNode* aNodes[8];
3208 const SMDS_MeshElement* newElem = 0;
3209 if ( N1.size() == 7 || N2.size() == 7 ) // biquadratic
3210 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3211 aNodes[4], aNodes[5], aNodes[6], aNodes[7], N1[4]);
3213 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3214 aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
3215 myLastCreatedElems.Append(newElem);
3216 AddToSameGroups( newElem, tr1, aMesh );
3217 int aShapeId = tr1->getshapeId();
3219 aMesh->SetMeshElementOnShape( newElem, aShapeId );
3220 aMesh->RemoveElement( tr1 );
3221 aMesh->RemoveElement( tr3 );
3222 // remove middle node (9)
3223 if ( N1[4]->NbInverseElements() == 0 )
3224 aMesh->RemoveNode( N1[4] );
3225 if ( N1.size() == 7 && N1[6]->NbInverseElements() == 0 )
3226 aMesh->RemoveNode( N1[6] );
3227 if ( N2.size() == 7 && N2[6]->NbInverseElements() == 0 )
3228 aMesh->RemoveNode( N2[6] );
3232 // Next element to fuse: the rejected one
3234 startElem = Ok12 ? tr3 : tr2;
3236 } // if ( startElem )
3237 } // while ( startElem || !startLinks.empty() )
3238 } // while ( ! mapEl_setLi.empty() )
3244 /*#define DUMPSO(txt) \
3245 // cout << txt << endl;
3246 //=============================================================================
3250 //=============================================================================
3251 static void swap( int i1, int i2, int idNodes[], gp_Pnt P[] )
3255 int tmp = idNodes[ i1 ];
3256 idNodes[ i1 ] = idNodes[ i2 ];
3257 idNodes[ i2 ] = tmp;
3258 gp_Pnt Ptmp = P[ i1 ];
3261 DUMPSO( i1 << "(" << idNodes[ i2 ] << ") <-> " << i2 << "(" << idNodes[ i1 ] << ")");
3264 //=======================================================================
3265 //function : SortQuadNodes
3266 //purpose : Set 4 nodes of a quadrangle face in a good order.
3267 // Swap 1<->2 or 2<->3 nodes and correspondingly return
3269 //=======================================================================
3271 int SMESH_MeshEditor::SortQuadNodes (const SMDS_Mesh * theMesh,
3276 for ( i = 0; i < 4; i++ ) {
3277 const SMDS_MeshNode *n = theMesh->FindNode( idNodes[i] );
3279 P[ i ].SetCoord( n->X(), n->Y(), n->Z() );
3282 gp_Vec V1(P[0], P[1]);
3283 gp_Vec V2(P[0], P[2]);
3284 gp_Vec V3(P[0], P[3]);
3286 gp_Vec Cross1 = V1 ^ V2;
3287 gp_Vec Cross2 = V2 ^ V3;
3290 if (Cross1.Dot(Cross2) < 0)
3295 if (Cross1.Dot(Cross2) < 0)
3299 swap ( i, i + 1, idNodes, P );
3301 // for ( int ii = 0; ii < 4; ii++ ) {
3302 // const SMDS_MeshNode *n = theMesh->FindNode( idNodes[ii] );
3303 // DUMPSO( ii << "(" << idNodes[ii] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
3309 //=======================================================================
3310 //function : SortHexaNodes
3311 //purpose : Set 8 nodes of a hexahedron in a good order.
3312 // Return success status
3313 //=======================================================================
3315 bool SMESH_MeshEditor::SortHexaNodes (const SMDS_Mesh * theMesh,
3320 DUMPSO( "INPUT: ========================================");
3321 for ( i = 0; i < 8; i++ ) {
3322 const SMDS_MeshNode *n = theMesh->FindNode( idNodes[i] );
3323 if ( !n ) return false;
3324 P[ i ].SetCoord( n->X(), n->Y(), n->Z() );
3325 DUMPSO( i << "(" << idNodes[i] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
3327 DUMPSO( "========================================");
3330 set<int> faceNodes; // ids of bottom face nodes, to be found
3331 set<int> checkedId1; // ids of tried 2-nd nodes
3332 Standard_Real leastDist = DBL_MAX; // dist of the 4-th node from 123 plane
3333 const Standard_Real tol = 1.e-6; // tolerance to find nodes in plane
3334 int iMin, iLoop1 = 0;
3336 // Loop to try the 2-nd nodes
3338 while ( leastDist > DBL_MIN && ++iLoop1 < 8 )
3340 // Find not checked 2-nd node
3341 for ( i = 1; i < 8; i++ )
3342 if ( checkedId1.find( idNodes[i] ) == checkedId1.end() ) {
3343 int id1 = idNodes[i];
3344 swap ( 1, i, idNodes, P );
3345 checkedId1.insert ( id1 );
3349 // Find the 3-d node so that 1-2-3 triangle to be on a hexa face,
3350 // ie that all but meybe one (id3 which is on the same face) nodes
3351 // lay on the same side from the triangle plane.
3353 bool manyInPlane = false; // more than 4 nodes lay in plane
3355 while ( ++iLoop2 < 6 ) {
3357 // get 1-2-3 plane coeffs
3358 Standard_Real A, B, C, D;
3359 gp_Vec N = gp_Vec (P[0], P[1]).Crossed( gp_Vec (P[0], P[2]) );
3360 if ( N.SquareMagnitude() > gp::Resolution() )
3362 gp_Pln pln ( P[0], N );
3363 pln.Coefficients( A, B, C, D );
3365 // find the node (iMin) closest to pln
3366 Standard_Real dist[ 8 ], minDist = DBL_MAX;
3368 for ( i = 3; i < 8; i++ ) {
3369 dist[i] = A * P[i].X() + B * P[i].Y() + C * P[i].Z() + D;
3370 if ( fabs( dist[i] ) < minDist ) {
3371 minDist = fabs( dist[i] );
3374 if ( fabs( dist[i] ) <= tol )
3375 idInPln.insert( idNodes[i] );
3378 // there should not be more than 4 nodes in bottom plane
3379 if ( idInPln.size() > 1 )
3381 DUMPSO( "### idInPln.size() = " << idInPln.size());
3382 // idInPlane does not contain the first 3 nodes
3383 if ( manyInPlane || idInPln.size() == 5)
3384 return false; // all nodes in one plane
3387 // set the 1-st node to be not in plane
3388 for ( i = 3; i < 8; i++ ) {
3389 if ( idInPln.find( idNodes[ i ] ) == idInPln.end() ) {
3390 DUMPSO( "### Reset 0-th node");
3391 swap( 0, i, idNodes, P );
3396 // reset to re-check second nodes
3397 leastDist = DBL_MAX;
3401 break; // from iLoop2;
3404 // check that the other 4 nodes are on the same side
3405 bool sameSide = true;
3406 bool isNeg = dist[ iMin == 3 ? 4 : 3 ] <= 0.;
3407 for ( i = 3; sameSide && i < 8; i++ ) {
3409 sameSide = ( isNeg == dist[i] <= 0.);
3412 // keep best solution
3413 if ( sameSide && minDist < leastDist ) {
3414 leastDist = minDist;
3416 faceNodes.insert( idNodes[ 1 ] );
3417 faceNodes.insert( idNodes[ 2 ] );
3418 faceNodes.insert( idNodes[ iMin ] );
3419 DUMPSO( "loop " << iLoop2 << " id2 " << idNodes[ 1 ] << " id3 " << idNodes[ 2 ]
3420 << " leastDist = " << leastDist);
3421 if ( leastDist <= DBL_MIN )
3426 // set next 3-d node to check
3427 int iNext = 2 + iLoop2;
3429 DUMPSO( "Try 2-nd");
3430 swap ( 2, iNext, idNodes, P );
3432 } // while ( iLoop2 < 6 )
3435 if ( faceNodes.empty() ) return false;
3437 // Put the faceNodes in proper places
3438 for ( i = 4; i < 8; i++ ) {
3439 if ( faceNodes.find( idNodes[ i ] ) != faceNodes.end() ) {
3440 // find a place to put
3442 while ( faceNodes.find( idNodes[ iTo ] ) != faceNodes.end() )
3444 DUMPSO( "Set faceNodes");
3445 swap ( iTo, i, idNodes, P );
3450 // Set nodes of the found bottom face in good order
3451 DUMPSO( " Found bottom face: ");
3452 i = SortQuadNodes( theMesh, idNodes );
3454 gp_Pnt Ptmp = P[ i ];
3459 // for ( int ii = 0; ii < 4; ii++ ) {
3460 // const SMDS_MeshNode *n = theMesh->FindNode( idNodes[ii] );
3461 // DUMPSO( ii << "(" << idNodes[ii] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
3464 // Gravity center of the top and bottom faces
3465 gp_Pnt aGCb = ( P[0].XYZ() + P[1].XYZ() + P[2].XYZ() + P[3].XYZ() ) / 4.;
3466 gp_Pnt aGCt = ( P[4].XYZ() + P[5].XYZ() + P[6].XYZ() + P[7].XYZ() ) / 4.;
3468 // Get direction from the bottom to the top face
3469 gp_Vec upDir ( aGCb, aGCt );
3470 Standard_Real upDirSize = upDir.Magnitude();
3471 if ( upDirSize <= gp::Resolution() ) return false;
3474 // Assure that the bottom face normal points up
3475 gp_Vec Nb = gp_Vec (P[0], P[1]).Crossed( gp_Vec (P[0], P[2]) );
3476 Nb += gp_Vec (P[0], P[2]).Crossed( gp_Vec (P[0], P[3]) );
3477 if ( Nb.Dot( upDir ) < 0 ) {
3478 DUMPSO( "Reverse bottom face");
3479 swap( 1, 3, idNodes, P );
3482 // Find 5-th node - the one closest to the 1-st among the last 4 nodes.
3483 Standard_Real minDist = DBL_MAX;
3484 for ( i = 4; i < 8; i++ ) {
3485 // projection of P[i] to the plane defined by P[0] and upDir
3486 gp_Pnt Pp = P[i].Translated( upDir * ( upDir.Dot( gp_Vec( P[i], P[0] ))));
3487 Standard_Real sqDist = P[0].SquareDistance( Pp );
3488 if ( sqDist < minDist ) {
3493 DUMPSO( "Set 4-th");
3494 swap ( 4, iMin, idNodes, P );
3496 // Set nodes of the top face in good order
3497 DUMPSO( "Sort top face");
3498 i = SortQuadNodes( theMesh, &idNodes[4] );
3501 gp_Pnt Ptmp = P[ i ];
3506 // Assure that direction of the top face normal is from the bottom face
3507 gp_Vec Nt = gp_Vec (P[4], P[5]).Crossed( gp_Vec (P[4], P[6]) );
3508 Nt += gp_Vec (P[4], P[6]).Crossed( gp_Vec (P[4], P[7]) );
3509 if ( Nt.Dot( upDir ) < 0 ) {
3510 DUMPSO( "Reverse top face");
3511 swap( 5, 7, idNodes, P );
3514 // DUMPSO( "OUTPUT: ========================================");
3515 // for ( i = 0; i < 8; i++ ) {
3516 // float *p = ugrid->GetPoint(idNodes[i]);
3517 // DUMPSO( i << "(" << idNodes[i] << ") : " << p[0] << " " << p[1] << " " << p[2]);
3523 //================================================================================
3525 * \brief Return nodes linked to the given one
3526 * \param theNode - the node
3527 * \param linkedNodes - the found nodes
3528 * \param type - the type of elements to check
3530 * Medium nodes are ignored
3532 //================================================================================
3534 void SMESH_MeshEditor::GetLinkedNodes( const SMDS_MeshNode* theNode,
3535 TIDSortedElemSet & linkedNodes,
3536 SMDSAbs_ElementType type )
3538 SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(type);
3539 while ( elemIt->more() )
3541 const SMDS_MeshElement* elem = elemIt->next();
3542 if(elem->GetType() == SMDSAbs_0DElement)
3545 SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
3546 if ( elem->GetType() == SMDSAbs_Volume )
3548 SMDS_VolumeTool vol( elem );
3549 while ( nodeIt->more() ) {
3550 const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
3551 if ( theNode != n && vol.IsLinked( theNode, n ))
3552 linkedNodes.insert( n );
3557 for ( int i = 0; nodeIt->more(); ++i ) {
3558 const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
3559 if ( n == theNode ) {
3560 int iBefore = i - 1;
3562 if ( elem->IsQuadratic() ) {
3563 int nb = elem->NbNodes() / 2;
3564 iAfter = SMESH_MesherHelper::WrapIndex( iAfter, nb );
3565 iBefore = SMESH_MesherHelper::WrapIndex( iBefore, nb );
3567 linkedNodes.insert( elem->GetNodeWrap( iAfter ));
3568 linkedNodes.insert( elem->GetNodeWrap( iBefore ));
3575 //=======================================================================
3576 //function : laplacianSmooth
3577 //purpose : pulls theNode toward the center of surrounding nodes directly
3578 // connected to that node along an element edge
3579 //=======================================================================
3581 void laplacianSmooth(const SMDS_MeshNode* theNode,
3582 const Handle(Geom_Surface)& theSurface,
3583 map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
3585 // find surrounding nodes
3587 TIDSortedElemSet nodeSet;
3588 SMESH_MeshEditor::GetLinkedNodes( theNode, nodeSet, SMDSAbs_Face );
3590 // compute new coodrs
3592 double coord[] = { 0., 0., 0. };
3593 TIDSortedElemSet::iterator nodeSetIt = nodeSet.begin();
3594 for ( ; nodeSetIt != nodeSet.end(); nodeSetIt++ ) {
3595 const SMDS_MeshNode* node = cast2Node(*nodeSetIt);
3596 if ( theSurface.IsNull() ) { // smooth in 3D
3597 coord[0] += node->X();
3598 coord[1] += node->Y();
3599 coord[2] += node->Z();
3601 else { // smooth in 2D
3602 ASSERT( theUVMap.find( node ) != theUVMap.end() );
3603 gp_XY* uv = theUVMap[ node ];
3604 coord[0] += uv->X();
3605 coord[1] += uv->Y();
3608 int nbNodes = nodeSet.size();
3611 coord[0] /= nbNodes;
3612 coord[1] /= nbNodes;
3614 if ( !theSurface.IsNull() ) {
3615 ASSERT( theUVMap.find( theNode ) != theUVMap.end() );
3616 theUVMap[ theNode ]->SetCoord( coord[0], coord[1] );
3617 gp_Pnt p3d = theSurface->Value( coord[0], coord[1] );
3623 coord[2] /= nbNodes;
3627 const_cast< SMDS_MeshNode* >( theNode )->setXYZ(coord[0],coord[1],coord[2]);
3630 //=======================================================================
3631 //function : centroidalSmooth
3632 //purpose : pulls theNode toward the element-area-weighted centroid of the
3633 // surrounding elements
3634 //=======================================================================
3636 void centroidalSmooth(const SMDS_MeshNode* theNode,
3637 const Handle(Geom_Surface)& theSurface,
3638 map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
3640 gp_XYZ aNewXYZ(0.,0.,0.);
3641 SMESH::Controls::Area anAreaFunc;
3642 double totalArea = 0.;
3647 SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(SMDSAbs_Face);
3648 while ( elemIt->more() )
3650 const SMDS_MeshElement* elem = elemIt->next();
3653 gp_XYZ elemCenter(0.,0.,0.);
3654 SMESH::Controls::TSequenceOfXYZ aNodePoints;
3655 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3656 int nn = elem->NbNodes();
3657 if(elem->IsQuadratic()) nn = nn/2;
3659 //while ( itN->more() ) {
3661 const SMDS_MeshNode* aNode = static_cast<const SMDS_MeshNode*>( itN->next() );
3663 gp_XYZ aP( aNode->X(), aNode->Y(), aNode->Z() );
3664 aNodePoints.push_back( aP );
3665 if ( !theSurface.IsNull() ) { // smooth in 2D
3666 ASSERT( theUVMap.find( aNode ) != theUVMap.end() );
3667 gp_XY* uv = theUVMap[ aNode ];
3668 aP.SetCoord( uv->X(), uv->Y(), 0. );
3672 double elemArea = anAreaFunc.GetValue( aNodePoints );
3673 totalArea += elemArea;
3675 aNewXYZ += elemCenter * elemArea;
3677 aNewXYZ /= totalArea;
3678 if ( !theSurface.IsNull() ) {
3679 theUVMap[ theNode ]->SetCoord( aNewXYZ.X(), aNewXYZ.Y() );
3680 aNewXYZ = theSurface->Value( aNewXYZ.X(), aNewXYZ.Y() ).XYZ();
3685 const_cast< SMDS_MeshNode* >( theNode )->setXYZ(aNewXYZ.X(),aNewXYZ.Y(),aNewXYZ.Z());
3688 //=======================================================================
3689 //function : getClosestUV
3690 //purpose : return UV of closest projection
3691 //=======================================================================
3693 static bool getClosestUV (Extrema_GenExtPS& projector,
3694 const gp_Pnt& point,
3697 projector.Perform( point );
3698 if ( projector.IsDone() ) {
3699 double u, v, minVal = DBL_MAX;
3700 for ( int i = projector.NbExt(); i > 0; i-- )
3701 if ( projector.SquareDistance( i ) < minVal ) {
3702 minVal = projector.SquareDistance( i );
3703 projector.Point( i ).Parameter( u, v );
3705 result.SetCoord( u, v );
3711 //=======================================================================
3713 //purpose : Smooth theElements during theNbIterations or until a worst
3714 // element has aspect ratio <= theTgtAspectRatio.
3715 // Aspect Ratio varies in range [1.0, inf].
3716 // If theElements is empty, the whole mesh is smoothed.
3717 // theFixedNodes contains additionally fixed nodes. Nodes built
3718 // on edges and boundary nodes are always fixed.
3719 //=======================================================================
3721 void SMESH_MeshEditor::Smooth (TIDSortedElemSet & theElems,
3722 set<const SMDS_MeshNode*> & theFixedNodes,
3723 const SmoothMethod theSmoothMethod,
3724 const int theNbIterations,
3725 double theTgtAspectRatio,
3728 myLastCreatedElems.Clear();
3729 myLastCreatedNodes.Clear();
3731 MESSAGE((theSmoothMethod==LAPLACIAN ? "LAPLACIAN" : "CENTROIDAL") << "--::Smooth()");
3733 if ( theTgtAspectRatio < 1.0 )
3734 theTgtAspectRatio = 1.0;
3736 const double disttol = 1.e-16;
3738 SMESH::Controls::AspectRatio aQualityFunc;
3740 SMESHDS_Mesh* aMesh = GetMeshDS();
3742 if ( theElems.empty() ) {
3743 // add all faces to theElems
3744 SMDS_FaceIteratorPtr fIt = aMesh->facesIterator();
3745 while ( fIt->more() ) {
3746 const SMDS_MeshElement* face = fIt->next();
3747 theElems.insert( theElems.end(), face );
3750 // get all face ids theElems are on
3751 set< int > faceIdSet;
3752 TIDSortedElemSet::iterator itElem;
3754 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
3755 int fId = FindShape( *itElem );
3756 // check that corresponding submesh exists and a shape is face
3758 faceIdSet.find( fId ) == faceIdSet.end() &&
3759 aMesh->MeshElements( fId )) {
3760 TopoDS_Shape F = aMesh->IndexToShape( fId );
3761 if ( !F.IsNull() && F.ShapeType() == TopAbs_FACE )
3762 faceIdSet.insert( fId );
3765 faceIdSet.insert( 0 ); // to smooth elements that are not on any TopoDS_Face
3767 // ===============================================
3768 // smooth elements on each TopoDS_Face separately
3769 // ===============================================
3771 set< int >::reverse_iterator fId = faceIdSet.rbegin(); // treate 0 fId at the end
3772 for ( ; fId != faceIdSet.rend(); ++fId ) {
3773 // get face surface and submesh
3774 Handle(Geom_Surface) surface;
3775 SMESHDS_SubMesh* faceSubMesh = 0;
3777 double fToler2 = 0, f,l;
3778 double u1 = 0, u2 = 0, v1 = 0, v2 = 0;
3779 bool isUPeriodic = false, isVPeriodic = false;
3781 face = TopoDS::Face( aMesh->IndexToShape( *fId ));
3782 surface = BRep_Tool::Surface( face );
3783 faceSubMesh = aMesh->MeshElements( *fId );
3784 fToler2 = BRep_Tool::Tolerance( face );
3785 fToler2 *= fToler2 * 10.;
3786 isUPeriodic = surface->IsUPeriodic();
3789 isVPeriodic = surface->IsVPeriodic();
3792 surface->Bounds( u1, u2, v1, v2 );
3794 // ---------------------------------------------------------
3795 // for elements on a face, find movable and fixed nodes and
3796 // compute UV for them
3797 // ---------------------------------------------------------
3798 bool checkBoundaryNodes = false;
3799 bool isQuadratic = false;
3800 set<const SMDS_MeshNode*> setMovableNodes;
3801 map< const SMDS_MeshNode*, gp_XY* > uvMap, uvMap2;
3802 list< gp_XY > listUV; // uvs the 2 uvMaps refer to
3803 list< const SMDS_MeshElement* > elemsOnFace;
3805 Extrema_GenExtPS projector;
3806 GeomAdaptor_Surface surfAdaptor;
3807 if ( !surface.IsNull() ) {
3808 surfAdaptor.Load( surface );
3809 projector.Initialize( surfAdaptor, 20,20, 1e-5,1e-5 );
3811 int nbElemOnFace = 0;
3812 itElem = theElems.begin();
3813 // loop on not yet smoothed elements: look for elems on a face
3814 while ( itElem != theElems.end() ) {
3815 if ( faceSubMesh && nbElemOnFace == faceSubMesh->NbElements() )
3816 break; // all elements found
3818 const SMDS_MeshElement* elem = *itElem;
3819 if ( !elem || elem->GetType() != SMDSAbs_Face || elem->NbNodes() < 3 ||
3820 ( faceSubMesh && !faceSubMesh->Contains( elem ))) {
3824 elemsOnFace.push_back( elem );
3825 theElems.erase( itElem++ );
3829 isQuadratic = elem->IsQuadratic();
3831 // get movable nodes of elem
3832 const SMDS_MeshNode* node;
3833 SMDS_TypeOfPosition posType;
3834 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3835 int nn = 0, nbn = elem->NbNodes();
3836 if(elem->IsQuadratic())
3838 while ( nn++ < nbn ) {
3839 node = static_cast<const SMDS_MeshNode*>( itN->next() );
3840 const SMDS_PositionPtr& pos = node->GetPosition();
3841 posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
3842 if (posType != SMDS_TOP_EDGE &&
3843 posType != SMDS_TOP_VERTEX &&
3844 theFixedNodes.find( node ) == theFixedNodes.end())
3846 // check if all faces around the node are on faceSubMesh
3847 // because a node on edge may be bound to face
3848 SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
3850 if ( faceSubMesh ) {
3851 while ( eIt->more() && all ) {
3852 const SMDS_MeshElement* e = eIt->next();
3853 all = faceSubMesh->Contains( e );
3857 setMovableNodes.insert( node );
3859 checkBoundaryNodes = true;
3861 if ( posType == SMDS_TOP_3DSPACE )
3862 checkBoundaryNodes = true;
3865 if ( surface.IsNull() )
3868 // get nodes to check UV
3869 list< const SMDS_MeshNode* > uvCheckNodes;
3870 itN = elem->nodesIterator();
3871 nn = 0; nbn = elem->NbNodes();
3872 if(elem->IsQuadratic())
3874 while ( nn++ < nbn ) {
3875 node = static_cast<const SMDS_MeshNode*>( itN->next() );
3876 if ( uvMap.find( node ) == uvMap.end() )
3877 uvCheckNodes.push_back( node );
3878 // add nodes of elems sharing node
3879 // SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
3880 // while ( eIt->more() ) {
3881 // const SMDS_MeshElement* e = eIt->next();
3882 // if ( e != elem ) {
3883 // SMDS_ElemIteratorPtr nIt = e->nodesIterator();
3884 // while ( nIt->more() ) {
3885 // const SMDS_MeshNode* n =
3886 // static_cast<const SMDS_MeshNode*>( nIt->next() );
3887 // if ( uvMap.find( n ) == uvMap.end() )
3888 // uvCheckNodes.push_back( n );
3894 list< const SMDS_MeshNode* >::iterator n = uvCheckNodes.begin();
3895 for ( ; n != uvCheckNodes.end(); ++n ) {
3898 const SMDS_PositionPtr& pos = node->GetPosition();
3899 posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
3901 switch ( posType ) {
3902 case SMDS_TOP_FACE: {
3903 SMDS_FacePosition* fPos = ( SMDS_FacePosition* ) pos;
3904 uv.SetCoord( fPos->GetUParameter(), fPos->GetVParameter() );
3907 case SMDS_TOP_EDGE: {
3908 TopoDS_Shape S = aMesh->IndexToShape( node->getshapeId() );
3909 Handle(Geom2d_Curve) pcurve;
3910 if ( !S.IsNull() && S.ShapeType() == TopAbs_EDGE )
3911 pcurve = BRep_Tool::CurveOnSurface( TopoDS::Edge( S ), face, f,l );
3912 if ( !pcurve.IsNull() ) {
3913 double u = (( SMDS_EdgePosition* ) pos )->GetUParameter();
3914 uv = pcurve->Value( u ).XY();
3918 case SMDS_TOP_VERTEX: {
3919 TopoDS_Shape S = aMesh->IndexToShape( node->getshapeId() );
3920 if ( !S.IsNull() && S.ShapeType() == TopAbs_VERTEX )
3921 uv = BRep_Tool::Parameters( TopoDS::Vertex( S ), face ).XY();
3926 // check existing UV
3927 bool project = true;
3928 gp_Pnt pNode ( node->X(), node->Y(), node->Z() );
3929 double dist1 = DBL_MAX, dist2 = 0;
3930 if ( posType != SMDS_TOP_3DSPACE ) {
3931 dist1 = pNode.SquareDistance( surface->Value( uv.X(), uv.Y() ));
3932 project = dist1 > fToler2;
3934 if ( project ) { // compute new UV
3936 if ( !getClosestUV( projector, pNode, newUV )) {
3937 MESSAGE("Node Projection Failed " << node);
3941 newUV.SetX( ElCLib::InPeriod( newUV.X(), u1, u2 ));
3943 newUV.SetY( ElCLib::InPeriod( newUV.Y(), v1, v2 ));
3945 if ( posType != SMDS_TOP_3DSPACE )
3946 dist2 = pNode.SquareDistance( surface->Value( newUV.X(), newUV.Y() ));
3947 if ( dist2 < dist1 )
3951 // store UV in the map
3952 listUV.push_back( uv );
3953 uvMap.insert( make_pair( node, &listUV.back() ));
3955 } // loop on not yet smoothed elements
3957 if ( !faceSubMesh || nbElemOnFace != faceSubMesh->NbElements() )
3958 checkBoundaryNodes = true;
3960 // fix nodes on mesh boundary
3962 if ( checkBoundaryNodes ) {
3963 map< SMESH_TLink, int > linkNbMap; // how many times a link encounters in elemsOnFace
3964 map< SMESH_TLink, int >::iterator link_nb;
3965 // put all elements links to linkNbMap
3966 list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
3967 for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
3968 const SMDS_MeshElement* elem = (*elemIt);
3969 int nbn = elem->NbCornerNodes();
3970 // loop on elem links: insert them in linkNbMap
3971 for ( int iN = 0; iN < nbn; ++iN ) {
3972 const SMDS_MeshNode* n1 = elem->GetNode( iN );
3973 const SMDS_MeshNode* n2 = elem->GetNode(( iN+1 ) % nbn);
3974 SMESH_TLink link( n1, n2 );
3975 link_nb = linkNbMap.insert( make_pair( link, 0 )).first;
3979 // remove nodes that are in links encountered only once from setMovableNodes
3980 for ( link_nb = linkNbMap.begin(); link_nb != linkNbMap.end(); ++link_nb ) {
3981 if ( link_nb->second == 1 ) {
3982 setMovableNodes.erase( link_nb->first.node1() );
3983 setMovableNodes.erase( link_nb->first.node2() );
3988 // -----------------------------------------------------
3989 // for nodes on seam edge, compute one more UV ( uvMap2 );
3990 // find movable nodes linked to nodes on seam and which
3991 // are to be smoothed using the second UV ( uvMap2 )
3992 // -----------------------------------------------------
3994 set<const SMDS_MeshNode*> nodesNearSeam; // to smooth using uvMap2
3995 if ( !surface.IsNull() ) {
3996 TopExp_Explorer eExp( face, TopAbs_EDGE );
3997 for ( ; eExp.More(); eExp.Next() ) {
3998 TopoDS_Edge edge = TopoDS::Edge( eExp.Current() );
3999 if ( !BRep_Tool::IsClosed( edge, face ))
4001 SMESHDS_SubMesh* sm = aMesh->MeshElements( edge );
4002 if ( !sm ) continue;
4003 // find out which parameter varies for a node on seam
4006 Handle(Geom2d_Curve) pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
4007 if ( pcurve.IsNull() ) continue;
4008 uv1 = pcurve->Value( f );
4010 pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
4011 if ( pcurve.IsNull() ) continue;
4012 uv2 = pcurve->Value( f );
4013 int iPar = Abs( uv1.X() - uv2.X() ) > Abs( uv1.Y() - uv2.Y() ) ? 1 : 2;
4015 if ( uv1.Coord( iPar ) > uv2.Coord( iPar )) {
4016 gp_Pnt2d tmp = uv1; uv1 = uv2; uv2 = tmp;
4018 // get nodes on seam and its vertices
4019 list< const SMDS_MeshNode* > seamNodes;
4020 SMDS_NodeIteratorPtr nSeamIt = sm->GetNodes();
4021 while ( nSeamIt->more() ) {
4022 const SMDS_MeshNode* node = nSeamIt->next();
4023 if ( !isQuadratic || !IsMedium( node ))
4024 seamNodes.push_back( node );
4026 TopExp_Explorer vExp( edge, TopAbs_VERTEX );
4027 for ( ; vExp.More(); vExp.Next() ) {
4028 sm = aMesh->MeshElements( vExp.Current() );
4030 nSeamIt = sm->GetNodes();
4031 while ( nSeamIt->more() )
4032 seamNodes.push_back( nSeamIt->next() );
4035 // loop on nodes on seam
4036 list< const SMDS_MeshNode* >::iterator noSeIt = seamNodes.begin();
4037 for ( ; noSeIt != seamNodes.end(); ++noSeIt ) {
4038 const SMDS_MeshNode* nSeam = *noSeIt;
4039 map< const SMDS_MeshNode*, gp_XY* >::iterator n_uv = uvMap.find( nSeam );
4040 if ( n_uv == uvMap.end() )
4043 n_uv->second->SetCoord( iPar, uv1.Coord( iPar ));
4044 // set the second UV
4045 listUV.push_back( *n_uv->second );
4046 listUV.back().SetCoord( iPar, uv2.Coord( iPar ));
4047 if ( uvMap2.empty() )
4048 uvMap2 = uvMap; // copy the uvMap contents
4049 uvMap2[ nSeam ] = &listUV.back();
4051 // collect movable nodes linked to ones on seam in nodesNearSeam
4052 SMDS_ElemIteratorPtr eIt = nSeam->GetInverseElementIterator(SMDSAbs_Face);
4053 while ( eIt->more() ) {
4054 const SMDS_MeshElement* e = eIt->next();
4055 int nbUseMap1 = 0, nbUseMap2 = 0;
4056 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
4057 int nn = 0, nbn = e->NbNodes();
4058 if(e->IsQuadratic()) nbn = nbn/2;
4059 while ( nn++ < nbn )
4061 const SMDS_MeshNode* n =
4062 static_cast<const SMDS_MeshNode*>( nIt->next() );
4064 setMovableNodes.find( n ) == setMovableNodes.end() )
4066 // add only nodes being closer to uv2 than to uv1
4067 gp_Pnt pMid (0.5 * ( n->X() + nSeam->X() ),
4068 0.5 * ( n->Y() + nSeam->Y() ),
4069 0.5 * ( n->Z() + nSeam->Z() ));
4071 getClosestUV( projector, pMid, uv );
4072 if ( uv.Coord( iPar ) > uvMap[ n ]->Coord( iPar ) ) {
4073 nodesNearSeam.insert( n );
4079 // for centroidalSmooth all element nodes must
4080 // be on one side of a seam
4081 if ( theSmoothMethod == CENTROIDAL && nbUseMap1 && nbUseMap2 ) {
4082 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
4084 while ( nn++ < nbn ) {
4085 const SMDS_MeshNode* n =
4086 static_cast<const SMDS_MeshNode*>( nIt->next() );
4087 setMovableNodes.erase( n );
4091 } // loop on nodes on seam
4092 } // loop on edge of a face
4093 } // if ( !face.IsNull() )
4095 if ( setMovableNodes.empty() ) {
4096 MESSAGE( "Face id : " << *fId << " - NO SMOOTHING: no nodes to move!!!");
4097 continue; // goto next face
4105 double maxRatio = -1., maxDisplacement = -1.;
4106 set<const SMDS_MeshNode*>::iterator nodeToMove;
4107 for ( it = 0; it < theNbIterations; it++ ) {
4108 maxDisplacement = 0.;
4109 nodeToMove = setMovableNodes.begin();
4110 for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
4111 const SMDS_MeshNode* node = (*nodeToMove);
4112 gp_XYZ aPrevPos ( node->X(), node->Y(), node->Z() );
4115 bool map2 = ( nodesNearSeam.find( node ) != nodesNearSeam.end() );
4116 if ( theSmoothMethod == LAPLACIAN )
4117 laplacianSmooth( node, surface, map2 ? uvMap2 : uvMap );
4119 centroidalSmooth( node, surface, map2 ? uvMap2 : uvMap );
4121 // node displacement
4122 gp_XYZ aNewPos ( node->X(), node->Y(), node->Z() );
4123 Standard_Real aDispl = (aPrevPos - aNewPos).SquareModulus();
4124 if ( aDispl > maxDisplacement )
4125 maxDisplacement = aDispl;
4127 // no node movement => exit
4128 //if ( maxDisplacement < 1.e-16 ) {
4129 if ( maxDisplacement < disttol ) {
4130 MESSAGE("-- no node movement --");
4134 // check elements quality
4136 list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4137 for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
4138 const SMDS_MeshElement* elem = (*elemIt);
4139 if ( !elem || elem->GetType() != SMDSAbs_Face )
4141 SMESH::Controls::TSequenceOfXYZ aPoints;
4142 if ( aQualityFunc.GetPoints( elem, aPoints )) {
4143 double aValue = aQualityFunc.GetValue( aPoints );
4144 if ( aValue > maxRatio )
4148 if ( maxRatio <= theTgtAspectRatio ) {
4149 MESSAGE("-- quality achived --");
4152 if (it+1 == theNbIterations) {
4153 MESSAGE("-- Iteration limit exceeded --");
4155 } // smoothing iterations
4157 MESSAGE(" Face id: " << *fId <<
4158 " Nb iterstions: " << it <<
4159 " Displacement: " << maxDisplacement <<
4160 " Aspect Ratio " << maxRatio);
4162 // ---------------------------------------
4163 // new nodes positions are computed,
4164 // record movement in DS and set new UV
4165 // ---------------------------------------
4166 nodeToMove = setMovableNodes.begin();
4167 for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
4168 SMDS_MeshNode* node = const_cast< SMDS_MeshNode* > (*nodeToMove);
4169 aMesh->MoveNode( node, node->X(), node->Y(), node->Z() );
4170 map< const SMDS_MeshNode*, gp_XY* >::iterator node_uv = uvMap.find( node );
4171 if ( node_uv != uvMap.end() ) {
4172 gp_XY* uv = node_uv->second;
4174 ( SMDS_PositionPtr( new SMDS_FacePosition( uv->X(), uv->Y() )));
4178 // move medium nodes of quadratic elements
4181 SMESH_MesherHelper helper( *GetMesh() );
4182 helper.SetSubShape( face );
4183 vector<const SMDS_MeshNode*> nodes;
4185 list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4186 for ( ; elemIt != elemsOnFace.end(); ++elemIt )
4188 const SMDS_MeshElement* QF = *elemIt;
4189 if ( QF->IsQuadratic() )
4191 nodes.assign( SMDS_MeshElement::iterator( QF->interlacedNodesElemIterator() ),
4192 SMDS_MeshElement::iterator() );
4193 nodes.push_back( nodes[0] );
4195 for (size_t i = 1; i < nodes.size(); i += 2 ) // i points to a medium node
4197 if ( !surface.IsNull() )
4199 gp_XY uv1 = helper.GetNodeUV( face, nodes[i-1], nodes[i+1], &checkUV );
4200 gp_XY uv2 = helper.GetNodeUV( face, nodes[i+1], nodes[i-1], &checkUV );
4201 gp_XY uv = helper.GetMiddleUV( surface, uv1, uv2 );
4202 xyz = surface->Value( uv.X(), uv.Y() );
4205 xyz = 0.5 * ( SMESH_TNodeXYZ( nodes[i-1] ) + SMESH_TNodeXYZ( nodes[i+1] ));
4207 if (( SMESH_TNodeXYZ( nodes[i] ) - xyz.XYZ() ).Modulus() > disttol )
4208 // we have to move a medium node
4209 aMesh->MoveNode( nodes[i], xyz.X(), xyz.Y(), xyz.Z() );
4215 } // loop on face ids
4219 //=======================================================================
4220 //function : isReverse
4221 //purpose : Return true if normal of prevNodes is not co-directied with
4222 // gp_Vec(prevNodes[iNotSame],nextNodes[iNotSame]).
4223 // iNotSame is where prevNodes and nextNodes are different.
4224 // If result is true then future volume orientation is OK
4225 //=======================================================================
4227 static bool isReverse(const SMDS_MeshElement* face,
4228 const vector<const SMDS_MeshNode*>& prevNodes,
4229 const vector<const SMDS_MeshNode*>& nextNodes,
4233 SMESH_TNodeXYZ pP = prevNodes[ iNotSame ];
4234 SMESH_TNodeXYZ pN = nextNodes[ iNotSame ];
4235 gp_XYZ extrDir( pN - pP ), faceNorm;
4236 SMESH_MeshAlgos::FaceNormal( face, faceNorm, /*normalized=*/false );
4238 return faceNorm * extrDir < 0.0;
4241 //=======================================================================
4243 * \brief Create elements by sweeping an element
4244 * \param elem - element to sweep
4245 * \param newNodesItVec - nodes generated from each node of the element
4246 * \param newElems - generated elements
4247 * \param nbSteps - number of sweeping steps
4248 * \param srcElements - to append elem for each generated element
4250 //=======================================================================
4252 void SMESH_MeshEditor::sweepElement(const SMDS_MeshElement* elem,
4253 const vector<TNodeOfNodeListMapItr> & newNodesItVec,
4254 list<const SMDS_MeshElement*>& newElems,
4256 SMESH_SequenceOfElemPtr& srcElements)
4258 //MESSAGE("sweepElement " << nbSteps);
4259 SMESHDS_Mesh* aMesh = GetMeshDS();
4261 const int nbNodes = elem->NbNodes();
4262 const int nbCorners = elem->NbCornerNodes();
4263 SMDSAbs_EntityType baseType = elem->GetEntityType(); /* it can change in case of
4264 polyhedron creation !!! */
4265 // Loop on elem nodes:
4266 // find new nodes and detect same nodes indices
4267 vector < list< const SMDS_MeshNode* >::const_iterator > itNN( nbNodes );
4268 vector<const SMDS_MeshNode*> prevNod( nbNodes );
4269 vector<const SMDS_MeshNode*> nextNod( nbNodes );
4270 vector<const SMDS_MeshNode*> midlNod( nbNodes );
4272 int iNode, nbSame = 0, nbDouble = 0, iNotSameNode = 0;
4273 vector<int> sames(nbNodes);
4274 vector<bool> isSingleNode(nbNodes);
4276 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4277 TNodeOfNodeListMapItr nnIt = newNodesItVec[ iNode ];
4278 const SMDS_MeshNode* node = nnIt->first;
4279 const list< const SMDS_MeshNode* > & listNewNodes = nnIt->second;
4280 if ( listNewNodes.empty() )
4283 itNN [ iNode ] = listNewNodes.begin();
4284 prevNod[ iNode ] = node;
4285 nextNod[ iNode ] = listNewNodes.front();
4287 isSingleNode[iNode] = (listNewNodes.size()==nbSteps); /* medium node of quadratic or
4288 corner node of linear */
4289 if ( prevNod[ iNode ] != nextNod [ iNode ])
4290 nbDouble += !isSingleNode[iNode];
4292 if( iNode < nbCorners ) { // check corners only
4293 if ( prevNod[ iNode ] == nextNod [ iNode ])
4294 sames[nbSame++] = iNode;
4296 iNotSameNode = iNode;
4300 if ( nbSame == nbNodes || nbSame > 2) {
4301 MESSAGE( " Too many same nodes of element " << elem->GetID() );
4305 if ( elem->GetType() == SMDSAbs_Face && !isReverse( elem, prevNod, nextNod, iNotSameNode ))
4307 // fix nodes order to have bottom normal external
4308 if ( baseType == SMDSEntity_Polygon )
4310 std::reverse( itNN.begin(), itNN.end() );
4311 std::reverse( prevNod.begin(), prevNod.end() );
4312 std::reverse( midlNod.begin(), midlNod.end() );
4313 std::reverse( nextNod.begin(), nextNod.end() );
4314 std::reverse( isSingleNode.begin(), isSingleNode.end() );
4318 const vector<int>& ind = SMDS_MeshCell::reverseSmdsOrder( baseType );
4319 SMDS_MeshCell::applyInterlace( ind, itNN );
4320 SMDS_MeshCell::applyInterlace( ind, prevNod );
4321 SMDS_MeshCell::applyInterlace( ind, nextNod );
4322 SMDS_MeshCell::applyInterlace( ind, midlNod );
4323 SMDS_MeshCell::applyInterlace( ind, isSingleNode );
4326 sames[nbSame] = iNotSameNode;
4327 for ( int j = 0; j <= nbSame; ++j )
4328 for ( size_t i = 0; i < ind.size(); ++i )
4329 if ( ind[i] == sames[j] )
4334 iNotSameNode = sames[nbSame];
4338 else if ( elem->GetType() == SMDSAbs_Edge )
4340 // orient a new face same as adjacent one
4342 const SMDS_MeshElement* e;
4343 TIDSortedElemSet dummy;
4344 if (( e = SMESH_MeshAlgos::FindFaceInSet( nextNod[0], prevNod[0], dummy,dummy, &i1, &i2 )) ||
4345 ( e = SMESH_MeshAlgos::FindFaceInSet( prevNod[1], nextNod[1], dummy,dummy, &i1, &i2 )) ||
4346 ( e = SMESH_MeshAlgos::FindFaceInSet( prevNod[0], prevNod[1], dummy,dummy, &i1, &i2 )))
4348 // there is an adjacent face, check order of nodes in it
4349 bool sameOrder = ( Abs( i2 - i1 ) == 1 ) ? ( i2 > i1 ) : ( i2 < i1 );
4352 std::swap( itNN[0], itNN[1] );
4353 std::swap( prevNod[0], prevNod[1] );
4354 std::swap( nextNod[0], nextNod[1] );
4356 sames[0] = 1 - sames[0];
4357 iNotSameNode = 1 - iNotSameNode;
4362 int iSameNode = 0, iBeforeSame = 0, iAfterSame = 0, iOpposSame = 0;
4364 iSameNode = sames[ nbSame-1 ];
4365 iBeforeSame = ( iSameNode + nbCorners - 1 ) % nbCorners;
4366 iAfterSame = ( iSameNode + 1 ) % nbCorners;
4367 iOpposSame = ( iSameNode - 2 < 0 ? iSameNode + 2 : iSameNode - 2 );
4370 // make new elements
4371 for (int iStep = 0; iStep < nbSteps; iStep++ )
4374 for ( iNode = 0; iNode < nbNodes; iNode++ )
4376 midlNod[ iNode ] = isSingleNode[iNode] ? 0 : *itNN[ iNode ]++;
4377 nextNod[ iNode ] = *itNN[ iNode ]++;
4380 SMDS_MeshElement* aNewElem = 0;
4381 /*if(!elem->IsPoly())*/ {
4382 switch ( baseType ) {
4384 case SMDSEntity_Node: { // sweep NODE
4385 if ( nbSame == 0 ) {
4386 if ( isSingleNode[0] )
4387 aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ] );
4389 aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ], midlNod[ 0 ] );
4395 case SMDSEntity_Edge: { // sweep EDGE
4396 if ( nbDouble == 0 )
4398 if ( nbSame == 0 ) // ---> quadrangle
4399 aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
4400 nextNod[ 1 ], nextNod[ 0 ] );
4401 else // ---> triangle
4402 aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
4403 nextNod[ iNotSameNode ] );
4405 else // ---> polygon
4407 vector<const SMDS_MeshNode*> poly_nodes;
4408 poly_nodes.push_back( prevNod[0] );
4409 poly_nodes.push_back( prevNod[1] );
4410 if ( prevNod[1] != nextNod[1] )
4412 if ( midlNod[1]) poly_nodes.push_back( midlNod[1]);
4413 poly_nodes.push_back( nextNod[1] );
4415 if ( prevNod[0] != nextNod[0] )
4417 poly_nodes.push_back( nextNod[0] );
4418 if ( midlNod[0]) poly_nodes.push_back( midlNod[0]);
4420 switch ( poly_nodes.size() ) {
4422 aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ], poly_nodes[ 2 ]);
4425 aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ],
4426 poly_nodes[ 2 ], poly_nodes[ 3 ]);
4429 aNewElem = aMesh->AddPolygonalFace (poly_nodes);
4434 case SMDSEntity_Triangle: // TRIANGLE --->
4436 if ( nbDouble > 0 ) break;
4437 if ( nbSame == 0 ) // ---> pentahedron
4438 aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
4439 nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ] );
4441 else if ( nbSame == 1 ) // ---> pyramid
4442 aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
4443 nextNod[ iAfterSame ], nextNod[ iBeforeSame ],
4444 nextNod[ iSameNode ]);
4446 else // 2 same nodes: ---> tetrahedron
4447 aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
4448 nextNod[ iNotSameNode ]);
4451 case SMDSEntity_Quad_Edge: // sweep quadratic EDGE --->
4455 if ( nbDouble+nbSame == 2 )
4457 if(nbSame==0) { // ---> quadratic quadrangle
4458 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
4459 prevNod[2], midlNod[1], nextNod[2], midlNod[0]);
4461 else { //(nbSame==1) // ---> quadratic triangle
4463 return; // medium node on axis
4465 else if(sames[0]==0)
4466 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1],
4467 prevNod[2], midlNod[1], nextNod[2] );
4469 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[0],
4470 prevNod[2], nextNod[2], midlNod[0]);
4473 else if ( nbDouble == 3 )
4475 if ( nbSame == 0 ) { // ---> bi-quadratic quadrangle
4476 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
4477 prevNod[2], midlNod[1], nextNod[2], midlNod[0], midlNod[2]);
4484 case SMDSEntity_Quadrangle: { // sweep QUADRANGLE --->
4485 if ( nbDouble > 0 ) break;
4487 if ( nbSame == 0 ) // ---> hexahedron
4488 aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ], prevNod[ 3 ],
4489 nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ], nextNod[ 3 ]);
4491 else if ( nbSame == 1 ) { // ---> pyramid + pentahedron
4492 aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
4493 nextNod[ iAfterSame ], nextNod[ iBeforeSame ],
4494 nextNod[ iSameNode ]);
4495 newElems.push_back( aNewElem );
4496 aNewElem = aMesh->AddVolume (prevNod[ iAfterSame ], prevNod[ iOpposSame ],
4497 prevNod[ iBeforeSame ], nextNod[ iAfterSame ],
4498 nextNod[ iOpposSame ], nextNod[ iBeforeSame ] );
4500 else if ( nbSame == 2 ) { // ---> pentahedron
4501 if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] )
4502 // iBeforeSame is same too
4503 aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iOpposSame ],
4504 nextNod[ iOpposSame ], prevNod[ iSameNode ],
4505 prevNod[ iAfterSame ], nextNod[ iAfterSame ]);
4507 // iAfterSame is same too
4508 aNewElem = aMesh->AddVolume (prevNod[ iSameNode ], prevNod[ iBeforeSame ],
4509 nextNod[ iBeforeSame ], prevNod[ iAfterSame ],
4510 prevNod[ iOpposSame ], nextNod[ iOpposSame ]);
4514 case SMDSEntity_Quad_Triangle: // sweep (Bi)Quadratic TRIANGLE --->
4515 case SMDSEntity_BiQuad_Triangle: /* ??? */ {
4516 if ( nbDouble+nbSame != 3 ) break;
4518 // ---> pentahedron with 15 nodes
4519 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
4520 nextNod[0], nextNod[1], nextNod[2],
4521 prevNod[3], prevNod[4], prevNod[5],
4522 nextNod[3], nextNod[4], nextNod[5],
4523 midlNod[0], midlNod[1], midlNod[2]);
4525 else if(nbSame==1) {
4526 // ---> 2d order pyramid of 13 nodes
4527 int apex = iSameNode;
4528 int i0 = ( apex + 1 ) % nbCorners;
4529 int i1 = ( apex - 1 + nbCorners ) % nbCorners;
4533 aNewElem = aMesh->AddVolume(prevNod[i1], prevNod[i0],
4534 nextNod[i0], nextNod[i1], prevNod[apex],
4535 prevNod[i01], midlNod[i0],
4536 nextNod[i01], midlNod[i1],
4537 prevNod[i1a], prevNod[i0a],
4538 nextNod[i0a], nextNod[i1a]);
4540 else if(nbSame==2) {
4541 // ---> 2d order tetrahedron of 10 nodes
4542 int n1 = iNotSameNode;
4543 int n2 = ( n1 + 1 ) % nbCorners;
4544 int n3 = ( n1 + nbCorners - 1 ) % nbCorners;
4548 aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], prevNod[n3], nextNod[n1],
4549 prevNod[n12], prevNod[n23], prevNod[n31],
4550 midlNod[n1], nextNod[n12], nextNod[n31]);
4554 case SMDSEntity_Quad_Quadrangle: { // sweep Quadratic QUADRANGLE --->
4556 if ( nbDouble != 4 ) break;
4557 // ---> hexahedron with 20 nodes
4558 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
4559 nextNod[0], nextNod[1], nextNod[2], nextNod[3],
4560 prevNod[4], prevNod[5], prevNod[6], prevNod[7],
4561 nextNod[4], nextNod[5], nextNod[6], nextNod[7],
4562 midlNod[0], midlNod[1], midlNod[2], midlNod[3]);
4564 else if(nbSame==1) {
4565 // ---> pyramid + pentahedron - can not be created since it is needed
4566 // additional middle node at the center of face
4567 INFOS( " Sweep for face " << elem->GetID() << " can not be created" );
4570 else if( nbSame == 2 ) {
4571 if ( nbDouble != 2 ) break;
4572 // ---> 2d order Pentahedron with 15 nodes
4574 if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] ) {
4575 // iBeforeSame is same too
4582 // iAfterSame is same too
4592 aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], nextNod[n2],
4593 prevNod[n4], prevNod[n5], nextNod[n5],
4594 prevNod[n12], midlNod[n2], nextNod[n12],
4595 prevNod[n45], midlNod[n5], nextNod[n45],
4596 prevNod[n14], prevNod[n25], nextNod[n25]);
4600 case SMDSEntity_BiQuad_Quadrangle: { // sweep BiQuadratic QUADRANGLE --->
4602 if( nbSame == 0 && nbDouble == 9 ) {
4603 // ---> tri-quadratic hexahedron with 27 nodes
4604 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
4605 nextNod[0], nextNod[1], nextNod[2], nextNod[3],
4606 prevNod[4], prevNod[5], prevNod[6], prevNod[7],
4607 nextNod[4], nextNod[5], nextNod[6], nextNod[7],
4608 midlNod[0], midlNod[1], midlNod[2], midlNod[3],
4609 prevNod[8], // bottom center
4610 midlNod[4], midlNod[5], midlNod[6], midlNod[7],
4611 nextNod[8], // top center
4612 midlNod[8]);// elem center
4620 case SMDSEntity_Polygon: { // sweep POLYGON
4622 if ( nbNodes == 6 && nbSame == 0 && nbDouble == 0 ) {
4623 // ---> hexagonal prism
4624 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
4625 prevNod[3], prevNod[4], prevNod[5],
4626 nextNod[0], nextNod[1], nextNod[2],
4627 nextNod[3], nextNod[4], nextNod[5]);
4631 case SMDSEntity_Ball:
4636 } // switch ( baseType )
4639 if ( !aNewElem && elem->GetType() == SMDSAbs_Face ) // try to create a polyherdal prism
4641 if ( baseType != SMDSEntity_Polygon )
4643 const std::vector<int>& ind = SMDS_MeshCell::interlacedSmdsOrder(baseType);
4644 SMDS_MeshCell::applyInterlace( ind, prevNod );
4645 SMDS_MeshCell::applyInterlace( ind, nextNod );
4646 SMDS_MeshCell::applyInterlace( ind, midlNod );
4647 SMDS_MeshCell::applyInterlace( ind, itNN );
4648 SMDS_MeshCell::applyInterlace( ind, isSingleNode );
4649 baseType = SMDSEntity_Polygon; // WARNING: change baseType !!!!
4651 vector<const SMDS_MeshNode*> polyedre_nodes (nbNodes*2 + 4*nbNodes);
4652 vector<int> quantities (nbNodes + 2);
4653 polyedre_nodes.clear();
4657 for (int inode = 0; inode < nbNodes; inode++)
4658 polyedre_nodes.push_back( prevNod[inode] );
4659 quantities.push_back( nbNodes );
4662 polyedre_nodes.push_back( nextNod[0] );
4663 for (int inode = nbNodes; inode-1; --inode )
4664 polyedre_nodes.push_back( nextNod[inode-1] );
4665 quantities.push_back( nbNodes );
4668 for (int iface = 0; iface < nbNodes; iface++)
4670 const int prevNbNodes = polyedre_nodes.size();
4671 int inextface = (iface+1) % nbNodes;
4672 polyedre_nodes.push_back( prevNod[inextface] );
4673 polyedre_nodes.push_back( prevNod[iface] );
4674 if ( prevNod[iface] != nextNod[iface] )
4676 if ( midlNod[ iface ]) polyedre_nodes.push_back( midlNod[ iface ]);
4677 polyedre_nodes.push_back( nextNod[iface] );
4679 if ( prevNod[inextface] != nextNod[inextface] )
4681 polyedre_nodes.push_back( nextNod[inextface] );
4682 if ( midlNod[ inextface ]) polyedre_nodes.push_back( midlNod[ inextface ]);
4684 const int nbFaceNodes = polyedre_nodes.size() - prevNbNodes;
4685 if ( nbFaceNodes > 2 )
4686 quantities.push_back( nbFaceNodes );
4687 else // degenerated face
4688 polyedre_nodes.resize( prevNbNodes );
4690 aNewElem = aMesh->AddPolyhedralVolume (polyedre_nodes, quantities);
4692 } // // try to create a polyherdal prism
4695 newElems.push_back( aNewElem );
4696 myLastCreatedElems.Append(aNewElem);
4697 srcElements.Append( elem );
4700 // set new prev nodes
4701 for ( iNode = 0; iNode < nbNodes; iNode++ )
4702 prevNod[ iNode ] = nextNod[ iNode ];
4707 //=======================================================================
4709 * \brief Create 1D and 2D elements around swept elements
4710 * \param mapNewNodes - source nodes and ones generated from them
4711 * \param newElemsMap - source elements and ones generated from them
4712 * \param elemNewNodesMap - nodes generated from each node of each element
4713 * \param elemSet - all swept elements
4714 * \param nbSteps - number of sweeping steps
4715 * \param srcElements - to append elem for each generated element
4717 //=======================================================================
4719 void SMESH_MeshEditor::makeWalls (TNodeOfNodeListMap & mapNewNodes,
4720 TTElemOfElemListMap & newElemsMap,
4721 TElemOfVecOfNnlmiMap & elemNewNodesMap,
4722 TIDSortedElemSet& elemSet,
4724 SMESH_SequenceOfElemPtr& srcElements)
4726 ASSERT( newElemsMap.size() == elemNewNodesMap.size() );
4727 SMESHDS_Mesh* aMesh = GetMeshDS();
4729 // Find nodes belonging to only one initial element - sweep them into edges.
4731 TNodeOfNodeListMapItr nList = mapNewNodes.begin();
4732 for ( ; nList != mapNewNodes.end(); nList++ )
4734 const SMDS_MeshNode* node =
4735 static_cast<const SMDS_MeshNode*>( nList->first );
4736 if ( newElemsMap.count( node ))
4737 continue; // node was extruded into edge
4738 SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator();
4739 int nbInitElems = 0;
4740 const SMDS_MeshElement* el = 0;
4741 SMDSAbs_ElementType highType = SMDSAbs_Edge; // count most complex elements only
4742 while ( eIt->more() && nbInitElems < 2 ) {
4744 SMDSAbs_ElementType type = el->GetType();
4745 if ( type == SMDSAbs_Volume || type < highType ) continue;
4746 if ( type > highType ) {
4750 nbInitElems += elemSet.count(el);
4752 if ( nbInitElems < 2 ) {
4753 bool NotCreateEdge = el && el->IsMediumNode(node);
4754 if(!NotCreateEdge) {
4755 vector<TNodeOfNodeListMapItr> newNodesItVec( 1, nList );
4756 list<const SMDS_MeshElement*> newEdges;
4757 sweepElement( node, newNodesItVec, newEdges, nbSteps, srcElements );
4762 // Make a ceiling for each element ie an equal element of last new nodes.
4763 // Find free links of faces - make edges and sweep them into faces.
4765 TTElemOfElemListMap::iterator itElem = newElemsMap.begin();
4766 TElemOfVecOfNnlmiMap::iterator itElemNodes = elemNewNodesMap.begin();
4767 for ( ; itElem != newElemsMap.end(); itElem++, itElemNodes++ )
4769 const SMDS_MeshElement* elem = itElem->first;
4770 vector<TNodeOfNodeListMapItr>& vecNewNodes = itElemNodes->second;
4772 if(itElem->second.size()==0) continue;
4774 const bool isQuadratic = elem->IsQuadratic();
4776 if ( elem->GetType() == SMDSAbs_Edge ) {
4777 // create a ceiling edge
4778 if ( !isQuadratic ) {
4779 if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
4780 vecNewNodes[ 1 ]->second.back())) {
4781 myLastCreatedElems.Append(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
4782 vecNewNodes[ 1 ]->second.back()));
4783 srcElements.Append( elem );
4787 if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
4788 vecNewNodes[ 1 ]->second.back(),
4789 vecNewNodes[ 2 ]->second.back())) {
4790 myLastCreatedElems.Append(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
4791 vecNewNodes[ 1 ]->second.back(),
4792 vecNewNodes[ 2 ]->second.back()));
4793 srcElements.Append( elem );
4797 if ( elem->GetType() != SMDSAbs_Face )
4800 bool hasFreeLinks = false;
4802 TIDSortedElemSet avoidSet;
4803 avoidSet.insert( elem );
4805 set<const SMDS_MeshNode*> aFaceLastNodes;
4806 int iNode, nbNodes = vecNewNodes.size();
4807 if ( !isQuadratic ) {
4808 // loop on the face nodes
4809 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4810 aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4811 // look for free links of the face
4812 int iNext = ( iNode + 1 == nbNodes ) ? 0 : iNode + 1;
4813 const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
4814 const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
4815 // check if a link n1-n2 is free
4816 if ( ! SMESH_MeshAlgos::FindFaceInSet ( n1, n2, elemSet, avoidSet )) {
4817 hasFreeLinks = true;
4818 // make a new edge and a ceiling for a new edge
4819 const SMDS_MeshElement* edge;
4820 if ( ! ( edge = aMesh->FindEdge( n1, n2 ))) {
4821 myLastCreatedElems.Append( edge = aMesh->AddEdge( n1, n2 )); // free link edge
4822 srcElements.Append( myLastCreatedElems.Last() );
4824 n1 = vecNewNodes[ iNode ]->second.back();
4825 n2 = vecNewNodes[ iNext ]->second.back();
4826 if ( !aMesh->FindEdge( n1, n2 )) {
4827 myLastCreatedElems.Append(aMesh->AddEdge( n1, n2 )); // new edge ceiling
4828 srcElements.Append( edge );
4833 else { // elem is quadratic face
4834 int nbn = nbNodes/2;
4835 for ( iNode = 0; iNode < nbn; iNode++ ) {
4836 aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4837 int iNext = ( iNode + 1 == nbn ) ? 0 : iNode + 1;
4838 const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
4839 const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
4840 const SMDS_MeshNode* n3 = vecNewNodes[ iNode+nbn ]->first;
4841 // check if a link is free
4842 if ( ! SMESH_MeshAlgos::FindFaceInSet ( n1, n2, elemSet, avoidSet ) &&
4843 ! SMESH_MeshAlgos::FindFaceInSet ( n1, n3, elemSet, avoidSet ) &&
4844 ! SMESH_MeshAlgos::FindFaceInSet ( n3, n2, elemSet, avoidSet ) ) {
4845 hasFreeLinks = true;
4846 // make an edge and a ceiling for a new edge
4848 if ( !aMesh->FindEdge( n1, n2, n3 )) {
4849 myLastCreatedElems.Append(aMesh->AddEdge( n1, n2, n3 )); // free link edge
4850 srcElements.Append( elem );
4852 n1 = vecNewNodes[ iNode ]->second.back();
4853 n2 = vecNewNodes[ iNext ]->second.back();
4854 n3 = vecNewNodes[ iNode+nbn ]->second.back();
4855 if ( !aMesh->FindEdge( n1, n2, n3 )) {
4856 myLastCreatedElems.Append(aMesh->AddEdge( n1, n2, n3 )); // ceiling edge
4857 srcElements.Append( elem );
4861 for ( iNode = nbn; iNode < nbNodes; iNode++ ) {
4862 aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4866 // sweep free links into faces
4868 if ( hasFreeLinks ) {
4869 list<const SMDS_MeshElement*> & newVolumes = itElem->second;
4870 int iVol, volNb, nbVolumesByStep = newVolumes.size() / nbSteps;
4872 set<const SMDS_MeshNode*> initNodeSet, topNodeSet, faceNodeSet;
4873 set<const SMDS_MeshNode*> initNodeSetNoCenter/*, topNodeSetNoCenter*/;
4874 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4875 initNodeSet.insert( vecNewNodes[ iNode ]->first );
4876 topNodeSet .insert( vecNewNodes[ iNode ]->second.back() );
4878 if ( isQuadratic && nbNodes % 2 ) { // node set for the case of a biquadratic
4879 initNodeSetNoCenter = initNodeSet; // swept face and a not biquadratic volume
4880 initNodeSetNoCenter.erase( vecNewNodes.back()->first );
4882 for ( volNb = 0; volNb < nbVolumesByStep; volNb++ ) {
4883 list<const SMDS_MeshElement*>::iterator v = newVolumes.begin();
4884 std::advance( v, volNb );
4885 // find indices of free faces of a volume and their source edges
4886 list< int > freeInd;
4887 list< const SMDS_MeshElement* > srcEdges; // source edges of free faces
4888 SMDS_VolumeTool vTool( *v, /*ignoreCentralNodes=*/false );
4889 int iF, nbF = vTool.NbFaces();
4890 for ( iF = 0; iF < nbF; iF ++ ) {
4891 if (vTool.IsFreeFace( iF ) &&
4892 vTool.GetFaceNodes( iF, faceNodeSet ) &&
4893 initNodeSet != faceNodeSet) // except an initial face
4895 if ( nbSteps == 1 && faceNodeSet == topNodeSet )
4897 if ( faceNodeSet == initNodeSetNoCenter )
4899 freeInd.push_back( iF );
4900 // find source edge of a free face iF
4901 vector<const SMDS_MeshNode*> commonNodes; // shared by the initial and free faces
4902 commonNodes.resize( initNodeSet.size(), NULL ); // avoid spoiling memory
4903 std::set_intersection( faceNodeSet.begin(), faceNodeSet.end(),
4904 initNodeSet.begin(), initNodeSet.end(),
4905 commonNodes.begin());
4906 if ( (*v)->IsQuadratic() )
4907 srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1],commonNodes[2]));
4909 srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1]));
4911 if ( !srcEdges.back() )
4913 cout << "SMESH_MeshEditor::makeWalls(), no source edge found for a free face #"
4914 << iF << " of volume #" << vTool.ID() << endl;
4919 if ( freeInd.empty() )
4922 // create faces for all steps;
4923 // if such a face has been already created by sweep of edge,
4924 // assure that its orientation is OK
4925 for ( int iStep = 0; iStep < nbSteps; iStep++ ) {
4926 vTool.Set( *v, /*ignoreCentralNodes=*/false );
4927 vTool.SetExternalNormal();
4928 const int nextShift = vTool.IsForward() ? +1 : -1;
4929 list< int >::iterator ind = freeInd.begin();
4930 list< const SMDS_MeshElement* >::iterator srcEdge = srcEdges.begin();
4931 for ( ; ind != freeInd.end(); ++ind, ++srcEdge ) // loop on free faces
4933 const SMDS_MeshNode** nodes = vTool.GetFaceNodes( *ind );
4934 int nbn = vTool.NbFaceNodes( *ind );
4935 const SMDS_MeshElement * f = 0;
4936 if ( nbn == 3 ) ///// triangle
4938 f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ]);
4940 nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
4942 const SMDS_MeshNode* newOrder[3] = { nodes[ 1 - nextShift ],
4944 nodes[ 1 + nextShift ] };
4946 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4948 myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
4952 else if ( nbn == 4 ) ///// quadrangle
4954 f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ], nodes[ 3 ]);
4956 nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
4958 const SMDS_MeshNode* newOrder[4] = { nodes[ 0 ], nodes[ 2-nextShift ],
4959 nodes[ 2 ], nodes[ 2+nextShift ] };
4961 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4963 myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
4964 newOrder[ 2 ], newOrder[ 3 ]));
4967 else if ( nbn == 6 && isQuadratic ) /////// quadratic triangle
4969 f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[1], nodes[3], nodes[5] );
4971 nodes[2] != f->GetNodeWrap( f->GetNodeIndex( nodes[0] ) + 2*nextShift ))
4973 const SMDS_MeshNode* newOrder[6] = { nodes[2 - 2*nextShift],
4975 nodes[2 + 2*nextShift],
4976 nodes[3 - 2*nextShift],
4978 nodes[3 + 2*nextShift]};
4980 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4982 myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ],
4990 else if ( nbn == 8 && isQuadratic ) /////// quadratic quadrangle
4992 f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[6],
4993 nodes[1], nodes[3], nodes[5], nodes[7] );
4995 nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
4997 const SMDS_MeshNode* newOrder[8] = { nodes[0],
4998 nodes[4 - 2*nextShift],
5000 nodes[4 + 2*nextShift],
5002 nodes[5 - 2*nextShift],
5004 nodes[5 + 2*nextShift] };
5006 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5008 myLastCreatedElems.Append(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
5009 newOrder[ 2 ], newOrder[ 3 ],
5010 newOrder[ 4 ], newOrder[ 5 ],
5011 newOrder[ 6 ], newOrder[ 7 ]));
5014 else if ( nbn == 9 && isQuadratic ) /////// bi-quadratic quadrangle
5016 f = aMesh->FindElement( vector<const SMDS_MeshNode*>( nodes, nodes+nbn ),
5017 SMDSAbs_Face, /*noMedium=*/false);
5019 nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
5021 const SMDS_MeshNode* newOrder[9] = { nodes[0],
5022 nodes[4 - 2*nextShift],
5024 nodes[4 + 2*nextShift],
5026 nodes[5 - 2*nextShift],
5028 nodes[5 + 2*nextShift],
5031 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5033 myLastCreatedElems.Append(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
5034 newOrder[ 2 ], newOrder[ 3 ],
5035 newOrder[ 4 ], newOrder[ 5 ],
5036 newOrder[ 6 ], newOrder[ 7 ],
5040 else //////// polygon
5042 vector<const SMDS_MeshNode*> polygon_nodes ( nodes, nodes+nbn );
5043 const SMDS_MeshFace * f = aMesh->FindFace( polygon_nodes );
5045 nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + nextShift ))
5047 if ( !vTool.IsForward() )
5048 std::reverse( polygon_nodes.begin(), polygon_nodes.end());
5050 aMesh->ChangeElementNodes( f, &polygon_nodes[0], nbn );
5052 AddElement(polygon_nodes, SMDSAbs_Face, polygon_nodes.size()>4);
5056 while ( srcElements.Length() < myLastCreatedElems.Length() )
5057 srcElements.Append( *srcEdge );
5059 } // loop on free faces
5061 // go to the next volume
5063 while ( iVol++ < nbVolumesByStep ) v++;
5066 } // loop on volumes of one step
5067 } // sweep free links into faces
5069 // Make a ceiling face with a normal external to a volume
5071 // use SMDS_VolumeTool to get a correctly ordered nodes of a ceiling face
5072 SMDS_VolumeTool lastVol( itElem->second.back(), /*ignoreCentralNodes=*/false );
5073 int iF = lastVol.GetFaceIndex( aFaceLastNodes );
5075 if ( iF < 0 && isQuadratic && nbNodes % 2 ) { // remove a central node of biquadratic
5076 aFaceLastNodes.erase( vecNewNodes.back()->second.back() );
5077 iF = lastVol.GetFaceIndex( aFaceLastNodes );
5080 lastVol.SetExternalNormal();
5081 const SMDS_MeshNode** nodes = lastVol.GetFaceNodes( iF );
5082 int nbn = lastVol.NbFaceNodes( iF );
5083 // we do not use this->AddElement() because nodes are interlaced
5084 vector<const SMDS_MeshNode*> nodeVec( nodes, nodes+nbn );
5085 if ( !hasFreeLinks ||
5086 !aMesh->FindElement( nodeVec, SMDSAbs_Face, /*noMedium=*/false) )
5089 myLastCreatedElems.Append(aMesh->AddFace( nodes[0], nodes[1], nodes[2] ));
5091 else if ( nbn == 4 )
5092 myLastCreatedElems.Append(aMesh->AddFace( nodes[0], nodes[1], nodes[2], nodes[3]));
5094 else if ( nbn == 6 && isQuadratic )
5095 myLastCreatedElems.Append(aMesh->AddFace( nodes[0], nodes[2], nodes[4],
5096 nodes[1], nodes[3], nodes[5]));
5097 else if ( nbn == 7 && isQuadratic )
5098 myLastCreatedElems.Append(aMesh->AddFace( nodes[0], nodes[2], nodes[4],
5099 nodes[1], nodes[3], nodes[5], nodes[6]));
5100 else if ( nbn == 8 && isQuadratic )
5101 myLastCreatedElems.Append(aMesh->AddFace( nodes[0], nodes[2], nodes[4], nodes[6],
5102 nodes[1], nodes[3], nodes[5], nodes[7]));
5103 else if ( nbn == 9 && isQuadratic )
5104 myLastCreatedElems.Append(aMesh->AddFace( nodes[0], nodes[2], nodes[4], nodes[6],
5105 nodes[1], nodes[3], nodes[5], nodes[7],
5108 myLastCreatedElems.Append(aMesh->AddPolygonalFace( nodeVec ));
5110 while ( srcElements.Length() < myLastCreatedElems.Length() )
5111 srcElements.Append( elem );
5114 } // loop on swept elements
5117 //=======================================================================
5118 //function : RotationSweep
5120 //=======================================================================
5122 SMESH_MeshEditor::PGroupIDs
5123 SMESH_MeshEditor::RotationSweep(TIDSortedElemSet & theElems,
5124 const gp_Ax1& theAxis,
5125 const double theAngle,
5126 const int theNbSteps,
5127 const double theTol,
5128 const bool theMakeGroups,
5129 const bool theMakeWalls)
5131 myLastCreatedElems.Clear();
5132 myLastCreatedNodes.Clear();
5134 // source elements for each generated one
5135 SMESH_SequenceOfElemPtr srcElems, srcNodes;
5137 MESSAGE( "RotationSweep()");
5139 aTrsf.SetRotation( theAxis, theAngle );
5141 aTrsf2.SetRotation( theAxis, theAngle/2. );
5143 gp_Lin aLine( theAxis );
5144 double aSqTol = theTol * theTol;
5146 SMESHDS_Mesh* aMesh = GetMeshDS();
5148 TNodeOfNodeListMap mapNewNodes;
5149 TElemOfVecOfNnlmiMap mapElemNewNodes;
5150 TTElemOfElemListMap newElemsMap;
5152 const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
5153 myMesh->NbFaces(ORDER_QUADRATIC) +
5154 myMesh->NbVolumes(ORDER_QUADRATIC) );
5156 TIDSortedElemSet::iterator itElem;
5157 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
5158 const SMDS_MeshElement* elem = *itElem;
5159 if ( !elem || elem->GetType() == SMDSAbs_Volume )
5161 vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5162 newNodesItVec.reserve( elem->NbNodes() );
5164 // loop on elem nodes
5165 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5166 while ( itN->more() )
5168 // check if a node has been already sweeped
5169 const SMDS_MeshNode* node = cast2Node( itN->next() );
5171 gp_XYZ aXYZ( node->X(), node->Y(), node->Z() );
5173 aXYZ.Coord( coord[0], coord[1], coord[2] );
5174 bool isOnAxis = ( aLine.SquareDistance( aXYZ ) <= aSqTol );
5176 TNodeOfNodeListMapItr nIt =
5177 mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5178 list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5179 if ( listNewNodes.empty() )
5181 // check if we are to create medium nodes between corner ones
5182 bool needMediumNodes = false;
5183 if ( isQuadraticMesh )
5185 SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
5186 while (it->more() && !needMediumNodes )
5188 const SMDS_MeshElement* invElem = it->next();
5189 if ( invElem != elem && !theElems.count( invElem )) continue;
5190 needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
5191 if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
5192 needMediumNodes = true;
5197 const SMDS_MeshNode * newNode = node;
5198 for ( int i = 0; i < theNbSteps; i++ ) {
5200 if ( needMediumNodes ) // create a medium node
5202 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
5203 newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5204 myLastCreatedNodes.Append(newNode);
5205 srcNodes.Append( node );
5206 listNewNodes.push_back( newNode );
5207 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
5210 aTrsf.Transforms( coord[0], coord[1], coord[2] );
5212 // create a corner node
5213 newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5214 myLastCreatedNodes.Append(newNode);
5215 srcNodes.Append( node );
5216 listNewNodes.push_back( newNode );
5219 listNewNodes.push_back( newNode );
5220 // if ( needMediumNodes )
5221 // listNewNodes.push_back( newNode );
5225 newNodesItVec.push_back( nIt );
5227 // make new elements
5228 sweepElement( elem, newNodesItVec, newElemsMap[elem], theNbSteps, srcElems );
5232 makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElems, theNbSteps, srcElems );
5234 PGroupIDs newGroupIDs;
5235 if ( theMakeGroups )
5236 newGroupIDs = generateGroups( srcNodes, srcElems, "rotated");
5241 //=======================================================================
5242 //function : ExtrusParam
5243 //purpose : standard construction
5244 //=======================================================================
5246 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const gp_Vec& theStep,
5247 const int theNbSteps,
5249 const double theTolerance):
5251 myFlags( theFlags ),
5252 myTolerance( theTolerance ),
5253 myElemsToUse( NULL )
5255 mySteps = new TColStd_HSequenceOfReal;
5256 const double stepSize = theStep.Magnitude();
5257 for (int i=1; i<=theNbSteps; i++ )
5258 mySteps->Append( stepSize );
5260 if (( theFlags & EXTRUSION_FLAG_SEW ) &&
5261 ( theTolerance > 0 ))
5263 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDirAndSew;
5267 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDir;
5271 //=======================================================================
5272 //function : ExtrusParam
5273 //purpose : steps are given explicitly
5274 //=======================================================================
5276 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const gp_Dir& theDir,
5277 Handle(TColStd_HSequenceOfReal) theSteps,
5279 const double theTolerance):
5281 mySteps( theSteps ),
5282 myFlags( theFlags ),
5283 myTolerance( theTolerance ),
5284 myElemsToUse( NULL )
5286 if (( theFlags & EXTRUSION_FLAG_SEW ) &&
5287 ( theTolerance > 0 ))
5289 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDirAndSew;
5293 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDir;
5297 //=======================================================================
5298 //function : ExtrusParam
5299 //purpose : for extrusion by normal
5300 //=======================================================================
5302 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const double theStepSize,
5303 const int theNbSteps,
5307 mySteps( new TColStd_HSequenceOfReal ),
5308 myFlags( theFlags ),
5310 myElemsToUse( NULL )
5312 for (int i = 0; i < theNbSteps; i++ )
5313 mySteps->Append( theStepSize );
5317 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByNormal1D;
5321 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByNormal2D;
5325 //=======================================================================
5326 //function : ExtrusParam::SetElementsToUse
5327 //purpose : stores elements to use for extrusion by normal, depending on
5328 // state of EXTRUSION_FLAG_USE_INPUT_ELEMS_ONLY flag
5329 //=======================================================================
5331 void SMESH_MeshEditor::ExtrusParam::SetElementsToUse( const TIDSortedElemSet& elems )
5333 myElemsToUse = ToUseInpElemsOnly() ? & elems : 0;
5336 //=======================================================================
5337 //function : ExtrusParam::beginStepIter
5338 //purpose : prepare iteration on steps
5339 //=======================================================================
5341 void SMESH_MeshEditor::ExtrusParam::beginStepIter( bool withMediumNodes )
5343 myWithMediumNodes = withMediumNodes;
5347 //=======================================================================
5348 //function : ExtrusParam::moreSteps
5349 //purpose : are there more steps?
5350 //=======================================================================
5352 bool SMESH_MeshEditor::ExtrusParam::moreSteps()
5354 return myNextStep <= mySteps->Length() || !myCurSteps.empty();
5356 //=======================================================================
5357 //function : ExtrusParam::nextStep
5358 //purpose : returns the next step
5359 //=======================================================================
5361 double SMESH_MeshEditor::ExtrusParam::nextStep()
5364 if ( !myCurSteps.empty() )
5366 res = myCurSteps.back();
5367 myCurSteps.pop_back();
5369 else if ( myNextStep <= mySteps->Length() )
5371 myCurSteps.push_back( mySteps->Value( myNextStep ));
5373 if ( myWithMediumNodes )
5375 myCurSteps.back() /= 2.;
5376 myCurSteps.push_back( myCurSteps.back() );
5383 //=======================================================================
5384 //function : ExtrusParam::makeNodesByDir
5385 //purpose : create nodes for standard extrusion
5386 //=======================================================================
5388 int SMESH_MeshEditor::ExtrusParam::
5389 makeNodesByDir( SMESHDS_Mesh* mesh,
5390 const SMDS_MeshNode* srcNode,
5391 std::list<const SMDS_MeshNode*> & newNodes,
5392 const bool makeMediumNodes)
5394 gp_XYZ p = SMESH_TNodeXYZ( srcNode );
5397 for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5399 p += myDir.XYZ() * nextStep();
5400 const SMDS_MeshNode * newNode = mesh->AddNode( p.X(), p.Y(), p.Z() );
5401 newNodes.push_back( newNode );
5406 //=======================================================================
5407 //function : ExtrusParam::makeNodesByDirAndSew
5408 //purpose : create nodes for standard extrusion with sewing
5409 //=======================================================================
5411 int SMESH_MeshEditor::ExtrusParam::
5412 makeNodesByDirAndSew( SMESHDS_Mesh* mesh,
5413 const SMDS_MeshNode* srcNode,
5414 std::list<const SMDS_MeshNode*> & newNodes,
5415 const bool makeMediumNodes)
5417 gp_XYZ P1 = SMESH_TNodeXYZ( srcNode );
5420 for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5422 P1 += myDir.XYZ() * nextStep();
5424 // try to search in sequence of existing nodes
5425 // if myNodes.Length()>0 we 'nave to use given sequence
5426 // else - use all nodes of mesh
5427 const SMDS_MeshNode * node = 0;
5428 if ( myNodes.Length() > 0 ) {
5430 for(i=1; i<=myNodes.Length(); i++) {
5431 gp_XYZ P2 = SMESH_TNodeXYZ( myNodes.Value(i) );
5432 if (( P1 - P2 ).SquareModulus() < myTolerance * myTolerance )
5434 node = myNodes.Value(i);
5440 SMDS_NodeIteratorPtr itn = mesh->nodesIterator();
5441 while(itn->more()) {
5442 SMESH_TNodeXYZ P2( itn->next() );
5443 if (( P1 - P2 ).SquareModulus() < myTolerance * myTolerance )
5452 node = mesh->AddNode( P1.X(), P1.Y(), P1.Z() );
5454 newNodes.push_back( node );
5461 //=======================================================================
5462 //function : ExtrusParam::makeNodesByNormal2D
5463 //purpose : create nodes for extrusion using normals of faces
5464 //=======================================================================
5466 int SMESH_MeshEditor::ExtrusParam::
5467 makeNodesByNormal2D( SMESHDS_Mesh* mesh,
5468 const SMDS_MeshNode* srcNode,
5469 std::list<const SMDS_MeshNode*> & newNodes,
5470 const bool makeMediumNodes)
5472 const bool alongAvgNorm = ( myFlags & EXTRUSION_FLAG_BY_AVG_NORMAL );
5474 gp_XYZ p = SMESH_TNodeXYZ( srcNode );
5476 // get normals to faces sharing srcNode
5477 vector< gp_XYZ > norms, baryCenters;
5478 gp_XYZ norm, avgNorm( 0,0,0 );
5479 SMDS_ElemIteratorPtr faceIt = srcNode->GetInverseElementIterator( SMDSAbs_Face );
5480 while ( faceIt->more() )
5482 const SMDS_MeshElement* face = faceIt->next();
5483 if ( myElemsToUse && !myElemsToUse->count( face ))
5485 if ( SMESH_MeshAlgos::FaceNormal( face, norm, /*normalized=*/true ))
5487 norms.push_back( norm );
5489 if ( !alongAvgNorm )
5493 for ( SMDS_ElemIteratorPtr nIt = face->nodesIterator(); nIt->more(); ++nbN )
5494 bc += SMESH_TNodeXYZ( nIt->next() );
5495 baryCenters.push_back( bc / nbN );
5500 if ( norms.empty() ) return 0;
5502 double normSize = avgNorm.Modulus();
5503 if ( normSize < std::numeric_limits<double>::min() )
5506 if ( myFlags & EXTRUSION_FLAG_BY_AVG_NORMAL ) // extrude along avgNorm
5509 return makeNodesByDir( mesh, srcNode, newNodes, makeMediumNodes );
5512 avgNorm /= normSize;
5515 for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5518 double stepSize = nextStep();
5520 if ( norms.size() > 1 )
5522 for ( size_t iF = 0; iF < norms.size(); ++iF ) // loop on faces
5524 // translate plane of a face
5525 baryCenters[ iF ] += norms[ iF ] * stepSize;
5527 // find point of intersection of the face plane located at baryCenters[ iF ]
5528 // and avgNorm located at pNew
5529 double d = -( norms[ iF ] * baryCenters[ iF ]); // d of plane equation ax+by+cz+d=0
5530 double dot = ( norms[ iF ] * avgNorm );
5531 if ( dot < std::numeric_limits<double>::min() )
5532 dot = stepSize * 1e-3;
5533 double step = -( norms[ iF ] * pNew + d ) / dot;
5534 pNew += step * avgNorm;
5539 pNew += stepSize * avgNorm;
5543 const SMDS_MeshNode * newNode = mesh->AddNode( p.X(), p.Y(), p.Z() );
5544 newNodes.push_back( newNode );
5549 //=======================================================================
5550 //function : ExtrusParam::makeNodesByNormal1D
5551 //purpose : create nodes for extrusion using normals of edges
5552 //=======================================================================
5554 int SMESH_MeshEditor::ExtrusParam::
5555 makeNodesByNormal1D( SMESHDS_Mesh* mesh,
5556 const SMDS_MeshNode* srcNode,
5557 std::list<const SMDS_MeshNode*> & newNodes,
5558 const bool makeMediumNodes)
5560 throw SALOME_Exception("Extrusion 1D by Normal not implemented");
5564 //=======================================================================
5565 //function : ExtrusionSweep
5567 //=======================================================================
5569 SMESH_MeshEditor::PGroupIDs
5570 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet & theElems,
5571 const gp_Vec& theStep,
5572 const int theNbSteps,
5573 TTElemOfElemListMap& newElemsMap,
5575 const double theTolerance)
5577 ExtrusParam aParams( theStep, theNbSteps, theFlags, theTolerance );
5578 return ExtrusionSweep( theElems, aParams, newElemsMap );
5582 //=======================================================================
5583 //function : ExtrusionSweep
5585 //=======================================================================
5587 SMESH_MeshEditor::PGroupIDs
5588 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet & theElems,
5589 ExtrusParam& theParams,
5590 TTElemOfElemListMap& newElemsMap)
5592 myLastCreatedElems.Clear();
5593 myLastCreatedNodes.Clear();
5595 // source elements for each generated one
5596 SMESH_SequenceOfElemPtr srcElems, srcNodes;
5598 SMESHDS_Mesh* aMesh = GetMeshDS();
5600 const int nbSteps = theParams.NbSteps();
5601 theParams.SetElementsToUse( theElems );
5603 TNodeOfNodeListMap mapNewNodes;
5604 //TNodeOfNodeVecMap mapNewNodes;
5605 TElemOfVecOfNnlmiMap mapElemNewNodes;
5606 //TElemOfVecOfMapNodesMap mapElemNewNodes;
5608 const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
5609 myMesh->NbFaces(ORDER_QUADRATIC) +
5610 myMesh->NbVolumes(ORDER_QUADRATIC) );
5612 TIDSortedElemSet::iterator itElem;
5613 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
5615 // check element type
5616 const SMDS_MeshElement* elem = *itElem;
5617 if ( !elem || elem->GetType() == SMDSAbs_Volume )
5620 const size_t nbNodes = elem->NbNodes();
5621 vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5622 newNodesItVec.reserve( nbNodes );
5624 // loop on elem nodes
5625 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5626 while ( itN->more() )
5628 // check if a node has been already sweeped
5629 const SMDS_MeshNode* node = cast2Node( itN->next() );
5630 TNodeOfNodeListMap::iterator nIt =
5631 mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5632 list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5633 if ( listNewNodes.empty() )
5637 // check if we are to create medium nodes between corner ones
5638 bool needMediumNodes = false;
5639 if ( isQuadraticMesh )
5641 SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
5642 while (it->more() && !needMediumNodes )
5644 const SMDS_MeshElement* invElem = it->next();
5645 if ( invElem != elem && !theElems.count( invElem )) continue;
5646 needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
5647 if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
5648 needMediumNodes = true;
5651 // create nodes for all steps
5652 if ( theParams.MakeNodes( GetMeshDS(), node, listNewNodes, needMediumNodes ))
5654 list<const SMDS_MeshNode*>::iterator newNodesIt = listNewNodes.begin();
5655 for ( ; newNodesIt != listNewNodes.end(); ++newNodesIt )
5657 myLastCreatedNodes.Append( *newNodesIt );
5658 srcNodes.Append( node );
5663 break; // newNodesItVec will be shorter than nbNodes
5666 newNodesItVec.push_back( nIt );
5668 // make new elements
5669 if ( newNodesItVec.size() == nbNodes )
5670 sweepElement( elem, newNodesItVec, newElemsMap[elem], nbSteps, srcElems );
5673 if ( theParams.ToMakeBoundary() ) {
5674 makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElems, nbSteps, srcElems );
5676 PGroupIDs newGroupIDs;
5677 if ( theParams.ToMakeGroups() )
5678 newGroupIDs = generateGroups( srcNodes, srcElems, "extruded");
5683 //=======================================================================
5684 //function : ExtrusionAlongTrack
5686 //=======================================================================
5687 SMESH_MeshEditor::Extrusion_Error
5688 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet & theElements,
5689 SMESH_subMesh* theTrack,
5690 const SMDS_MeshNode* theN1,
5691 const bool theHasAngles,
5692 list<double>& theAngles,
5693 const bool theLinearVariation,
5694 const bool theHasRefPoint,
5695 const gp_Pnt& theRefPoint,
5696 const bool theMakeGroups)
5698 MESSAGE("ExtrusionAlongTrack");
5699 myLastCreatedElems.Clear();
5700 myLastCreatedNodes.Clear();
5703 std::list<double> aPrms;
5704 TIDSortedElemSet::iterator itElem;
5707 TopoDS_Edge aTrackEdge;
5708 TopoDS_Vertex aV1, aV2;
5710 SMDS_ElemIteratorPtr aItE;
5711 SMDS_NodeIteratorPtr aItN;
5712 SMDSAbs_ElementType aTypeE;
5714 TNodeOfNodeListMap mapNewNodes;
5717 aNbE = theElements.size();
5720 return EXTR_NO_ELEMENTS;
5722 // 1.1 Track Pattern
5725 SMESHDS_SubMesh* pSubMeshDS = theTrack->GetSubMeshDS();
5727 aItE = pSubMeshDS->GetElements();
5728 while ( aItE->more() ) {
5729 const SMDS_MeshElement* pE = aItE->next();
5730 aTypeE = pE->GetType();
5731 // Pattern must contain links only
5732 if ( aTypeE != SMDSAbs_Edge )
5733 return EXTR_PATH_NOT_EDGE;
5736 list<SMESH_MeshEditor_PathPoint> fullList;
5738 const TopoDS_Shape& aS = theTrack->GetSubShape();
5739 // Sub-shape for the Pattern must be an Edge or Wire
5740 if( aS.ShapeType() == TopAbs_EDGE ) {
5741 aTrackEdge = TopoDS::Edge( aS );
5742 // the Edge must not be degenerated
5743 if ( SMESH_Algo::isDegenerated( aTrackEdge ) )
5744 return EXTR_BAD_PATH_SHAPE;
5745 TopExp::Vertices( aTrackEdge, aV1, aV2 );
5746 aItN = theTrack->GetFather()->GetSubMesh( aV1 )->GetSubMeshDS()->GetNodes();
5747 const SMDS_MeshNode* aN1 = aItN->next();
5748 aItN = theTrack->GetFather()->GetSubMesh( aV2 )->GetSubMeshDS()->GetNodes();
5749 const SMDS_MeshNode* aN2 = aItN->next();
5750 // starting node must be aN1 or aN2
5751 if ( !( aN1 == theN1 || aN2 == theN1 ) )
5752 return EXTR_BAD_STARTING_NODE;
5753 aItN = pSubMeshDS->GetNodes();
5754 while ( aItN->more() ) {
5755 const SMDS_MeshNode* pNode = aItN->next();
5756 const SMDS_EdgePosition* pEPos =
5757 static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
5758 double aT = pEPos->GetUParameter();
5759 aPrms.push_back( aT );
5761 //Extrusion_Error err =
5762 MakeEdgePathPoints(aPrms, aTrackEdge, (aN1==theN1), fullList);
5763 } else if( aS.ShapeType() == TopAbs_WIRE ) {
5764 list< SMESH_subMesh* > LSM;
5765 TopTools_SequenceOfShape Edges;
5766 SMESH_subMeshIteratorPtr itSM = theTrack->getDependsOnIterator(false,true);
5767 while(itSM->more()) {
5768 SMESH_subMesh* SM = itSM->next();
5770 const TopoDS_Shape& aS = SM->GetSubShape();
5773 list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
5774 int startNid = theN1->GetID();
5775 TColStd_MapOfInteger UsedNums;
5777 int NbEdges = Edges.Length();
5779 for(; i<=NbEdges; i++) {
5781 list< SMESH_subMesh* >::iterator itLSM = LSM.begin();
5782 for(; itLSM!=LSM.end(); itLSM++) {
5784 if(UsedNums.Contains(k)) continue;
5785 aTrackEdge = TopoDS::Edge( Edges.Value(k) );
5786 SMESH_subMesh* locTrack = *itLSM;
5787 SMESHDS_SubMesh* locMeshDS = locTrack->GetSubMeshDS();
5788 TopExp::Vertices( aTrackEdge, aV1, aV2 );
5789 aItN = locTrack->GetFather()->GetSubMesh(aV1)->GetSubMeshDS()->GetNodes();
5790 const SMDS_MeshNode* aN1 = aItN->next();
5791 aItN = locTrack->GetFather()->GetSubMesh(aV2)->GetSubMeshDS()->GetNodes();
5792 const SMDS_MeshNode* aN2 = aItN->next();
5793 // starting node must be aN1 or aN2
5794 if ( !( aN1->GetID() == startNid || aN2->GetID() == startNid ) ) continue;
5795 // 2. Collect parameters on the track edge
5797 aItN = locMeshDS->GetNodes();
5798 while ( aItN->more() ) {
5799 const SMDS_MeshNode* pNode = aItN->next();
5800 const SMDS_EdgePosition* pEPos =
5801 static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
5802 double aT = pEPos->GetUParameter();
5803 aPrms.push_back( aT );
5805 list<SMESH_MeshEditor_PathPoint> LPP;
5806 //Extrusion_Error err =
5807 MakeEdgePathPoints(aPrms, aTrackEdge,(aN1->GetID()==startNid), LPP);
5808 LLPPs.push_back(LPP);
5810 // update startN for search following egde
5811 if( aN1->GetID() == startNid ) startNid = aN2->GetID();
5812 else startNid = aN1->GetID();
5816 list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
5817 list<SMESH_MeshEditor_PathPoint> firstList = *itLLPP;
5818 list<SMESH_MeshEditor_PathPoint>::iterator itPP = firstList.begin();
5819 for(; itPP!=firstList.end(); itPP++) {
5820 fullList.push_back( *itPP );
5822 SMESH_MeshEditor_PathPoint PP1 = fullList.back();
5823 fullList.pop_back();
5825 for(; itLLPP!=LLPPs.end(); itLLPP++) {
5826 list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
5827 itPP = currList.begin();
5828 SMESH_MeshEditor_PathPoint PP2 = currList.front();
5829 gp_Dir D1 = PP1.Tangent();
5830 gp_Dir D2 = PP2.Tangent();
5831 gp_Dir Dnew( gp_Vec( (D1.X()+D2.X())/2, (D1.Y()+D2.Y())/2,
5832 (D1.Z()+D2.Z())/2 ) );
5833 PP1.SetTangent(Dnew);
5834 fullList.push_back(PP1);
5836 for(; itPP!=firstList.end(); itPP++) {
5837 fullList.push_back( *itPP );
5839 PP1 = fullList.back();
5840 fullList.pop_back();
5842 // if wire not closed
5843 fullList.push_back(PP1);
5847 return EXTR_BAD_PATH_SHAPE;
5850 return MakeExtrElements(theElements, fullList, theHasAngles, theAngles, theLinearVariation,
5851 theHasRefPoint, theRefPoint, theMakeGroups);
5855 //=======================================================================
5856 //function : ExtrusionAlongTrack
5858 //=======================================================================
5859 SMESH_MeshEditor::Extrusion_Error
5860 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet & theElements,
5861 SMESH_Mesh* theTrack,
5862 const SMDS_MeshNode* theN1,
5863 const bool theHasAngles,
5864 list<double>& theAngles,
5865 const bool theLinearVariation,
5866 const bool theHasRefPoint,
5867 const gp_Pnt& theRefPoint,
5868 const bool theMakeGroups)
5870 myLastCreatedElems.Clear();
5871 myLastCreatedNodes.Clear();
5874 std::list<double> aPrms;
5875 TIDSortedElemSet::iterator itElem;
5878 TopoDS_Edge aTrackEdge;
5879 TopoDS_Vertex aV1, aV2;
5881 SMDS_ElemIteratorPtr aItE;
5882 SMDS_NodeIteratorPtr aItN;
5883 SMDSAbs_ElementType aTypeE;
5885 TNodeOfNodeListMap mapNewNodes;
5888 aNbE = theElements.size();
5891 return EXTR_NO_ELEMENTS;
5893 // 1.1 Track Pattern
5896 SMESHDS_Mesh* pMeshDS = theTrack->GetMeshDS();
5898 aItE = pMeshDS->elementsIterator();
5899 while ( aItE->more() ) {
5900 const SMDS_MeshElement* pE = aItE->next();
5901 aTypeE = pE->GetType();
5902 // Pattern must contain links only
5903 if ( aTypeE != SMDSAbs_Edge )
5904 return EXTR_PATH_NOT_EDGE;
5907 list<SMESH_MeshEditor_PathPoint> fullList;
5909 const TopoDS_Shape& aS = theTrack->GetShapeToMesh();
5911 if ( !theTrack->HasShapeToMesh() ) {
5912 //Mesh without shape
5913 const SMDS_MeshNode* currentNode = NULL;
5914 const SMDS_MeshNode* prevNode = theN1;
5915 std::vector<const SMDS_MeshNode*> aNodesList;
5916 aNodesList.push_back(theN1);
5917 int nbEdges = 0, conn=0;
5918 const SMDS_MeshElement* prevElem = NULL;
5919 const SMDS_MeshElement* currentElem = NULL;
5920 int totalNbEdges = theTrack->NbEdges();
5921 SMDS_ElemIteratorPtr nIt;
5924 if( !theTrack->GetMeshDS()->Contains(theN1) ) {
5925 return EXTR_BAD_STARTING_NODE;
5928 conn = nbEdgeConnectivity(theN1);
5930 return EXTR_PATH_NOT_EDGE;
5932 aItE = theN1->GetInverseElementIterator();
5933 prevElem = aItE->next();
5934 currentElem = prevElem;
5936 if(totalNbEdges == 1 ) {
5937 nIt = currentElem->nodesIterator();
5938 currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
5939 if(currentNode == prevNode)
5940 currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
5941 aNodesList.push_back(currentNode);
5943 nIt = currentElem->nodesIterator();
5944 while( nIt->more() ) {
5945 currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
5946 if(currentNode == prevNode)
5947 currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
5948 aNodesList.push_back(currentNode);
5950 //case of the closed mesh
5951 if(currentNode == theN1) {
5956 conn = nbEdgeConnectivity(currentNode);
5958 return EXTR_PATH_NOT_EDGE;
5959 }else if( conn == 1 && nbEdges > 0 ) {
5964 prevNode = currentNode;
5965 aItE = currentNode->GetInverseElementIterator();
5966 currentElem = aItE->next();
5967 if( currentElem == prevElem)
5968 currentElem = aItE->next();
5969 nIt = currentElem->nodesIterator();
5970 prevElem = currentElem;
5976 if(nbEdges != totalNbEdges)
5977 return EXTR_PATH_NOT_EDGE;
5979 TopTools_SequenceOfShape Edges;
5980 double x1,x2,y1,y2,z1,z2;
5981 list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
5982 int startNid = theN1->GetID();
5983 for(int i = 1; i < aNodesList.size(); i++) {
5984 x1 = aNodesList[i-1]->X();x2 = aNodesList[i]->X();
5985 y1 = aNodesList[i-1]->Y();y2 = aNodesList[i]->Y();
5986 z1 = aNodesList[i-1]->Z();z2 = aNodesList[i]->Z();
5987 TopoDS_Edge e = BRepBuilderAPI_MakeEdge(gp_Pnt(x1,y1,z1),gp_Pnt(x2,y2,z2));
5988 list<SMESH_MeshEditor_PathPoint> LPP;
5990 MakeEdgePathPoints(aPrms, e, (aNodesList[i-1]->GetID()==startNid), LPP);
5991 LLPPs.push_back(LPP);
5992 if( aNodesList[i-1]->GetID() == startNid ) startNid = aNodesList[i]->GetID();
5993 else startNid = aNodesList[i-1]->GetID();
5997 list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
5998 list<SMESH_MeshEditor_PathPoint> firstList = *itLLPP;
5999 list<SMESH_MeshEditor_PathPoint>::iterator itPP = firstList.begin();
6000 for(; itPP!=firstList.end(); itPP++) {
6001 fullList.push_back( *itPP );
6004 SMESH_MeshEditor_PathPoint PP1 = fullList.back();
6005 SMESH_MeshEditor_PathPoint PP2;
6006 fullList.pop_back();
6008 for(; itLLPP!=LLPPs.end(); itLLPP++) {
6009 list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
6010 itPP = currList.begin();
6011 PP2 = currList.front();
6012 gp_Dir D1 = PP1.Tangent();
6013 gp_Dir D2 = PP2.Tangent();
6014 gp_Dir Dnew( gp_Vec( (D1.X()+D2.X())/2, (D1.Y()+D2.Y())/2,
6015 (D1.Z()+D2.Z())/2 ) );
6016 PP1.SetTangent(Dnew);
6017 fullList.push_back(PP1);
6019 for(; itPP!=currList.end(); itPP++) {
6020 fullList.push_back( *itPP );
6022 PP1 = fullList.back();
6023 fullList.pop_back();
6025 fullList.push_back(PP1);
6027 } // Sub-shape for the Pattern must be an Edge or Wire
6028 else if( aS.ShapeType() == TopAbs_EDGE ) {
6029 aTrackEdge = TopoDS::Edge( aS );
6030 // the Edge must not be degenerated
6031 if ( SMESH_Algo::isDegenerated( aTrackEdge ) )
6032 return EXTR_BAD_PATH_SHAPE;
6033 TopExp::Vertices( aTrackEdge, aV1, aV2 );
6034 const SMDS_MeshNode* aN1 = SMESH_Algo::VertexNode( aV1, pMeshDS );
6035 const SMDS_MeshNode* aN2 = SMESH_Algo::VertexNode( aV2, pMeshDS );
6036 // starting node must be aN1 or aN2
6037 if ( !( aN1 == theN1 || aN2 == theN1 ) )
6038 return EXTR_BAD_STARTING_NODE;
6039 aItN = pMeshDS->nodesIterator();
6040 while ( aItN->more() ) {
6041 const SMDS_MeshNode* pNode = aItN->next();
6042 if( pNode==aN1 || pNode==aN2 ) continue;
6043 const SMDS_EdgePosition* pEPos =
6044 static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
6045 double aT = pEPos->GetUParameter();
6046 aPrms.push_back( aT );
6048 //Extrusion_Error err =
6049 MakeEdgePathPoints(aPrms, aTrackEdge, (aN1==theN1), fullList);
6051 else if( aS.ShapeType() == TopAbs_WIRE ) {
6052 list< SMESH_subMesh* > LSM;
6053 TopTools_SequenceOfShape Edges;
6054 TopExp_Explorer eExp(aS, TopAbs_EDGE);
6055 for(; eExp.More(); eExp.Next()) {
6056 TopoDS_Edge E = TopoDS::Edge( eExp.Current() );
6057 if( SMESH_Algo::isDegenerated(E) ) continue;
6058 SMESH_subMesh* SM = theTrack->GetSubMesh(E);
6064 list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
6065 TopoDS_Vertex aVprev;
6066 TColStd_MapOfInteger UsedNums;
6067 int NbEdges = Edges.Length();
6069 for(; i<=NbEdges; i++) {
6071 list< SMESH_subMesh* >::iterator itLSM = LSM.begin();
6072 for(; itLSM!=LSM.end(); itLSM++) {
6074 if(UsedNums.Contains(k)) continue;
6075 aTrackEdge = TopoDS::Edge( Edges.Value(k) );
6076 SMESH_subMesh* locTrack = *itLSM;
6077 SMESHDS_SubMesh* locMeshDS = locTrack->GetSubMeshDS();
6078 TopExp::Vertices( aTrackEdge, aV1, aV2 );
6079 bool aN1isOK = false, aN2isOK = false;
6080 if ( aVprev.IsNull() ) {
6081 // if previous vertex is not yet defined, it means that we in the beginning of wire
6082 // and we have to find initial vertex corresponding to starting node theN1
6083 const SMDS_MeshNode* aN1 = SMESH_Algo::VertexNode( aV1, pMeshDS );
6084 const SMDS_MeshNode* aN2 = SMESH_Algo::VertexNode( aV2, pMeshDS );
6085 // starting node must be aN1 or aN2
6086 aN1isOK = ( aN1 && aN1 == theN1 );
6087 aN2isOK = ( aN2 && aN2 == theN1 );
6090 // we have specified ending vertex of the previous edge on the previous iteration
6091 // and we have just to check that it corresponds to any vertex in current segment
6092 aN1isOK = aVprev.IsSame( aV1 );
6093 aN2isOK = aVprev.IsSame( aV2 );
6095 if ( !aN1isOK && !aN2isOK ) continue;
6096 // 2. Collect parameters on the track edge
6098 aItN = locMeshDS->GetNodes();
6099 while ( aItN->more() ) {
6100 const SMDS_MeshNode* pNode = aItN->next();
6101 const SMDS_EdgePosition* pEPos =
6102 static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
6103 double aT = pEPos->GetUParameter();
6104 aPrms.push_back( aT );
6106 list<SMESH_MeshEditor_PathPoint> LPP;
6107 //Extrusion_Error err =
6108 MakeEdgePathPoints(aPrms, aTrackEdge, aN1isOK, LPP);
6109 LLPPs.push_back(LPP);
6111 // update startN for search following egde
6112 if ( aN1isOK ) aVprev = aV2;
6117 list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
6118 list<SMESH_MeshEditor_PathPoint>& firstList = *itLLPP;
6119 fullList.splice( fullList.end(), firstList );
6121 SMESH_MeshEditor_PathPoint PP1 = fullList.back();
6122 fullList.pop_back();
6124 for(; itLLPP!=LLPPs.end(); itLLPP++) {
6125 list<SMESH_MeshEditor_PathPoint>& currList = *itLLPP;
6126 SMESH_MeshEditor_PathPoint PP2 = currList.front();
6127 gp_Dir D1 = PP1.Tangent();
6128 gp_Dir D2 = PP2.Tangent();
6129 gp_Dir Dnew( ( D1.XYZ() + D2.XYZ() ) / 2 );
6130 PP1.SetTangent(Dnew);
6131 fullList.push_back(PP1);
6132 fullList.splice( fullList.end(), currList, ++currList.begin(), currList.end() );
6133 PP1 = fullList.back();
6134 fullList.pop_back();
6136 // if wire not closed
6137 fullList.push_back(PP1);
6141 return EXTR_BAD_PATH_SHAPE;
6144 return MakeExtrElements(theElements, fullList, theHasAngles, theAngles, theLinearVariation,
6145 theHasRefPoint, theRefPoint, theMakeGroups);
6149 //=======================================================================
6150 //function : MakeEdgePathPoints
6151 //purpose : auxilary for ExtrusionAlongTrack
6152 //=======================================================================
6153 SMESH_MeshEditor::Extrusion_Error
6154 SMESH_MeshEditor::MakeEdgePathPoints(std::list<double>& aPrms,
6155 const TopoDS_Edge& aTrackEdge,
6157 list<SMESH_MeshEditor_PathPoint>& LPP)
6159 Standard_Real aTx1, aTx2, aL2, aTolVec, aTolVec2;
6161 aTolVec2=aTolVec*aTolVec;
6163 TopoDS_Vertex aV1, aV2;
6164 TopExp::Vertices( aTrackEdge, aV1, aV2 );
6165 aT1=BRep_Tool::Parameter( aV1, aTrackEdge );
6166 aT2=BRep_Tool::Parameter( aV2, aTrackEdge );
6167 // 2. Collect parameters on the track edge
6168 aPrms.push_front( aT1 );
6169 aPrms.push_back( aT2 );
6172 if( FirstIsStart ) {
6183 SMESH_MeshEditor_PathPoint aPP;
6184 Handle(Geom_Curve) aC3D = BRep_Tool::Curve( aTrackEdge, aTx1, aTx2 );
6185 std::list<double>::iterator aItD = aPrms.begin();
6186 for(; aItD != aPrms.end(); ++aItD) {
6190 aC3D->D1( aT, aP3D, aVec );
6191 aL2 = aVec.SquareMagnitude();
6192 if ( aL2 < aTolVec2 )
6193 return EXTR_CANT_GET_TANGENT;
6194 gp_Dir aTgt( aVec );
6196 aPP.SetTangent( aTgt );
6197 aPP.SetParameter( aT );
6204 //=======================================================================
6205 //function : MakeExtrElements
6206 //purpose : auxilary for ExtrusionAlongTrack
6207 //=======================================================================
6208 SMESH_MeshEditor::Extrusion_Error
6209 SMESH_MeshEditor::MakeExtrElements(TIDSortedElemSet& theElements,
6210 list<SMESH_MeshEditor_PathPoint>& fullList,
6211 const bool theHasAngles,
6212 list<double>& theAngles,
6213 const bool theLinearVariation,
6214 const bool theHasRefPoint,
6215 const gp_Pnt& theRefPoint,
6216 const bool theMakeGroups)
6218 const int aNbTP = fullList.size();
6220 if( theHasAngles && !theAngles.empty() && theLinearVariation )
6221 LinearAngleVariation(aNbTP-1, theAngles);
6222 // fill vector of path points with angles
6223 vector<SMESH_MeshEditor_PathPoint> aPPs;
6224 list<SMESH_MeshEditor_PathPoint>::iterator itPP = fullList.begin();
6225 list<double>::iterator itAngles = theAngles.begin();
6226 aPPs.push_back( *itPP++ );
6227 for( ; itPP != fullList.end(); itPP++) {
6228 aPPs.push_back( *itPP );
6229 if ( theHasAngles && itAngles != theAngles.end() )
6230 aPPs.back().SetAngle( *itAngles++ );
6233 TNodeOfNodeListMap mapNewNodes;
6234 TElemOfVecOfNnlmiMap mapElemNewNodes;
6235 TTElemOfElemListMap newElemsMap;
6236 TIDSortedElemSet::iterator itElem;
6237 // source elements for each generated one
6238 SMESH_SequenceOfElemPtr srcElems, srcNodes;
6240 // 3. Center of rotation aV0
6241 gp_Pnt aV0 = theRefPoint;
6242 if ( !theHasRefPoint )
6244 gp_XYZ aGC( 0.,0.,0. );
6245 TIDSortedElemSet newNodes;
6247 itElem = theElements.begin();
6248 for ( ; itElem != theElements.end(); itElem++ ) {
6249 const SMDS_MeshElement* elem = *itElem;
6251 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6252 while ( itN->more() ) {
6253 const SMDS_MeshElement* node = itN->next();
6254 if ( newNodes.insert( node ).second )
6255 aGC += SMESH_TNodeXYZ( node );
6258 aGC /= newNodes.size();
6260 } // if (!theHasRefPoint) {
6262 // 4. Processing the elements
6263 SMESHDS_Mesh* aMesh = GetMeshDS();
6265 for ( itElem = theElements.begin(); itElem != theElements.end(); itElem++ ) {
6266 // check element type
6267 const SMDS_MeshElement* elem = *itElem;
6268 SMDSAbs_ElementType aTypeE = elem->GetType();
6269 if ( !elem || ( aTypeE != SMDSAbs_Face && aTypeE != SMDSAbs_Edge ) )
6272 vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
6273 newNodesItVec.reserve( elem->NbNodes() );
6275 // loop on elem nodes
6277 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6278 while ( itN->more() )
6281 // check if a node has been already processed
6282 const SMDS_MeshNode* node =
6283 static_cast<const SMDS_MeshNode*>( itN->next() );
6284 TNodeOfNodeListMap::iterator nIt = mapNewNodes.find( node );
6285 if ( nIt == mapNewNodes.end() ) {
6286 nIt = mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
6287 list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
6290 Standard_Real aAngle1x, aAngleT1T0, aTolAng;
6291 gp_Pnt aP0x, aP1x, aPN0, aPN1, aV0x, aV1x;
6292 gp_Ax1 anAx1, anAxT1T0;
6293 gp_Dir aDT1x, aDT0x, aDT1T0;
6298 aPN0 = SMESH_TNodeXYZ( node );
6300 const SMESH_MeshEditor_PathPoint& aPP0 = aPPs[0];
6302 aDT0x= aPP0.Tangent();
6303 //cout<<"j = 0 PP: Pnt("<<aP0x.X()<<","<<aP0x.Y()<<","<<aP0x.Z()<<")"<<endl;
6305 for ( int j = 1; j < aNbTP; ++j ) {
6306 const SMESH_MeshEditor_PathPoint& aPP1 = aPPs[j];
6308 aDT1x = aPP1.Tangent();
6309 aAngle1x = aPP1.Angle();
6311 gp_Trsf aTrsf, aTrsfRot, aTrsfRotT1T0;
6313 gp_Vec aV01x( aP0x, aP1x );
6314 aTrsf.SetTranslation( aV01x );
6317 aV1x = aV0x.Transformed( aTrsf );
6318 aPN1 = aPN0.Transformed( aTrsf );
6320 // rotation 1 [ T1,T0 ]
6321 aAngleT1T0=-aDT1x.Angle( aDT0x );
6322 if (fabs(aAngleT1T0) > aTolAng) {
6324 anAxT1T0.SetLocation( aV1x );
6325 anAxT1T0.SetDirection( aDT1T0 );
6326 aTrsfRotT1T0.SetRotation( anAxT1T0, aAngleT1T0 );
6328 aPN1 = aPN1.Transformed( aTrsfRotT1T0 );
6332 if ( theHasAngles ) {
6333 anAx1.SetLocation( aV1x );
6334 anAx1.SetDirection( aDT1x );
6335 aTrsfRot.SetRotation( anAx1, aAngle1x );
6337 aPN1 = aPN1.Transformed( aTrsfRot );
6341 //MESSAGE("elem->IsQuadratic " << elem->IsQuadratic() << " " << elem->IsMediumNode(node));
6342 if( elem->IsQuadratic() && !elem->IsMediumNode(node) ) {
6343 // create additional node
6344 double x = ( aPN1.X() + aPN0.X() )/2.;
6345 double y = ( aPN1.Y() + aPN0.Y() )/2.;
6346 double z = ( aPN1.Z() + aPN0.Z() )/2.;
6347 const SMDS_MeshNode* newNode = aMesh->AddNode(x,y,z);
6348 myLastCreatedNodes.Append(newNode);
6349 srcNodes.Append( node );
6350 listNewNodes.push_back( newNode );
6352 const SMDS_MeshNode* newNode = aMesh->AddNode( aPN1.X(), aPN1.Y(), aPN1.Z() );
6353 myLastCreatedNodes.Append(newNode);
6354 srcNodes.Append( node );
6355 listNewNodes.push_back( newNode );
6365 // if current elem is quadratic and current node is not medium
6366 // we have to check - may be it is needed to insert additional nodes
6367 if( elem->IsQuadratic() && !elem->IsMediumNode(node) ) {
6368 list< const SMDS_MeshNode* > & listNewNodes = nIt->second;
6369 if(listNewNodes.size()==aNbTP-1) {
6370 vector<const SMDS_MeshNode*> aNodes(2*(aNbTP-1));
6371 gp_XYZ P(node->X(), node->Y(), node->Z());
6372 list< const SMDS_MeshNode* >::iterator it = listNewNodes.begin();
6374 for(i=0; i<aNbTP-1; i++) {
6375 const SMDS_MeshNode* N = *it;
6376 double x = ( N->X() + P.X() )/2.;
6377 double y = ( N->Y() + P.Y() )/2.;
6378 double z = ( N->Z() + P.Z() )/2.;
6379 const SMDS_MeshNode* newN = aMesh->AddNode(x,y,z);
6380 srcNodes.Append( node );
6381 myLastCreatedNodes.Append(newN);
6384 P = gp_XYZ(N->X(),N->Y(),N->Z());
6386 listNewNodes.clear();
6387 for(i=0; i<2*(aNbTP-1); i++) {
6388 listNewNodes.push_back(aNodes[i]);
6394 newNodesItVec.push_back( nIt );
6396 // make new elements
6397 //sweepElement( aMesh, elem, newNodesItVec, newElemsMap[elem],
6398 // newNodesItVec[0]->second.size(), myLastCreatedElems );
6399 sweepElement( elem, newNodesItVec, newElemsMap[elem], aNbTP-1, srcElems );
6402 makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElements, aNbTP-1, srcElems );
6404 if ( theMakeGroups )
6405 generateGroups( srcNodes, srcElems, "extruded");
6411 //=======================================================================
6412 //function : LinearAngleVariation
6413 //purpose : auxilary for ExtrusionAlongTrack
6414 //=======================================================================
6415 void SMESH_MeshEditor::LinearAngleVariation(const int nbSteps,
6416 list<double>& Angles)
6418 int nbAngles = Angles.size();
6419 if( nbSteps > nbAngles ) {
6420 vector<double> theAngles(nbAngles);
6421 list<double>::iterator it = Angles.begin();
6423 for(; it!=Angles.end(); it++) {
6425 theAngles[i] = (*it);
6428 double rAn2St = double( nbAngles ) / double( nbSteps );
6429 double angPrev = 0, angle;
6430 for ( int iSt = 0; iSt < nbSteps; ++iSt ) {
6431 double angCur = rAn2St * ( iSt+1 );
6432 double angCurFloor = floor( angCur );
6433 double angPrevFloor = floor( angPrev );
6434 if ( angPrevFloor == angCurFloor )
6435 angle = rAn2St * theAngles[ int( angCurFloor ) ];
6437 int iP = int( angPrevFloor );
6438 double angPrevCeil = ceil(angPrev);
6439 angle = ( angPrevCeil - angPrev ) * theAngles[ iP ];
6441 int iC = int( angCurFloor );
6442 if ( iC < nbAngles )
6443 angle += ( angCur - angCurFloor ) * theAngles[ iC ];
6445 iP = int( angPrevCeil );
6447 angle += theAngles[ iC ];
6449 res.push_back(angle);
6454 for(; it!=res.end(); it++)
6455 Angles.push_back( *it );
6460 //================================================================================
6462 * \brief Move or copy theElements applying theTrsf to their nodes
6463 * \param theElems - elements to transform, if theElems is empty then apply to all mesh nodes
6464 * \param theTrsf - transformation to apply
6465 * \param theCopy - if true, create translated copies of theElems
6466 * \param theMakeGroups - if true and theCopy, create translated groups
6467 * \param theTargetMesh - mesh to copy translated elements into
6468 * \return SMESH_MeshEditor::PGroupIDs - list of ids of created groups
6470 //================================================================================
6472 SMESH_MeshEditor::PGroupIDs
6473 SMESH_MeshEditor::Transform (TIDSortedElemSet & theElems,
6474 const gp_Trsf& theTrsf,
6476 const bool theMakeGroups,
6477 SMESH_Mesh* theTargetMesh)
6479 myLastCreatedElems.Clear();
6480 myLastCreatedNodes.Clear();
6482 bool needReverse = false;
6483 string groupPostfix;
6484 switch ( theTrsf.Form() ) {
6486 MESSAGE("gp_PntMirror");
6488 groupPostfix = "mirrored";
6491 MESSAGE("gp_Ax1Mirror");
6492 groupPostfix = "mirrored";
6495 MESSAGE("gp_Ax2Mirror");
6497 groupPostfix = "mirrored";
6500 MESSAGE("gp_Rotation");
6501 groupPostfix = "rotated";
6503 case gp_Translation:
6504 MESSAGE("gp_Translation");
6505 groupPostfix = "translated";
6508 MESSAGE("gp_Scale");
6509 groupPostfix = "scaled";
6511 case gp_CompoundTrsf: // different scale by axis
6512 MESSAGE("gp_CompoundTrsf");
6513 groupPostfix = "scaled";
6517 needReverse = false;
6518 groupPostfix = "transformed";
6521 SMESH_MeshEditor targetMeshEditor( theTargetMesh );
6522 SMESHDS_Mesh* aTgtMesh = theTargetMesh ? theTargetMesh->GetMeshDS() : 0;
6523 SMESHDS_Mesh* aMesh = GetMeshDS();
6526 // map old node to new one
6527 TNodeNodeMap nodeMap;
6529 // elements sharing moved nodes; those of them which have all
6530 // nodes mirrored but are not in theElems are to be reversed
6531 TIDSortedElemSet inverseElemSet;
6533 // source elements for each generated one
6534 SMESH_SequenceOfElemPtr srcElems, srcNodes;
6536 // issue 021015: EDF 1578 SMESH: Free nodes are removed when translating a mesh
6537 TIDSortedElemSet orphanNode;
6539 if ( theElems.empty() ) // transform the whole mesh
6542 SMDS_ElemIteratorPtr eIt = aMesh->elementsIterator();
6543 while ( eIt->more() ) theElems.insert( eIt->next() );
6545 SMDS_NodeIteratorPtr nIt = aMesh->nodesIterator();
6546 while ( nIt->more() )
6548 const SMDS_MeshNode* node = nIt->next();
6549 if ( node->NbInverseElements() == 0)
6550 orphanNode.insert( node );
6554 // loop on elements to transform nodes : first orphan nodes then elems
6555 TIDSortedElemSet::iterator itElem;
6556 TIDSortedElemSet *elements[] = {&orphanNode, &theElems };
6557 for (int i=0; i<2; i++)
6558 for ( itElem = elements[i]->begin(); itElem != elements[i]->end(); itElem++ ) {
6559 const SMDS_MeshElement* elem = *itElem;
6563 // loop on elem nodes
6564 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6565 while ( itN->more() ) {
6567 const SMDS_MeshNode* node = cast2Node( itN->next() );
6568 // check if a node has been already transformed
6569 pair<TNodeNodeMap::iterator,bool> n2n_isnew =
6570 nodeMap.insert( make_pair ( node, node ));
6571 if ( !n2n_isnew.second )
6575 coord[0] = node->X();
6576 coord[1] = node->Y();
6577 coord[2] = node->Z();
6578 theTrsf.Transforms( coord[0], coord[1], coord[2] );
6579 if ( theTargetMesh ) {
6580 const SMDS_MeshNode * newNode = aTgtMesh->AddNode( coord[0], coord[1], coord[2] );
6581 n2n_isnew.first->second = newNode;
6582 myLastCreatedNodes.Append(newNode);
6583 srcNodes.Append( node );
6585 else if ( theCopy ) {
6586 const SMDS_MeshNode * newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
6587 n2n_isnew.first->second = newNode;
6588 myLastCreatedNodes.Append(newNode);
6589 srcNodes.Append( node );
6592 aMesh->MoveNode( node, coord[0], coord[1], coord[2] );
6593 // node position on shape becomes invalid
6594 const_cast< SMDS_MeshNode* > ( node )->SetPosition
6595 ( SMDS_SpacePosition::originSpacePosition() );
6598 // keep inverse elements
6599 if ( !theCopy && !theTargetMesh && needReverse ) {
6600 SMDS_ElemIteratorPtr invElemIt = node->GetInverseElementIterator();
6601 while ( invElemIt->more() ) {
6602 const SMDS_MeshElement* iel = invElemIt->next();
6603 inverseElemSet.insert( iel );
6609 // either create new elements or reverse mirrored ones
6610 if ( !theCopy && !needReverse && !theTargetMesh )
6613 TIDSortedElemSet::iterator invElemIt = inverseElemSet.begin();
6614 for ( ; invElemIt != inverseElemSet.end(); invElemIt++ )
6615 theElems.insert( *invElemIt );
6617 // Replicate or reverse elements
6619 std::vector<int> iForw;
6620 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
6622 const SMDS_MeshElement* elem = *itElem;
6623 if ( !elem ) continue;
6625 SMDSAbs_GeometryType geomType = elem->GetGeomType();
6626 int nbNodes = elem->NbNodes();
6627 if ( geomType == SMDSGeom_NONE ) continue; // node
6629 switch ( geomType ) {
6631 case SMDSGeom_POLYGON: // ---------------------- polygon
6633 vector<const SMDS_MeshNode*> poly_nodes (nbNodes);
6635 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6636 while (itN->more()) {
6637 const SMDS_MeshNode* node =
6638 static_cast<const SMDS_MeshNode*>(itN->next());
6639 TNodeNodeMap::iterator nodeMapIt = nodeMap.find(node);
6640 if (nodeMapIt == nodeMap.end())
6641 break; // not all nodes transformed
6643 // reverse mirrored faces and volumes
6644 poly_nodes[nbNodes - iNode - 1] = (*nodeMapIt).second;
6646 poly_nodes[iNode] = (*nodeMapIt).second;
6650 if ( iNode != nbNodes )
6651 continue; // not all nodes transformed
6653 if ( theTargetMesh ) {
6654 myLastCreatedElems.Append(aTgtMesh->AddPolygonalFace(poly_nodes));
6655 srcElems.Append( elem );
6657 else if ( theCopy ) {
6658 myLastCreatedElems.Append(aMesh->AddPolygonalFace(poly_nodes));
6659 srcElems.Append( elem );
6662 aMesh->ChangePolygonNodes(elem, poly_nodes);
6667 case SMDSGeom_POLYHEDRA: // ------------------ polyhedral volume
6669 const SMDS_VtkVolume* aPolyedre =
6670 dynamic_cast<const SMDS_VtkVolume*>( elem );
6672 MESSAGE("Warning: bad volumic element");
6676 vector<const SMDS_MeshNode*> poly_nodes; poly_nodes.reserve( nbNodes );
6677 vector<int> quantities; quantities.reserve( nbNodes );
6679 bool allTransformed = true;
6680 int nbFaces = aPolyedre->NbFaces();
6681 for (int iface = 1; iface <= nbFaces && allTransformed; iface++) {
6682 int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
6683 for (int inode = 1; inode <= nbFaceNodes && allTransformed; inode++) {
6684 const SMDS_MeshNode* node = aPolyedre->GetFaceNode(iface, inode);
6685 TNodeNodeMap::iterator nodeMapIt = nodeMap.find(node);
6686 if (nodeMapIt == nodeMap.end()) {
6687 allTransformed = false; // not all nodes transformed
6689 poly_nodes.push_back((*nodeMapIt).second);
6691 if ( needReverse && allTransformed )
6692 std::reverse( poly_nodes.end() - nbFaceNodes, poly_nodes.end() );
6694 quantities.push_back(nbFaceNodes);
6696 if ( !allTransformed )
6697 continue; // not all nodes transformed
6699 if ( theTargetMesh ) {
6700 myLastCreatedElems.Append(aTgtMesh->AddPolyhedralVolume(poly_nodes, quantities));
6701 srcElems.Append( elem );
6703 else if ( theCopy ) {
6704 myLastCreatedElems.Append(aMesh->AddPolyhedralVolume(poly_nodes, quantities));
6705 srcElems.Append( elem );
6708 aMesh->ChangePolyhedronNodes(elem, poly_nodes, quantities);
6713 case SMDSGeom_BALL: // -------------------- Ball
6715 if ( !theCopy && !theTargetMesh ) continue;
6717 TNodeNodeMap::iterator nodeMapIt = nodeMap.find( elem->GetNode(0) );
6718 if (nodeMapIt == nodeMap.end())
6719 continue; // not all nodes transformed
6721 double diameter = static_cast<const SMDS_BallElement*>(elem)->GetDiameter();
6722 if ( theTargetMesh ) {
6723 myLastCreatedElems.Append(aTgtMesh->AddBall( nodeMapIt->second, diameter ));
6724 srcElems.Append( elem );
6727 myLastCreatedElems.Append(aMesh->AddBall( nodeMapIt->second, diameter ));
6728 srcElems.Append( elem );
6733 default: // ----------------------- Regular elements
6735 while ( iForw.size() < nbNodes ) iForw.push_back( iForw.size() );
6736 const std::vector<int>& iRev = SMDS_MeshCell::reverseSmdsOrder( elem->GetEntityType() );
6737 const std::vector<int>& i = needReverse ? iRev : iForw;
6739 // find transformed nodes
6740 vector<const SMDS_MeshNode*> nodes(nbNodes);
6742 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6743 while ( itN->more() ) {
6744 const SMDS_MeshNode* node =
6745 static_cast<const SMDS_MeshNode*>( itN->next() );
6746 TNodeNodeMap::iterator nodeMapIt = nodeMap.find( node );
6747 if ( nodeMapIt == nodeMap.end() )
6748 break; // not all nodes transformed
6749 nodes[ i [ iNode++ ]] = (*nodeMapIt).second;
6751 if ( iNode != nbNodes )
6752 continue; // not all nodes transformed
6754 if ( theTargetMesh ) {
6755 if ( SMDS_MeshElement* copy =
6756 targetMeshEditor.AddElement( nodes, elem->GetType(), elem->IsPoly() )) {
6757 myLastCreatedElems.Append( copy );
6758 srcElems.Append( elem );
6761 else if ( theCopy ) {
6762 if ( AddElement( nodes, elem->GetType(), elem->IsPoly() ))
6763 srcElems.Append( elem );
6766 // reverse element as it was reversed by transformation
6768 aMesh->ChangeElementNodes( elem, &nodes[0], nbNodes );
6770 } // switch ( geomType )
6772 } // loop on elements
6774 PGroupIDs newGroupIDs;
6776 if ( ( theMakeGroups && theCopy ) ||
6777 ( theMakeGroups && theTargetMesh ) )
6778 newGroupIDs = generateGroups( srcNodes, srcElems, groupPostfix, theTargetMesh, false );
6783 //=======================================================================
6785 * \brief Create groups of elements made during transformation
6786 * \param nodeGens - nodes making corresponding myLastCreatedNodes
6787 * \param elemGens - elements making corresponding myLastCreatedElems
6788 * \param postfix - to append to names of new groups
6789 * \param targetMesh - mesh to create groups in
6790 * \param topPresent - is there "top" elements that are created by sweeping
6792 //=======================================================================
6794 SMESH_MeshEditor::PGroupIDs
6795 SMESH_MeshEditor::generateGroups(const SMESH_SequenceOfElemPtr& nodeGens,
6796 const SMESH_SequenceOfElemPtr& elemGens,
6797 const std::string& postfix,
6798 SMESH_Mesh* targetMesh,
6799 const bool topPresent)
6801 PGroupIDs newGroupIDs( new list<int> );
6802 SMESH_Mesh* mesh = targetMesh ? targetMesh : GetMesh();
6804 // Sort existing groups by types and collect their names
6806 // containers to store an old group and generated new ones;
6807 // 1st new group is for result elems of different type than a source one;
6808 // 2nd new group is for same type result elems ("top" group at extrusion)
6810 using boost::make_tuple;
6811 typedef tuple< SMESHDS_GroupBase*, SMESHDS_Group*, SMESHDS_Group* > TOldNewGroup;
6812 vector< list< TOldNewGroup > > groupsByType( SMDSAbs_NbElementTypes );
6813 vector< TOldNewGroup* > orderedOldNewGroups; // in order of old groups
6815 set< string > groupNames;
6817 SMESH_Mesh::GroupIteratorPtr groupIt = GetMesh()->GetGroups();
6818 if ( !groupIt->more() ) return newGroupIDs;
6820 int newGroupID = mesh->GetGroupIds().back()+1;
6821 while ( groupIt->more() )
6823 SMESH_Group * group = groupIt->next();
6824 if ( !group ) continue;
6825 SMESHDS_GroupBase* groupDS = group->GetGroupDS();
6826 if ( !groupDS || groupDS->IsEmpty() ) continue;
6827 groupNames.insert ( group->GetName() );
6828 groupDS->SetStoreName( group->GetName() );
6829 const SMDSAbs_ElementType type = groupDS->GetType();
6830 SMESHDS_Group* newGroup = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
6831 SMESHDS_Group* newTopGroup = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
6832 groupsByType[ type ].push_back( make_tuple( groupDS, newGroup, newTopGroup ));
6833 orderedOldNewGroups.push_back( & groupsByType[ type ].back() );
6836 // Loop on nodes and elements to add them in new groups
6838 vector< const SMDS_MeshElement* > resultElems;
6839 for ( int isNodes = 0; isNodes < 2; ++isNodes )
6841 const SMESH_SequenceOfElemPtr& gens = isNodes ? nodeGens : elemGens;
6842 const SMESH_SequenceOfElemPtr& elems = isNodes ? myLastCreatedNodes : myLastCreatedElems;
6843 if ( gens.Length() != elems.Length() )
6844 throw SALOME_Exception("SMESH_MeshEditor::generateGroups(): invalid args");
6846 // loop on created elements
6847 for (int iElem = 1; iElem <= elems.Length(); ++iElem )
6849 const SMDS_MeshElement* sourceElem = gens( iElem );
6850 if ( !sourceElem ) {
6851 MESSAGE("generateGroups(): NULL source element");
6854 list< TOldNewGroup > & groupsOldNew = groupsByType[ sourceElem->GetType() ];
6855 if ( groupsOldNew.empty() ) { // no groups of this type at all
6856 while ( iElem < gens.Length() && gens( iElem+1 ) == sourceElem )
6857 ++iElem; // skip all elements made by sourceElem
6860 // collect all elements made by the iElem-th sourceElem
6861 resultElems.clear();
6862 if ( const SMDS_MeshElement* resElem = elems( iElem ))
6863 if ( resElem != sourceElem )
6864 resultElems.push_back( resElem );
6865 while ( iElem < gens.Length() && gens( iElem+1 ) == sourceElem )
6866 if ( const SMDS_MeshElement* resElem = elems( ++iElem ))
6867 if ( resElem != sourceElem )
6868 resultElems.push_back( resElem );
6870 const SMDS_MeshElement* topElem = 0;
6871 if ( isNodes ) // there must be a top element
6873 topElem = resultElems.back();
6874 resultElems.pop_back();
6878 vector< const SMDS_MeshElement* >::reverse_iterator resElemIt = resultElems.rbegin();
6879 for ( ; resElemIt != resultElems.rend() ; ++resElemIt )
6880 if ( (*resElemIt)->GetType() == sourceElem->GetType() )
6882 topElem = *resElemIt;
6883 *resElemIt = 0; // erase *resElemIt
6887 // add resultElems to groups originted from ones the sourceElem belongs to
6888 list< TOldNewGroup >::iterator gOldNew, gLast = groupsOldNew.end();
6889 for ( gOldNew = groupsOldNew.begin(); gOldNew != gLast; ++gOldNew )
6891 SMESHDS_GroupBase* oldGroup = gOldNew->get<0>();
6892 if ( oldGroup->Contains( sourceElem )) // sourceElem is in oldGroup
6894 // fill in a new group
6895 SMDS_MeshGroup & newGroup = gOldNew->get<1>()->SMDSGroup();
6896 vector< const SMDS_MeshElement* >::iterator resLast = resultElems.end(), resElemIt;
6897 for ( resElemIt = resultElems.begin(); resElemIt != resLast; ++resElemIt )
6899 newGroup.Add( *resElemIt );
6901 // fill a "top" group
6904 SMDS_MeshGroup & newTopGroup = gOldNew->get<2>()->SMDSGroup();
6905 newTopGroup.Add( topElem );
6909 } // loop on created elements
6910 }// loop on nodes and elements
6912 // Create new SMESH_Groups from SMESHDS_Groups and remove empty SMESHDS_Groups
6914 list<int> topGrouIds;
6915 for ( size_t i = 0; i < orderedOldNewGroups.size(); ++i )
6917 SMESHDS_GroupBase* oldGroupDS = orderedOldNewGroups[i]->get<0>();
6918 SMESHDS_Group* newGroups[2] = { orderedOldNewGroups[i]->get<1>(),
6919 orderedOldNewGroups[i]->get<2>() };
6920 for ( int is2nd = 0; is2nd < 2; ++is2nd )
6922 SMESHDS_Group* newGroupDS = newGroups[ is2nd ];
6923 if ( newGroupDS->IsEmpty() )
6925 mesh->GetMeshDS()->RemoveGroup( newGroupDS );
6930 newGroupDS->SetType( newGroupDS->GetElements()->next()->GetType() );
6933 const bool isTop = ( topPresent &&
6934 newGroupDS->GetType() == oldGroupDS->GetType() &&
6937 string name = oldGroupDS->GetStoreName();
6938 { // remove trailing whitespaces (issue 22599)
6939 size_t size = name.size();
6940 while ( size > 1 && isspace( name[ size-1 ]))
6942 if ( size != name.size() )
6944 name.resize( size );
6945 oldGroupDS->SetStoreName( name.c_str() );
6948 if ( !targetMesh ) {
6949 string suffix = ( isTop ? "top": postfix.c_str() );
6953 while ( !groupNames.insert( name ).second ) // name exists
6954 name = SMESH_Comment( oldGroupDS->GetStoreName() ) << "_" << suffix << "_" << nb++;
6959 newGroupDS->SetStoreName( name.c_str() );
6961 // make a SMESH_Groups
6962 mesh->AddGroup( newGroupDS );
6964 topGrouIds.push_back( newGroupDS->GetID() );
6966 newGroupIDs->push_back( newGroupDS->GetID() );
6970 newGroupIDs->splice( newGroupIDs->end(), topGrouIds );
6975 //================================================================================
6977 * \brief Return list of group of nodes close to each other within theTolerance
6978 * Search among theNodes or in the whole mesh if theNodes is empty using
6979 * an Octree algorithm
6981 //================================================================================
6983 void SMESH_MeshEditor::FindCoincidentNodes (TIDSortedNodeSet & theNodes,
6984 const double theTolerance,
6985 TListOfListOfNodes & theGroupsOfNodes)
6987 myLastCreatedElems.Clear();
6988 myLastCreatedNodes.Clear();
6990 if ( theNodes.empty() )
6991 { // get all nodes in the mesh
6992 SMDS_NodeIteratorPtr nIt = GetMeshDS()->nodesIterator(/*idInceasingOrder=*/true);
6993 while ( nIt->more() )
6994 theNodes.insert( theNodes.end(),nIt->next());
6997 SMESH_OctreeNode::FindCoincidentNodes ( theNodes, &theGroupsOfNodes, theTolerance);
7000 //=======================================================================
7001 //function : SimplifyFace
7003 //=======================================================================
7005 int SMESH_MeshEditor::SimplifyFace (const vector<const SMDS_MeshNode *>& faceNodes,
7006 vector<const SMDS_MeshNode *>& poly_nodes,
7007 vector<int>& quantities) const
7009 int nbNodes = faceNodes.size();
7014 set<const SMDS_MeshNode*> nodeSet;
7016 // get simple seq of nodes
7017 //const SMDS_MeshNode* simpleNodes[ nbNodes ];
7018 vector<const SMDS_MeshNode*> simpleNodes( nbNodes );
7019 int iSimple = 0, nbUnique = 0;
7021 simpleNodes[iSimple++] = faceNodes[0];
7023 for (int iCur = 1; iCur < nbNodes; iCur++) {
7024 if (faceNodes[iCur] != simpleNodes[iSimple - 1]) {
7025 simpleNodes[iSimple++] = faceNodes[iCur];
7026 if (nodeSet.insert( faceNodes[iCur] ).second)
7030 int nbSimple = iSimple;
7031 if (simpleNodes[nbSimple - 1] == simpleNodes[0]) {
7041 bool foundLoop = (nbSimple > nbUnique);
7044 set<const SMDS_MeshNode*> loopSet;
7045 for (iSimple = 0; iSimple < nbSimple && !foundLoop; iSimple++) {
7046 const SMDS_MeshNode* n = simpleNodes[iSimple];
7047 if (!loopSet.insert( n ).second) {
7051 int iC = 0, curLast = iSimple;
7052 for (; iC < curLast; iC++) {
7053 if (simpleNodes[iC] == n) break;
7055 int loopLen = curLast - iC;
7057 // create sub-element
7059 quantities.push_back(loopLen);
7060 for (; iC < curLast; iC++) {
7061 poly_nodes.push_back(simpleNodes[iC]);
7064 // shift the rest nodes (place from the first loop position)
7065 for (iC = curLast + 1; iC < nbSimple; iC++) {
7066 simpleNodes[iC - loopLen] = simpleNodes[iC];
7068 nbSimple -= loopLen;
7071 } // for (iSimple = 0; iSimple < nbSimple; iSimple++)
7072 } // while (foundLoop)
7076 quantities.push_back(iSimple);
7077 for (int i = 0; i < iSimple; i++)
7078 poly_nodes.push_back(simpleNodes[i]);
7084 //=======================================================================
7085 //function : MergeNodes
7086 //purpose : In each group, the cdr of nodes are substituted by the first one
7088 //=======================================================================
7090 void SMESH_MeshEditor::MergeNodes (TListOfListOfNodes & theGroupsOfNodes)
7092 MESSAGE("MergeNodes");
7093 myLastCreatedElems.Clear();
7094 myLastCreatedNodes.Clear();
7096 SMESHDS_Mesh* aMesh = GetMeshDS();
7098 TNodeNodeMap nodeNodeMap; // node to replace - new node
7099 set<const SMDS_MeshElement*> elems; // all elements with changed nodes
7100 list< int > rmElemIds, rmNodeIds;
7102 // Fill nodeNodeMap and elems
7104 TListOfListOfNodes::iterator grIt = theGroupsOfNodes.begin();
7105 for ( ; grIt != theGroupsOfNodes.end(); grIt++ ) {
7106 list<const SMDS_MeshNode*>& nodes = *grIt;
7107 list<const SMDS_MeshNode*>::iterator nIt = nodes.begin();
7108 const SMDS_MeshNode* nToKeep = *nIt;
7109 //MESSAGE("node to keep " << nToKeep->GetID());
7110 for ( ++nIt; nIt != nodes.end(); nIt++ ) {
7111 const SMDS_MeshNode* nToRemove = *nIt;
7112 nodeNodeMap.insert( TNodeNodeMap::value_type( nToRemove, nToKeep ));
7113 if ( nToRemove != nToKeep ) {
7114 //MESSAGE(" node to remove " << nToRemove->GetID());
7115 rmNodeIds.push_back( nToRemove->GetID() );
7116 AddToSameGroups( nToKeep, nToRemove, aMesh );
7117 // set _alwaysComputed to a sub-mesh of VERTEX to enable mesh computing
7118 // after MergeNodes() w/o creating node in place of merged ones.
7119 const SMDS_PositionPtr& pos = nToRemove->GetPosition();
7120 if ( pos && pos->GetTypeOfPosition() == SMDS_TOP_VERTEX )
7121 if ( SMESH_subMesh* sm = myMesh->GetSubMeshContaining( nToRemove->getshapeId() ))
7122 sm->SetIsAlwaysComputed( true );
7125 SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
7126 while ( invElemIt->more() ) {
7127 const SMDS_MeshElement* elem = invElemIt->next();
7132 // Change element nodes or remove an element
7134 set<const SMDS_MeshElement*>::iterator eIt = elems.begin();
7135 for ( ; eIt != elems.end(); eIt++ ) {
7136 const SMDS_MeshElement* elem = *eIt;
7137 //MESSAGE(" ---- inverse elem on node to remove " << elem->GetID());
7138 int nbNodes = elem->NbNodes();
7139 int aShapeId = FindShape( elem );
7141 set<const SMDS_MeshNode*> nodeSet;
7142 vector< const SMDS_MeshNode*> curNodes( nbNodes ), uniqueNodes( nbNodes );
7143 int iUnique = 0, iCur = 0, nbRepl = 0;
7144 vector<int> iRepl( nbNodes );
7146 // get new seq of nodes
7147 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
7148 while ( itN->more() ) {
7149 const SMDS_MeshNode* n =
7150 static_cast<const SMDS_MeshNode*>( itN->next() );
7152 TNodeNodeMap::iterator nnIt = nodeNodeMap.find( n );
7153 if ( nnIt != nodeNodeMap.end() ) { // n sticks
7155 // BUG 0020185: begin
7157 bool stopRecur = false;
7158 set<const SMDS_MeshNode*> nodesRecur;
7159 nodesRecur.insert(n);
7160 while (!stopRecur) {
7161 TNodeNodeMap::iterator nnIt_i = nodeNodeMap.find( n );
7162 if ( nnIt_i != nodeNodeMap.end() ) { // n sticks
7163 n = (*nnIt_i).second;
7164 if (!nodesRecur.insert(n).second) {
7165 // error: recursive dependancy
7175 curNodes[ iCur ] = n;
7176 bool isUnique = nodeSet.insert( n ).second;
7178 uniqueNodes[ iUnique++ ] = n;
7180 iRepl[ nbRepl++ ] = iCur;
7184 // Analyse element topology after replacement
7187 int nbUniqueNodes = nodeSet.size();
7188 //MESSAGE("nbNodes nbUniqueNodes " << nbNodes << " " << nbUniqueNodes);
7189 if ( nbNodes != nbUniqueNodes ) { // some nodes stick
7190 // Polygons and Polyhedral volumes
7191 if (elem->IsPoly()) {
7193 if (elem->GetType() == SMDSAbs_Face) {
7195 vector<const SMDS_MeshNode *> face_nodes (nbNodes);
7197 for (; inode < nbNodes; inode++) {
7198 face_nodes[inode] = curNodes[inode];
7201 vector<const SMDS_MeshNode *> polygons_nodes;
7202 vector<int> quantities;
7203 int nbNew = SimplifyFace(face_nodes, polygons_nodes, quantities);
7206 for (int iface = 0; iface < nbNew; iface++) {
7207 int nbNodes = quantities[iface];
7208 vector<const SMDS_MeshNode *> poly_nodes (nbNodes);
7209 for (int ii = 0; ii < nbNodes; ii++, inode++) {
7210 poly_nodes[ii] = polygons_nodes[inode];
7212 SMDS_MeshElement* newElem = aMesh->AddPolygonalFace(poly_nodes);
7213 myLastCreatedElems.Append(newElem);
7215 aMesh->SetMeshElementOnShape(newElem, aShapeId);
7218 MESSAGE("ChangeElementNodes MergeNodes Polygon");
7219 //aMesh->ChangeElementNodes(elem, &polygons_nodes[inode], quantities[nbNew - 1]);
7220 vector<const SMDS_MeshNode *> polynodes(polygons_nodes.begin()+inode,polygons_nodes.end());
7222 if (nbNew > 0) quid = nbNew - 1;
7223 vector<int> newquant(quantities.begin()+quid, quantities.end());
7224 const SMDS_MeshElement* newElem = 0;
7225 newElem = aMesh->AddPolyhedralVolume(polynodes, newquant);
7226 myLastCreatedElems.Append(newElem);
7227 if ( aShapeId && newElem )
7228 aMesh->SetMeshElementOnShape( newElem, aShapeId );
7229 rmElemIds.push_back(elem->GetID());
7232 rmElemIds.push_back(elem->GetID());
7236 else if (elem->GetType() == SMDSAbs_Volume) {
7237 // Polyhedral volume
7238 if (nbUniqueNodes < 4) {
7239 rmElemIds.push_back(elem->GetID());
7242 // each face has to be analyzed in order to check volume validity
7243 const SMDS_VtkVolume* aPolyedre =
7244 dynamic_cast<const SMDS_VtkVolume*>( elem );
7246 int nbFaces = aPolyedre->NbFaces();
7248 vector<const SMDS_MeshNode *> poly_nodes;
7249 vector<int> quantities;
7251 for (int iface = 1; iface <= nbFaces; iface++) {
7252 int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
7253 vector<const SMDS_MeshNode *> faceNodes (nbFaceNodes);
7255 for (int inode = 1; inode <= nbFaceNodes; inode++) {
7256 const SMDS_MeshNode * faceNode = aPolyedre->GetFaceNode(iface, inode);
7257 TNodeNodeMap::iterator nnIt = nodeNodeMap.find(faceNode);
7258 if (nnIt != nodeNodeMap.end()) { // faceNode sticks
7259 faceNode = (*nnIt).second;
7261 faceNodes[inode - 1] = faceNode;
7264 SimplifyFace(faceNodes, poly_nodes, quantities);
7267 if (quantities.size() > 3) {
7268 // to be done: remove coincident faces
7271 if (quantities.size() > 3)
7273 MESSAGE("ChangeElementNodes MergeNodes Polyhedron");
7274 //aMesh->ChangePolyhedronNodes(elem, poly_nodes, quantities);
7275 const SMDS_MeshElement* newElem = 0;
7276 newElem = aMesh->AddPolyhedralVolume(poly_nodes, quantities);
7277 myLastCreatedElems.Append(newElem);
7278 if ( aShapeId && newElem )
7279 aMesh->SetMeshElementOnShape( newElem, aShapeId );
7280 rmElemIds.push_back(elem->GetID());
7284 rmElemIds.push_back(elem->GetID());
7295 // TODO not all the possible cases are solved. Find something more generic?
7296 switch ( nbNodes ) {
7297 case 2: ///////////////////////////////////// EDGE
7298 isOk = false; break;
7299 case 3: ///////////////////////////////////// TRIANGLE
7300 isOk = false; break;
7302 if ( elem->GetType() == SMDSAbs_Volume ) // TETRAHEDRON
7304 else { //////////////////////////////////// QUADRANGLE
7305 if ( nbUniqueNodes < 3 )
7307 else if ( nbRepl == 2 && iRepl[ 1 ] - iRepl[ 0 ] == 2 )
7308 isOk = false; // opposite nodes stick
7309 //MESSAGE("isOk " << isOk);
7312 case 6: ///////////////////////////////////// PENTAHEDRON
7313 if ( nbUniqueNodes == 4 ) {
7314 // ---------------------------------> tetrahedron
7316 iRepl[ 0 ] > 2 && iRepl[ 1 ] > 2 && iRepl[ 2 ] > 2 ) {
7317 // all top nodes stick: reverse a bottom
7318 uniqueNodes[ 0 ] = curNodes [ 1 ];
7319 uniqueNodes[ 1 ] = curNodes [ 0 ];
7321 else if (nbRepl == 3 &&
7322 iRepl[ 0 ] < 3 && iRepl[ 1 ] < 3 && iRepl[ 2 ] < 3 ) {
7323 // all bottom nodes stick: set a top before
7324 uniqueNodes[ 3 ] = uniqueNodes [ 0 ];
7325 uniqueNodes[ 0 ] = curNodes [ 3 ];
7326 uniqueNodes[ 1 ] = curNodes [ 4 ];
7327 uniqueNodes[ 2 ] = curNodes [ 5 ];
7329 else if (nbRepl == 4 &&
7330 iRepl[ 2 ] - iRepl [ 0 ] == 3 && iRepl[ 3 ] - iRepl [ 1 ] == 3 ) {
7331 // a lateral face turns into a line: reverse a bottom
7332 uniqueNodes[ 0 ] = curNodes [ 1 ];
7333 uniqueNodes[ 1 ] = curNodes [ 0 ];
7338 else if ( nbUniqueNodes == 5 ) {
7339 // PENTAHEDRON --------------------> 2 tetrahedrons
7340 if ( nbRepl == 2 && iRepl[ 1 ] - iRepl [ 0 ] == 3 ) {
7341 // a bottom node sticks with a linked top one
7343 SMDS_MeshElement* newElem =
7344 aMesh->AddVolume(curNodes[ 3 ],
7347 curNodes[ iRepl[ 0 ] == 2 ? 1 : 2 ]);
7348 myLastCreatedElems.Append(newElem);
7350 aMesh->SetMeshElementOnShape( newElem, aShapeId );
7351 // 2. : reverse a bottom
7352 uniqueNodes[ 0 ] = curNodes [ 1 ];
7353 uniqueNodes[ 1 ] = curNodes [ 0 ];
7363 if(elem->IsQuadratic()) { // Quadratic quadrangle
7375 MESSAGE("nbRepl=2: " << iRepl[0] << " " << iRepl[1]);
7378 MESSAGE("nbRepl=3: " << iRepl[0] << " " << iRepl[1] << " " << iRepl[2]);
7380 if( iRepl[0]==0 && iRepl[1]==1 && iRepl[2]==4 ) {
7381 uniqueNodes[0] = curNodes[0];
7382 uniqueNodes[1] = curNodes[2];
7383 uniqueNodes[2] = curNodes[3];
7384 uniqueNodes[3] = curNodes[5];
7385 uniqueNodes[4] = curNodes[6];
7386 uniqueNodes[5] = curNodes[7];
7389 if( iRepl[0]==0 && iRepl[1]==3 && iRepl[2]==7 ) {
7390 uniqueNodes[0] = curNodes[0];
7391 uniqueNodes[1] = curNodes[1];
7392 uniqueNodes[2] = curNodes[2];
7393 uniqueNodes[3] = curNodes[4];
7394 uniqueNodes[4] = curNodes[5];
7395 uniqueNodes[5] = curNodes[6];
7398 if( iRepl[0]==0 && iRepl[1]==4 && iRepl[2]==7 ) {
7399 uniqueNodes[0] = curNodes[1];
7400 uniqueNodes[1] = curNodes[2];
7401 uniqueNodes[2] = curNodes[3];
7402 uniqueNodes[3] = curNodes[5];
7403 uniqueNodes[4] = curNodes[6];
7404 uniqueNodes[5] = curNodes[0];
7407 if( iRepl[0]==1 && iRepl[1]==2 && iRepl[2]==5 ) {
7408 uniqueNodes[0] = curNodes[0];
7409 uniqueNodes[1] = curNodes[1];
7410 uniqueNodes[2] = curNodes[3];
7411 uniqueNodes[3] = curNodes[4];
7412 uniqueNodes[4] = curNodes[6];
7413 uniqueNodes[5] = curNodes[7];
7416 if( iRepl[0]==1 && iRepl[1]==4 && iRepl[2]==5 ) {
7417 uniqueNodes[0] = curNodes[0];
7418 uniqueNodes[1] = curNodes[2];
7419 uniqueNodes[2] = curNodes[3];
7420 uniqueNodes[3] = curNodes[1];
7421 uniqueNodes[4] = curNodes[6];
7422 uniqueNodes[5] = curNodes[7];
7425 if( iRepl[0]==2 && iRepl[1]==3 && iRepl[2]==6 ) {
7426 uniqueNodes[0] = curNodes[0];
7427 uniqueNodes[1] = curNodes[1];
7428 uniqueNodes[2] = curNodes[2];
7429 uniqueNodes[3] = curNodes[4];
7430 uniqueNodes[4] = curNodes[5];
7431 uniqueNodes[5] = curNodes[7];
7434 if( iRepl[0]==2 && iRepl[1]==5 && iRepl[2]==6 ) {
7435 uniqueNodes[0] = curNodes[0];
7436 uniqueNodes[1] = curNodes[1];
7437 uniqueNodes[2] = curNodes[3];
7438 uniqueNodes[3] = curNodes[4];
7439 uniqueNodes[4] = curNodes[2];
7440 uniqueNodes[5] = curNodes[7];
7443 if( iRepl[0]==3 && iRepl[1]==6 && iRepl[2]==7 ) {
7444 uniqueNodes[0] = curNodes[0];
7445 uniqueNodes[1] = curNodes[1];
7446 uniqueNodes[2] = curNodes[2];
7447 uniqueNodes[3] = curNodes[4];
7448 uniqueNodes[4] = curNodes[5];
7449 uniqueNodes[5] = curNodes[3];
7454 MESSAGE("nbRepl=4: " << iRepl[0] << " " << iRepl[1] << " " << iRepl[2] << " " << iRepl[3]);
7457 MESSAGE("nbRepl=5: " << iRepl[0] << " " << iRepl[1] << " " << iRepl[2] << " " << iRepl[3] << " " << iRepl[4]);
7461 //////////////////////////////////// HEXAHEDRON
7463 SMDS_VolumeTool hexa (elem);
7464 hexa.SetExternalNormal();
7465 if ( nbUniqueNodes == 4 && nbRepl == 4 ) {
7466 //////////////////////// HEX ---> 1 tetrahedron
7467 for ( int iFace = 0; iFace < 6; iFace++ ) {
7468 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7469 if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7470 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7471 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7472 // one face turns into a point ...
7473 int iOppFace = hexa.GetOppFaceIndex( iFace );
7474 ind = hexa.GetFaceNodesIndices( iOppFace );
7476 for ( iCur = 0; iCur < 4 && nbStick < 2; iCur++ ) {
7477 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7480 if ( nbStick == 1 ) {
7481 // ... and the opposite one - into a triangle.
7483 ind = hexa.GetFaceNodesIndices( iFace );
7484 uniqueNodes[ 3 ] = curNodes[ind[ 0 ]];
7491 else if ( nbUniqueNodes == 6 && nbRepl == 2 ) {
7492 //////////////////////// HEX ---> 1 prism
7493 int nbTria = 0, iTria[3];
7494 const int *ind; // indices of face nodes
7495 // look for triangular faces
7496 for ( int iFace = 0; iFace < 6 && nbTria < 3; iFace++ ) {
7497 ind = hexa.GetFaceNodesIndices( iFace );
7498 TIDSortedNodeSet faceNodes;
7499 for ( iCur = 0; iCur < 4; iCur++ )
7500 faceNodes.insert( curNodes[ind[iCur]] );
7501 if ( faceNodes.size() == 3 )
7502 iTria[ nbTria++ ] = iFace;
7504 // check if triangles are opposite
7505 if ( nbTria == 2 && iTria[0] == hexa.GetOppFaceIndex( iTria[1] ))
7508 // set nodes of the bottom triangle
7509 ind = hexa.GetFaceNodesIndices( iTria[ 0 ]);
7511 for ( iCur = 0; iCur < 4; iCur++ )
7512 if ( ind[iCur] != iRepl[0] && ind[iCur] != iRepl[1])
7513 indB.push_back( ind[iCur] );
7514 if ( !hexa.IsForward() )
7515 std::swap( indB[0], indB[2] );
7516 for ( iCur = 0; iCur < 3; iCur++ )
7517 uniqueNodes[ iCur ] = curNodes[indB[iCur]];
7518 // set nodes of the top triangle
7519 const int *indT = hexa.GetFaceNodesIndices( iTria[ 1 ]);
7520 for ( iCur = 0; iCur < 3; ++iCur )
7521 for ( int j = 0; j < 4; ++j )
7522 if ( hexa.IsLinked( indB[ iCur ], indT[ j ] ))
7524 uniqueNodes[ iCur + 3 ] = curNodes[ indT[ j ]];
7530 else if (nbUniqueNodes == 5 && nbRepl == 4 ) {
7531 //////////////////// HEXAHEDRON ---> 2 tetrahedrons
7532 for ( int iFace = 0; iFace < 6; iFace++ ) {
7533 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7534 if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7535 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7536 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7537 // one face turns into a point ...
7538 int iOppFace = hexa.GetOppFaceIndex( iFace );
7539 ind = hexa.GetFaceNodesIndices( iOppFace );
7541 iUnique = 2; // reverse a tetrahedron 1 bottom
7542 for ( iCur = 0; iCur < 4 && nbStick == 0; iCur++ ) {
7543 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7545 else if ( iUnique >= 0 )
7546 uniqueNodes[ iUnique-- ] = curNodes[ind[ iCur ]];
7548 if ( nbStick == 0 ) {
7549 // ... and the opposite one is a quadrangle
7551 const int* indTop = hexa.GetFaceNodesIndices( iFace );
7552 uniqueNodes[ 3 ] = curNodes[indTop[ 0 ]];
7555 SMDS_MeshElement* newElem =
7556 aMesh->AddVolume(curNodes[ind[ 0 ]],
7559 curNodes[indTop[ 0 ]]);
7560 myLastCreatedElems.Append(newElem);
7562 aMesh->SetMeshElementOnShape( newElem, aShapeId );
7569 else if ( nbUniqueNodes == 6 && nbRepl == 4 ) {
7570 ////////////////// HEXAHEDRON ---> 2 tetrahedrons or 1 prism
7571 // find indices of quad and tri faces
7572 int iQuadFace[ 6 ], iTriFace[ 6 ], nbQuad = 0, nbTri = 0, iFace;
7573 for ( iFace = 0; iFace < 6; iFace++ ) {
7574 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7576 for ( iCur = 0; iCur < 4; iCur++ )
7577 nodeSet.insert( curNodes[ind[ iCur ]] );
7578 nbUniqueNodes = nodeSet.size();
7579 if ( nbUniqueNodes == 3 )
7580 iTriFace[ nbTri++ ] = iFace;
7581 else if ( nbUniqueNodes == 4 )
7582 iQuadFace[ nbQuad++ ] = iFace;
7584 if (nbQuad == 2 && nbTri == 4 &&
7585 hexa.GetOppFaceIndex( iQuadFace[ 0 ] ) == iQuadFace[ 1 ]) {
7586 // 2 opposite quadrangles stuck with a diagonal;
7587 // sample groups of merged indices: (0-4)(2-6)
7588 // --------------------------------------------> 2 tetrahedrons
7589 const int *ind1 = hexa.GetFaceNodesIndices( iQuadFace[ 0 ]); // indices of quad1 nodes
7590 const int *ind2 = hexa.GetFaceNodesIndices( iQuadFace[ 1 ]);
7591 int i0, i1d, i2, i3d, i0t, i2t; // d-daigonal, t-top
7592 if (curNodes[ind1[ 0 ]] == curNodes[ind2[ 0 ]] &&
7593 curNodes[ind1[ 2 ]] == curNodes[ind2[ 2 ]]) {
7594 // stuck with 0-2 diagonal
7602 else if (curNodes[ind1[ 1 ]] == curNodes[ind2[ 3 ]] &&
7603 curNodes[ind1[ 3 ]] == curNodes[ind2[ 1 ]]) {
7604 // stuck with 1-3 diagonal
7616 uniqueNodes[ 0 ] = curNodes [ i0 ];
7617 uniqueNodes[ 1 ] = curNodes [ i1d ];
7618 uniqueNodes[ 2 ] = curNodes [ i3d ];
7619 uniqueNodes[ 3 ] = curNodes [ i0t ];
7622 SMDS_MeshElement* newElem = aMesh->AddVolume(curNodes[ i1d ],
7626 myLastCreatedElems.Append(newElem);
7628 aMesh->SetMeshElementOnShape( newElem, aShapeId );
7631 else if (( nbTri == 2 && nbQuad == 3 ) || // merged (0-4)(1-5)
7632 ( nbTri == 4 && nbQuad == 2 )) { // merged (7-4)(1-5)
7633 // --------------------------------------------> prism
7634 // find 2 opposite triangles
7636 for ( iFace = 0; iFace + 1 < nbTri; iFace++ ) {
7637 if ( hexa.GetOppFaceIndex( iTriFace[ iFace ] ) == iTriFace[ iFace + 1 ]) {
7638 // find indices of kept and replaced nodes
7639 // and fill unique nodes of 2 opposite triangles
7640 const int *ind1 = hexa.GetFaceNodesIndices( iTriFace[ iFace ]);
7641 const int *ind2 = hexa.GetFaceNodesIndices( iTriFace[ iFace + 1 ]);
7642 const SMDS_MeshNode** hexanodes = hexa.GetNodes();
7643 // fill unique nodes
7646 for ( iCur = 0; iCur < 4 && isOk; iCur++ ) {
7647 const SMDS_MeshNode* n = curNodes[ind1[ iCur ]];
7648 const SMDS_MeshNode* nInit = hexanodes[ind1[ iCur ]];
7650 // iCur of a linked node of the opposite face (make normals co-directed):
7651 int iCurOpp = ( iCur == 1 || iCur == 3 ) ? 4 - iCur : iCur;
7652 // check that correspondent corners of triangles are linked
7653 if ( !hexa.IsLinked( ind1[ iCur ], ind2[ iCurOpp ] ))
7656 uniqueNodes[ iUnique ] = n;
7657 uniqueNodes[ iUnique + 3 ] = curNodes[ind2[ iCurOpp ]];
7666 } // if ( nbUniqueNodes == 6 && nbRepl == 4 )
7669 MESSAGE("MergeNodes() removes hexahedron "<< elem);
7676 } // switch ( nbNodes )
7678 } // if ( nbNodes != nbUniqueNodes ) // some nodes stick
7680 if ( isOk ) { // the elem remains valid after sticking nodes
7681 if (elem->IsPoly() && elem->GetType() == SMDSAbs_Volume)
7683 // Change nodes of polyedre
7684 const SMDS_VtkVolume* aPolyedre =
7685 dynamic_cast<const SMDS_VtkVolume*>( elem );
7687 int nbFaces = aPolyedre->NbFaces();
7689 vector<const SMDS_MeshNode *> poly_nodes;
7690 vector<int> quantities (nbFaces);
7692 for (int iface = 1; iface <= nbFaces; iface++) {
7693 int inode, nbFaceNodes = aPolyedre->NbFaceNodes(iface);
7694 quantities[iface - 1] = nbFaceNodes;
7696 for (inode = 1; inode <= nbFaceNodes; inode++) {
7697 const SMDS_MeshNode* curNode = aPolyedre->GetFaceNode(iface, inode);
7699 TNodeNodeMap::iterator nnIt = nodeNodeMap.find( curNode );
7700 if (nnIt != nodeNodeMap.end()) { // curNode sticks
7701 curNode = (*nnIt).second;
7703 poly_nodes.push_back(curNode);
7706 aMesh->ChangePolyhedronNodes( elem, poly_nodes, quantities );
7709 else // replace non-polyhedron elements
7711 const SMDSAbs_ElementType etyp = elem->GetType();
7712 const int elemId = elem->GetID();
7713 const bool isPoly = (elem->GetEntityType() == SMDSEntity_Polygon);
7714 uniqueNodes.resize(nbUniqueNodes);
7716 SMESHDS_SubMesh * sm = aShapeId > 0 ? aMesh->MeshElements(aShapeId) : 0;
7718 aMesh->RemoveFreeElement(elem, sm, /*fromGroups=*/false);
7719 SMDS_MeshElement* newElem = this->AddElement(uniqueNodes, etyp, isPoly, elemId);
7720 if ( sm && newElem )
7721 sm->AddElement( newElem );
7722 if ( elem != newElem )
7723 ReplaceElemInGroups( elem, newElem, aMesh );
7727 // Remove invalid regular element or invalid polygon
7728 rmElemIds.push_back( elem->GetID() );
7731 } // loop on elements
7733 // Remove bad elements, then equal nodes (order important)
7735 Remove( rmElemIds, false );
7736 Remove( rmNodeIds, true );
7741 // ========================================================
7742 // class : SortableElement
7743 // purpose : allow sorting elements basing on their nodes
7744 // ========================================================
7745 class SortableElement : public set <const SMDS_MeshElement*>
7749 SortableElement( const SMDS_MeshElement* theElem )
7752 SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator();
7753 while ( nodeIt->more() )
7754 this->insert( nodeIt->next() );
7757 const SMDS_MeshElement* Get() const
7760 void Set(const SMDS_MeshElement* e) const
7765 mutable const SMDS_MeshElement* myElem;
7768 //=======================================================================
7769 //function : FindEqualElements
7770 //purpose : Return list of group of elements built on the same nodes.
7771 // Search among theElements or in the whole mesh if theElements is empty
7772 //=======================================================================
7774 void SMESH_MeshEditor::FindEqualElements(TIDSortedElemSet & theElements,
7775 TListOfListOfElementsID & theGroupsOfElementsID)
7777 myLastCreatedElems.Clear();
7778 myLastCreatedNodes.Clear();
7780 typedef map< SortableElement, int > TMapOfNodeSet;
7781 typedef list<int> TGroupOfElems;
7783 if ( theElements.empty() )
7784 { // get all elements in the mesh
7785 SMDS_ElemIteratorPtr eIt = GetMeshDS()->elementsIterator();
7786 while ( eIt->more() )
7787 theElements.insert( theElements.end(), eIt->next());
7790 vector< TGroupOfElems > arrayOfGroups;
7791 TGroupOfElems groupOfElems;
7792 TMapOfNodeSet mapOfNodeSet;
7794 TIDSortedElemSet::iterator elemIt = theElements.begin();
7795 for ( int i = 0, j=0; elemIt != theElements.end(); ++elemIt, ++j ) {
7796 const SMDS_MeshElement* curElem = *elemIt;
7797 SortableElement SE(curElem);
7800 pair< TMapOfNodeSet::iterator, bool> pp = mapOfNodeSet.insert(make_pair(SE, i));
7801 if( !(pp.second) ) {
7802 TMapOfNodeSet::iterator& itSE = pp.first;
7803 ind = (*itSE).second;
7804 arrayOfGroups[ind].push_back(curElem->GetID());
7807 groupOfElems.clear();
7808 groupOfElems.push_back(curElem->GetID());
7809 arrayOfGroups.push_back(groupOfElems);
7814 vector< TGroupOfElems >::iterator groupIt = arrayOfGroups.begin();
7815 for ( ; groupIt != arrayOfGroups.end(); ++groupIt ) {
7816 groupOfElems = *groupIt;
7817 if ( groupOfElems.size() > 1 ) {
7818 groupOfElems.sort();
7819 theGroupsOfElementsID.push_back(groupOfElems);
7824 //=======================================================================
7825 //function : MergeElements
7826 //purpose : In each given group, substitute all elements by the first one.
7827 //=======================================================================
7829 void SMESH_MeshEditor::MergeElements(TListOfListOfElementsID & theGroupsOfElementsID)
7831 myLastCreatedElems.Clear();
7832 myLastCreatedNodes.Clear();
7834 typedef list<int> TListOfIDs;
7835 TListOfIDs rmElemIds; // IDs of elems to remove
7837 SMESHDS_Mesh* aMesh = GetMeshDS();
7839 TListOfListOfElementsID::iterator groupsIt = theGroupsOfElementsID.begin();
7840 while ( groupsIt != theGroupsOfElementsID.end() ) {
7841 TListOfIDs& aGroupOfElemID = *groupsIt;
7842 aGroupOfElemID.sort();
7843 int elemIDToKeep = aGroupOfElemID.front();
7844 const SMDS_MeshElement* elemToKeep = aMesh->FindElement(elemIDToKeep);
7845 aGroupOfElemID.pop_front();
7846 TListOfIDs::iterator idIt = aGroupOfElemID.begin();
7847 while ( idIt != aGroupOfElemID.end() ) {
7848 int elemIDToRemove = *idIt;
7849 const SMDS_MeshElement* elemToRemove = aMesh->FindElement(elemIDToRemove);
7850 // add the kept element in groups of removed one (PAL15188)
7851 AddToSameGroups( elemToKeep, elemToRemove, aMesh );
7852 rmElemIds.push_back( elemIDToRemove );
7858 Remove( rmElemIds, false );
7861 //=======================================================================
7862 //function : MergeEqualElements
7863 //purpose : Remove all but one of elements built on the same nodes.
7864 //=======================================================================
7866 void SMESH_MeshEditor::MergeEqualElements()
7868 TIDSortedElemSet aMeshElements; /* empty input ==
7869 to merge equal elements in the whole mesh */
7870 TListOfListOfElementsID aGroupsOfElementsID;
7871 FindEqualElements(aMeshElements, aGroupsOfElementsID);
7872 MergeElements(aGroupsOfElementsID);
7875 //=======================================================================
7876 //function : findAdjacentFace
7878 //=======================================================================
7880 static const SMDS_MeshElement* findAdjacentFace(const SMDS_MeshNode* n1,
7881 const SMDS_MeshNode* n2,
7882 const SMDS_MeshElement* elem)
7884 TIDSortedElemSet elemSet, avoidSet;
7886 avoidSet.insert ( elem );
7887 return SMESH_MeshAlgos::FindFaceInSet( n1, n2, elemSet, avoidSet );
7890 //=======================================================================
7891 //function : FindFreeBorder
7893 //=======================================================================
7895 #define ControlFreeBorder SMESH::Controls::FreeEdges::IsFreeEdge
7897 bool SMESH_MeshEditor::FindFreeBorder (const SMDS_MeshNode* theFirstNode,
7898 const SMDS_MeshNode* theSecondNode,
7899 const SMDS_MeshNode* theLastNode,
7900 list< const SMDS_MeshNode* > & theNodes,
7901 list< const SMDS_MeshElement* >& theFaces)
7903 if ( !theFirstNode || !theSecondNode )
7905 // find border face between theFirstNode and theSecondNode
7906 const SMDS_MeshElement* curElem = findAdjacentFace( theFirstNode, theSecondNode, 0 );
7910 theFaces.push_back( curElem );
7911 theNodes.push_back( theFirstNode );
7912 theNodes.push_back( theSecondNode );
7914 //vector<const SMDS_MeshNode*> nodes;
7915 const SMDS_MeshNode *nIgnore = theFirstNode, *nStart = theSecondNode;
7916 TIDSortedElemSet foundElems;
7917 bool needTheLast = ( theLastNode != 0 );
7919 while ( nStart != theLastNode ) {
7920 if ( nStart == theFirstNode )
7921 return !needTheLast;
7923 // find all free border faces sharing form nStart
7925 list< const SMDS_MeshElement* > curElemList;
7926 list< const SMDS_MeshNode* > nStartList;
7927 SMDS_ElemIteratorPtr invElemIt = nStart->GetInverseElementIterator(SMDSAbs_Face);
7928 while ( invElemIt->more() ) {
7929 const SMDS_MeshElement* e = invElemIt->next();
7930 if ( e == curElem || foundElems.insert( e ).second ) {
7932 int iNode = 0, nbNodes = e->NbNodes();
7933 //const SMDS_MeshNode* nodes[nbNodes+1];
7934 vector<const SMDS_MeshNode*> nodes(nbNodes+1);
7936 if(e->IsQuadratic()) {
7937 const SMDS_VtkFace* F =
7938 dynamic_cast<const SMDS_VtkFace*>(e);
7939 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
7940 // use special nodes iterator
7941 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
7942 while( anIter->more() ) {
7943 nodes[ iNode++ ] = cast2Node(anIter->next());
7947 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
7948 while ( nIt->more() )
7949 nodes[ iNode++ ] = static_cast<const SMDS_MeshNode*>( nIt->next() );
7951 nodes[ iNode ] = nodes[ 0 ];
7953 for ( iNode = 0; iNode < nbNodes; iNode++ )
7954 if (((nodes[ iNode ] == nStart && nodes[ iNode + 1] != nIgnore ) ||
7955 (nodes[ iNode + 1] == nStart && nodes[ iNode ] != nIgnore )) &&
7956 ControlFreeBorder( &nodes[ iNode ], e->GetID() ))
7958 nStartList.push_back( nodes[ iNode + ( nodes[ iNode ] == nStart ? 1 : 0 )]);
7959 curElemList.push_back( e );
7963 // analyse the found
7965 int nbNewBorders = curElemList.size();
7966 if ( nbNewBorders == 0 ) {
7967 // no free border furthermore
7968 return !needTheLast;
7970 else if ( nbNewBorders == 1 ) {
7971 // one more element found
7973 nStart = nStartList.front();
7974 curElem = curElemList.front();
7975 theFaces.push_back( curElem );
7976 theNodes.push_back( nStart );
7979 // several continuations found
7980 list< const SMDS_MeshElement* >::iterator curElemIt;
7981 list< const SMDS_MeshNode* >::iterator nStartIt;
7982 // check if one of them reached the last node
7983 if ( needTheLast ) {
7984 for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
7985 curElemIt!= curElemList.end();
7986 curElemIt++, nStartIt++ )
7987 if ( *nStartIt == theLastNode ) {
7988 theFaces.push_back( *curElemIt );
7989 theNodes.push_back( *nStartIt );
7993 // find the best free border by the continuations
7994 list<const SMDS_MeshNode*> contNodes[ 2 ], *cNL;
7995 list<const SMDS_MeshElement*> contFaces[ 2 ], *cFL;
7996 for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
7997 curElemIt!= curElemList.end();
7998 curElemIt++, nStartIt++ )
8000 cNL = & contNodes[ contNodes[0].empty() ? 0 : 1 ];
8001 cFL = & contFaces[ contFaces[0].empty() ? 0 : 1 ];
8002 // find one more free border
8003 if ( ! SMESH_MeshEditor::FindFreeBorder( nStart, *nStartIt, theLastNode, *cNL, *cFL )) {
8007 else if ( !contNodes[0].empty() && !contNodes[1].empty() ) {
8008 // choice: clear a worse one
8009 int iLongest = ( contNodes[0].size() < contNodes[1].size() ? 1 : 0 );
8010 int iWorse = ( needTheLast ? 1 - iLongest : iLongest );
8011 contNodes[ iWorse ].clear();
8012 contFaces[ iWorse ].clear();
8015 if ( contNodes[0].empty() && contNodes[1].empty() )
8018 // append the best free border
8019 cNL = & contNodes[ contNodes[0].empty() ? 1 : 0 ];
8020 cFL = & contFaces[ contFaces[0].empty() ? 1 : 0 ];
8021 theNodes.pop_back(); // remove nIgnore
8022 theNodes.pop_back(); // remove nStart
8023 theFaces.pop_back(); // remove curElem
8024 list< const SMDS_MeshNode* >::iterator nIt = cNL->begin();
8025 list< const SMDS_MeshElement* >::iterator fIt = cFL->begin();
8026 for ( ; nIt != cNL->end(); nIt++ ) theNodes.push_back( *nIt );
8027 for ( ; fIt != cFL->end(); fIt++ ) theFaces.push_back( *fIt );
8030 } // several continuations found
8031 } // while ( nStart != theLastNode )
8036 //=======================================================================
8037 //function : CheckFreeBorderNodes
8038 //purpose : Return true if the tree nodes are on a free border
8039 //=======================================================================
8041 bool SMESH_MeshEditor::CheckFreeBorderNodes(const SMDS_MeshNode* theNode1,
8042 const SMDS_MeshNode* theNode2,
8043 const SMDS_MeshNode* theNode3)
8045 list< const SMDS_MeshNode* > nodes;
8046 list< const SMDS_MeshElement* > faces;
8047 return FindFreeBorder( theNode1, theNode2, theNode3, nodes, faces);
8050 //=======================================================================
8051 //function : SewFreeBorder
8053 //=======================================================================
8055 SMESH_MeshEditor::Sew_Error
8056 SMESH_MeshEditor::SewFreeBorder (const SMDS_MeshNode* theBordFirstNode,
8057 const SMDS_MeshNode* theBordSecondNode,
8058 const SMDS_MeshNode* theBordLastNode,
8059 const SMDS_MeshNode* theSideFirstNode,
8060 const SMDS_MeshNode* theSideSecondNode,
8061 const SMDS_MeshNode* theSideThirdNode,
8062 const bool theSideIsFreeBorder,
8063 const bool toCreatePolygons,
8064 const bool toCreatePolyedrs)
8066 myLastCreatedElems.Clear();
8067 myLastCreatedNodes.Clear();
8069 MESSAGE("::SewFreeBorder()");
8070 Sew_Error aResult = SEW_OK;
8072 // ====================================
8073 // find side nodes and elements
8074 // ====================================
8076 list< const SMDS_MeshNode* > nSide[ 2 ];
8077 list< const SMDS_MeshElement* > eSide[ 2 ];
8078 list< const SMDS_MeshNode* >::iterator nIt[ 2 ];
8079 list< const SMDS_MeshElement* >::iterator eIt[ 2 ];
8083 if (!FindFreeBorder(theBordFirstNode,theBordSecondNode,theBordLastNode,
8084 nSide[0], eSide[0])) {
8085 MESSAGE(" Free Border 1 not found " );
8086 aResult = SEW_BORDER1_NOT_FOUND;
8088 if (theSideIsFreeBorder) {
8091 if (!FindFreeBorder(theSideFirstNode, theSideSecondNode, theSideThirdNode,
8092 nSide[1], eSide[1])) {
8093 MESSAGE(" Free Border 2 not found " );
8094 aResult = ( aResult != SEW_OK ? SEW_BOTH_BORDERS_NOT_FOUND : SEW_BORDER2_NOT_FOUND );
8097 if ( aResult != SEW_OK )
8100 if (!theSideIsFreeBorder) {
8104 // -------------------------------------------------------------------------
8106 // 1. If nodes to merge are not coincident, move nodes of the free border
8107 // from the coord sys defined by the direction from the first to last
8108 // nodes of the border to the correspondent sys of the side 2
8109 // 2. On the side 2, find the links most co-directed with the correspondent
8110 // links of the free border
8111 // -------------------------------------------------------------------------
8113 // 1. Since sewing may break if there are volumes to split on the side 2,
8114 // we wont move nodes but just compute new coordinates for them
8115 typedef map<const SMDS_MeshNode*, gp_XYZ> TNodeXYZMap;
8116 TNodeXYZMap nBordXYZ;
8117 list< const SMDS_MeshNode* >& bordNodes = nSide[ 0 ];
8118 list< const SMDS_MeshNode* >::iterator nBordIt;
8120 gp_XYZ Pb1( theBordFirstNode->X(), theBordFirstNode->Y(), theBordFirstNode->Z() );
8121 gp_XYZ Pb2( theBordLastNode->X(), theBordLastNode->Y(), theBordLastNode->Z() );
8122 gp_XYZ Ps1( theSideFirstNode->X(), theSideFirstNode->Y(), theSideFirstNode->Z() );
8123 gp_XYZ Ps2( theSideSecondNode->X(), theSideSecondNode->Y(), theSideSecondNode->Z() );
8124 double tol2 = 1.e-8;
8125 gp_Vec Vbs1( Pb1 - Ps1 ),Vbs2( Pb2 - Ps2 );
8126 if ( Vbs1.SquareMagnitude() > tol2 || Vbs2.SquareMagnitude() > tol2 ) {
8127 // Need node movement.
8129 // find X and Z axes to create trsf
8130 gp_Vec Zb( Pb1 - Pb2 ), Zs( Ps1 - Ps2 );
8132 if ( X.SquareMagnitude() <= gp::Resolution() * gp::Resolution() )
8134 X = gp_Ax2( gp::Origin(), Zb ).XDirection();
8137 gp_Ax3 toBordAx( Pb1, Zb, X );
8138 gp_Ax3 fromSideAx( Ps1, Zs, X );
8139 gp_Ax3 toGlobalAx( gp::Origin(), gp::DZ(), gp::DX() );
8141 gp_Trsf toBordSys, fromSide2Sys;
8142 toBordSys.SetTransformation( toBordAx );
8143 fromSide2Sys.SetTransformation( fromSideAx, toGlobalAx );
8144 fromSide2Sys.SetScaleFactor( Zs.Magnitude() / Zb.Magnitude() );
8147 for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
8148 const SMDS_MeshNode* n = *nBordIt;
8149 gp_XYZ xyz( n->X(),n->Y(),n->Z() );
8150 toBordSys.Transforms( xyz );
8151 fromSide2Sys.Transforms( xyz );
8152 nBordXYZ.insert( TNodeXYZMap::value_type( n, xyz ));
8156 // just insert nodes XYZ in the nBordXYZ map
8157 for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
8158 const SMDS_MeshNode* n = *nBordIt;
8159 nBordXYZ.insert( TNodeXYZMap::value_type( n, gp_XYZ( n->X(),n->Y(),n->Z() )));
8163 // 2. On the side 2, find the links most co-directed with the correspondent
8164 // links of the free border
8166 list< const SMDS_MeshElement* >& sideElems = eSide[ 1 ];
8167 list< const SMDS_MeshNode* >& sideNodes = nSide[ 1 ];
8168 sideNodes.push_back( theSideFirstNode );
8170 bool hasVolumes = false;
8171 LinkID_Gen aLinkID_Gen( GetMeshDS() );
8172 set<long> foundSideLinkIDs, checkedLinkIDs;
8173 SMDS_VolumeTool volume;
8174 //const SMDS_MeshNode* faceNodes[ 4 ];
8176 const SMDS_MeshNode* sideNode;
8177 const SMDS_MeshElement* sideElem;
8178 const SMDS_MeshNode* prevSideNode = theSideFirstNode;
8179 const SMDS_MeshNode* prevBordNode = theBordFirstNode;
8180 nBordIt = bordNodes.begin();
8182 // border node position and border link direction to compare with
8183 gp_XYZ bordPos = nBordXYZ[ *nBordIt ];
8184 gp_XYZ bordDir = bordPos - nBordXYZ[ prevBordNode ];
8185 // choose next side node by link direction or by closeness to
8186 // the current border node:
8187 bool searchByDir = ( *nBordIt != theBordLastNode );
8189 // find the next node on the Side 2
8191 double maxDot = -DBL_MAX, minDist = DBL_MAX;
8193 checkedLinkIDs.clear();
8194 gp_XYZ prevXYZ( prevSideNode->X(), prevSideNode->Y(), prevSideNode->Z() );
8196 // loop on inverse elements of current node (prevSideNode) on the Side 2
8197 SMDS_ElemIteratorPtr invElemIt = prevSideNode->GetInverseElementIterator();
8198 while ( invElemIt->more() )
8200 const SMDS_MeshElement* elem = invElemIt->next();
8201 // prepare data for a loop on links coming to prevSideNode, of a face or a volume
8202 int iPrevNode, iNode = 0, nbNodes = elem->NbNodes();
8203 vector< const SMDS_MeshNode* > faceNodes( nbNodes, (const SMDS_MeshNode*)0 );
8204 bool isVolume = volume.Set( elem );
8205 const SMDS_MeshNode** nodes = isVolume ? volume.GetNodes() : & faceNodes[0];
8206 if ( isVolume ) // --volume
8208 else if ( elem->GetType()==SMDSAbs_Face ) { // --face
8209 // retrieve all face nodes and find iPrevNode - an index of the prevSideNode
8210 if(elem->IsQuadratic()) {
8211 const SMDS_VtkFace* F =
8212 dynamic_cast<const SMDS_VtkFace*>(elem);
8213 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8214 // use special nodes iterator
8215 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8216 while( anIter->more() ) {
8217 nodes[ iNode ] = cast2Node(anIter->next());
8218 if ( nodes[ iNode++ ] == prevSideNode )
8219 iPrevNode = iNode - 1;
8223 SMDS_ElemIteratorPtr nIt = elem->nodesIterator();
8224 while ( nIt->more() ) {
8225 nodes[ iNode ] = cast2Node( nIt->next() );
8226 if ( nodes[ iNode++ ] == prevSideNode )
8227 iPrevNode = iNode - 1;
8230 // there are 2 links to check
8235 // loop on links, to be precise, on the second node of links
8236 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
8237 const SMDS_MeshNode* n = nodes[ iNode ];
8239 if ( !volume.IsLinked( n, prevSideNode ))
8243 if ( iNode ) // a node before prevSideNode
8244 n = nodes[ iPrevNode == 0 ? elem->NbNodes() - 1 : iPrevNode - 1 ];
8245 else // a node after prevSideNode
8246 n = nodes[ iPrevNode + 1 == elem->NbNodes() ? 0 : iPrevNode + 1 ];
8248 // check if this link was already used
8249 long iLink = aLinkID_Gen.GetLinkID( prevSideNode, n );
8250 bool isJustChecked = !checkedLinkIDs.insert( iLink ).second;
8251 if (!isJustChecked &&
8252 foundSideLinkIDs.find( iLink ) == foundSideLinkIDs.end() )
8254 // test a link geometrically
8255 gp_XYZ nextXYZ ( n->X(), n->Y(), n->Z() );
8256 bool linkIsBetter = false;
8257 double dot = 0.0, dist = 0.0;
8258 if ( searchByDir ) { // choose most co-directed link
8259 dot = bordDir * ( nextXYZ - prevXYZ ).Normalized();
8260 linkIsBetter = ( dot > maxDot );
8262 else { // choose link with the node closest to bordPos
8263 dist = ( nextXYZ - bordPos ).SquareModulus();
8264 linkIsBetter = ( dist < minDist );
8266 if ( linkIsBetter ) {
8275 } // loop on inverse elements of prevSideNode
8278 MESSAGE(" Cant find path by links of the Side 2 ");
8279 return SEW_BAD_SIDE_NODES;
8281 sideNodes.push_back( sideNode );
8282 sideElems.push_back( sideElem );
8283 foundSideLinkIDs.insert ( linkID );
8284 prevSideNode = sideNode;
8286 if ( *nBordIt == theBordLastNode )
8287 searchByDir = false;
8289 // find the next border link to compare with
8290 gp_XYZ sidePos( sideNode->X(), sideNode->Y(), sideNode->Z() );
8291 searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8292 // move to next border node if sideNode is before forward border node (bordPos)
8293 while ( *nBordIt != theBordLastNode && !searchByDir ) {
8294 prevBordNode = *nBordIt;
8296 bordPos = nBordXYZ[ *nBordIt ];
8297 bordDir = bordPos - nBordXYZ[ prevBordNode ];
8298 searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8302 while ( sideNode != theSideSecondNode );
8304 if ( hasVolumes && sideNodes.size () != bordNodes.size() && !toCreatePolyedrs) {
8305 MESSAGE("VOLUME SPLITTING IS FORBIDDEN");
8306 return SEW_VOLUMES_TO_SPLIT; // volume splitting is forbidden
8308 } // end nodes search on the side 2
8310 // ============================
8311 // sew the border to the side 2
8312 // ============================
8314 int nbNodes[] = { nSide[0].size(), nSide[1].size() };
8315 int maxNbNodes = Max( nbNodes[0], nbNodes[1] );
8317 TListOfListOfNodes nodeGroupsToMerge;
8318 if ( nbNodes[0] == nbNodes[1] ||
8319 ( theSideIsFreeBorder && !theSideThirdNode)) {
8321 // all nodes are to be merged
8323 for (nIt[0] = nSide[0].begin(), nIt[1] = nSide[1].begin();
8324 nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end();
8325 nIt[0]++, nIt[1]++ )
8327 nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8328 nodeGroupsToMerge.back().push_back( *nIt[1] ); // to keep
8329 nodeGroupsToMerge.back().push_back( *nIt[0] ); // to remove
8334 // insert new nodes into the border and the side to get equal nb of segments
8336 // get normalized parameters of nodes on the borders
8337 //double param[ 2 ][ maxNbNodes ];
8339 param[0] = new double [ maxNbNodes ];
8340 param[1] = new double [ maxNbNodes ];
8342 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8343 list< const SMDS_MeshNode* >& nodes = nSide[ iBord ];
8344 list< const SMDS_MeshNode* >::iterator nIt = nodes.begin();
8345 const SMDS_MeshNode* nPrev = *nIt;
8346 double bordLength = 0;
8347 for ( iNode = 0; nIt != nodes.end(); nIt++, iNode++ ) { // loop on border nodes
8348 const SMDS_MeshNode* nCur = *nIt;
8349 gp_XYZ segment (nCur->X() - nPrev->X(),
8350 nCur->Y() - nPrev->Y(),
8351 nCur->Z() - nPrev->Z());
8352 double segmentLen = segment.Modulus();
8353 bordLength += segmentLen;
8354 param[ iBord ][ iNode ] = bordLength;
8357 // normalize within [0,1]
8358 for ( iNode = 0; iNode < nbNodes[ iBord ]; iNode++ ) {
8359 param[ iBord ][ iNode ] /= bordLength;
8363 // loop on border segments
8364 const SMDS_MeshNode *nPrev[ 2 ] = { 0, 0 };
8365 int i[ 2 ] = { 0, 0 };
8366 nIt[0] = nSide[0].begin(); eIt[0] = eSide[0].begin();
8367 nIt[1] = nSide[1].begin(); eIt[1] = eSide[1].begin();
8369 TElemOfNodeListMap insertMap;
8370 TElemOfNodeListMap::iterator insertMapIt;
8372 // key: elem to insert nodes into
8373 // value: 2 nodes to insert between + nodes to be inserted
8375 bool next[ 2 ] = { false, false };
8377 // find min adjacent segment length after sewing
8378 double nextParam = 10., prevParam = 0;
8379 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8380 if ( i[ iBord ] + 1 < nbNodes[ iBord ])
8381 nextParam = Min( nextParam, param[iBord][ i[iBord] + 1 ]);
8382 if ( i[ iBord ] > 0 )
8383 prevParam = Max( prevParam, param[iBord][ i[iBord] - 1 ]);
8385 double minParam = Min( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8386 double maxParam = Max( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8387 double minSegLen = Min( nextParam - minParam, maxParam - prevParam );
8389 // choose to insert or to merge nodes
8390 double du = param[ 1 ][ i[1] ] - param[ 0 ][ i[0] ];
8391 if ( Abs( du ) <= minSegLen * 0.2 ) {
8394 nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8395 const SMDS_MeshNode* n0 = *nIt[0];
8396 const SMDS_MeshNode* n1 = *nIt[1];
8397 nodeGroupsToMerge.back().push_back( n1 );
8398 nodeGroupsToMerge.back().push_back( n0 );
8399 // position of node of the border changes due to merge
8400 param[ 0 ][ i[0] ] += du;
8401 // move n1 for the sake of elem shape evaluation during insertion.
8402 // n1 will be removed by MergeNodes() anyway
8403 const_cast<SMDS_MeshNode*>( n0 )->setXYZ( n1->X(), n1->Y(), n1->Z() );
8404 next[0] = next[1] = true;
8409 int intoBord = ( du < 0 ) ? 0 : 1;
8410 const SMDS_MeshElement* elem = *eIt[ intoBord ];
8411 const SMDS_MeshNode* n1 = nPrev[ intoBord ];
8412 const SMDS_MeshNode* n2 = *nIt[ intoBord ];
8413 const SMDS_MeshNode* nIns = *nIt[ 1 - intoBord ];
8414 if ( intoBord == 1 ) {
8415 // move node of the border to be on a link of elem of the side
8416 gp_XYZ p1 (n1->X(), n1->Y(), n1->Z());
8417 gp_XYZ p2 (n2->X(), n2->Y(), n2->Z());
8418 double ratio = du / ( param[ 1 ][ i[1] ] - param[ 1 ][ i[1]-1 ]);
8419 gp_XYZ p = p2 * ( 1 - ratio ) + p1 * ratio;
8420 GetMeshDS()->MoveNode( nIns, p.X(), p.Y(), p.Z() );
8422 insertMapIt = insertMap.find( elem );
8423 bool notFound = ( insertMapIt == insertMap.end() );
8424 bool otherLink = ( !notFound && (*insertMapIt).second.front() != n1 );
8426 // insert into another link of the same element:
8427 // 1. perform insertion into the other link of the elem
8428 list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8429 const SMDS_MeshNode* n12 = nodeList.front(); nodeList.pop_front();
8430 const SMDS_MeshNode* n22 = nodeList.front(); nodeList.pop_front();
8431 InsertNodesIntoLink( elem, n12, n22, nodeList, toCreatePolygons );
8432 // 2. perform insertion into the link of adjacent faces
8434 const SMDS_MeshElement* adjElem = findAdjacentFace( n12, n22, elem );
8436 InsertNodesIntoLink( adjElem, n12, n22, nodeList, toCreatePolygons );
8440 if (toCreatePolyedrs) {
8441 // perform insertion into the links of adjacent volumes
8442 UpdateVolumes(n12, n22, nodeList);
8444 // 3. find an element appeared on n1 and n2 after the insertion
8445 insertMap.erase( elem );
8446 elem = findAdjacentFace( n1, n2, 0 );
8448 if ( notFound || otherLink ) {
8449 // add element and nodes of the side into the insertMap
8450 insertMapIt = insertMap.insert
8451 ( TElemOfNodeListMap::value_type( elem, list<const SMDS_MeshNode*>() )).first;
8452 (*insertMapIt).second.push_back( n1 );
8453 (*insertMapIt).second.push_back( n2 );
8455 // add node to be inserted into elem
8456 (*insertMapIt).second.push_back( nIns );
8457 next[ 1 - intoBord ] = true;
8460 // go to the next segment
8461 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8462 if ( next[ iBord ] ) {
8463 if ( i[ iBord ] != 0 && eIt[ iBord ] != eSide[ iBord ].end())
8465 nPrev[ iBord ] = *nIt[ iBord ];
8466 nIt[ iBord ]++; i[ iBord ]++;
8470 while ( nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end());
8472 // perform insertion of nodes into elements
8474 for (insertMapIt = insertMap.begin();
8475 insertMapIt != insertMap.end();
8478 const SMDS_MeshElement* elem = (*insertMapIt).first;
8479 list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8480 const SMDS_MeshNode* n1 = nodeList.front(); nodeList.pop_front();
8481 const SMDS_MeshNode* n2 = nodeList.front(); nodeList.pop_front();
8483 InsertNodesIntoLink( elem, n1, n2, nodeList, toCreatePolygons );
8485 if ( !theSideIsFreeBorder ) {
8486 // look for and insert nodes into the faces adjacent to elem
8488 const SMDS_MeshElement* adjElem = findAdjacentFace( n1, n2, elem );
8490 InsertNodesIntoLink( adjElem, n1, n2, nodeList, toCreatePolygons );
8495 if (toCreatePolyedrs) {
8496 // perform insertion into the links of adjacent volumes
8497 UpdateVolumes(n1, n2, nodeList);
8503 } // end: insert new nodes
8505 MergeNodes ( nodeGroupsToMerge );
8510 //=======================================================================
8511 //function : InsertNodesIntoLink
8512 //purpose : insert theNodesToInsert into theFace between theBetweenNode1
8513 // and theBetweenNode2 and split theElement
8514 //=======================================================================
8516 void SMESH_MeshEditor::InsertNodesIntoLink(const SMDS_MeshElement* theFace,
8517 const SMDS_MeshNode* theBetweenNode1,
8518 const SMDS_MeshNode* theBetweenNode2,
8519 list<const SMDS_MeshNode*>& theNodesToInsert,
8520 const bool toCreatePoly)
8522 if ( theFace->GetType() != SMDSAbs_Face ) return;
8524 // find indices of 2 link nodes and of the rest nodes
8525 int iNode = 0, il1, il2, i3, i4;
8526 il1 = il2 = i3 = i4 = -1;
8527 //const SMDS_MeshNode* nodes[ theFace->NbNodes() ];
8528 vector<const SMDS_MeshNode*> nodes( theFace->NbNodes() );
8530 if(theFace->IsQuadratic()) {
8531 const SMDS_VtkFace* F =
8532 dynamic_cast<const SMDS_VtkFace*>(theFace);
8533 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8534 // use special nodes iterator
8535 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8536 while( anIter->more() ) {
8537 const SMDS_MeshNode* n = cast2Node(anIter->next());
8538 if ( n == theBetweenNode1 )
8540 else if ( n == theBetweenNode2 )
8546 nodes[ iNode++ ] = n;
8550 SMDS_ElemIteratorPtr nodeIt = theFace->nodesIterator();
8551 while ( nodeIt->more() ) {
8552 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8553 if ( n == theBetweenNode1 )
8555 else if ( n == theBetweenNode2 )
8561 nodes[ iNode++ ] = n;
8564 if ( il1 < 0 || il2 < 0 || i3 < 0 )
8567 // arrange link nodes to go one after another regarding the face orientation
8568 bool reverse = ( Abs( il2 - il1 ) == 1 ? il2 < il1 : il1 < il2 );
8569 list<const SMDS_MeshNode *> aNodesToInsert = theNodesToInsert;
8574 aNodesToInsert.reverse();
8576 // check that not link nodes of a quadrangles are in good order
8577 int nbFaceNodes = theFace->NbNodes();
8578 if ( nbFaceNodes == 4 && i4 - i3 != 1 ) {
8584 if (toCreatePoly || theFace->IsPoly()) {
8587 vector<const SMDS_MeshNode *> poly_nodes (nbFaceNodes + aNodesToInsert.size());
8589 // add nodes of face up to first node of link
8592 if(theFace->IsQuadratic()) {
8593 const SMDS_VtkFace* F =
8594 dynamic_cast<const SMDS_VtkFace*>(theFace);
8595 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8596 // use special nodes iterator
8597 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8598 while( anIter->more() && !isFLN ) {
8599 const SMDS_MeshNode* n = cast2Node(anIter->next());
8600 poly_nodes[iNode++] = n;
8601 if (n == nodes[il1]) {
8605 // add nodes to insert
8606 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8607 for (; nIt != aNodesToInsert.end(); nIt++) {
8608 poly_nodes[iNode++] = *nIt;
8610 // add nodes of face starting from last node of link
8611 while ( anIter->more() ) {
8612 poly_nodes[iNode++] = cast2Node(anIter->next());
8616 SMDS_ElemIteratorPtr nodeIt = theFace->nodesIterator();
8617 while ( nodeIt->more() && !isFLN ) {
8618 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8619 poly_nodes[iNode++] = n;
8620 if (n == nodes[il1]) {
8624 // add nodes to insert
8625 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8626 for (; nIt != aNodesToInsert.end(); nIt++) {
8627 poly_nodes[iNode++] = *nIt;
8629 // add nodes of face starting from last node of link
8630 while ( nodeIt->more() ) {
8631 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8632 poly_nodes[iNode++] = n;
8636 // edit or replace the face
8637 SMESHDS_Mesh *aMesh = GetMeshDS();
8639 if (theFace->IsPoly()) {
8640 aMesh->ChangePolygonNodes(theFace, poly_nodes);
8643 int aShapeId = FindShape( theFace );
8645 SMDS_MeshElement* newElem = aMesh->AddPolygonalFace(poly_nodes);
8646 myLastCreatedElems.Append(newElem);
8647 if ( aShapeId && newElem )
8648 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8650 aMesh->RemoveElement(theFace);
8655 SMESHDS_Mesh *aMesh = GetMeshDS();
8656 if( !theFace->IsQuadratic() ) {
8658 // put aNodesToInsert between theBetweenNode1 and theBetweenNode2
8659 int nbLinkNodes = 2 + aNodesToInsert.size();
8660 //const SMDS_MeshNode* linkNodes[ nbLinkNodes ];
8661 vector<const SMDS_MeshNode*> linkNodes( nbLinkNodes );
8662 linkNodes[ 0 ] = nodes[ il1 ];
8663 linkNodes[ nbLinkNodes - 1 ] = nodes[ il2 ];
8664 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8665 for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
8666 linkNodes[ iNode++ ] = *nIt;
8668 // decide how to split a quadrangle: compare possible variants
8669 // and choose which of splits to be a quadrangle
8670 int i1, i2, iSplit, nbSplits = nbLinkNodes - 1, iBestQuad;
8671 if ( nbFaceNodes == 3 ) {
8672 iBestQuad = nbSplits;
8675 else if ( nbFaceNodes == 4 ) {
8676 SMESH::Controls::NumericalFunctorPtr aCrit( new SMESH::Controls::AspectRatio);
8677 double aBestRate = DBL_MAX;
8678 for ( int iQuad = 0; iQuad < nbSplits; iQuad++ ) {
8680 double aBadRate = 0;
8681 // evaluate elements quality
8682 for ( iSplit = 0; iSplit < nbSplits; iSplit++ ) {
8683 if ( iSplit == iQuad ) {
8684 SMDS_FaceOfNodes quad (linkNodes[ i1++ ],
8688 aBadRate += getBadRate( &quad, aCrit );
8691 SMDS_FaceOfNodes tria (linkNodes[ i1++ ],
8693 nodes[ iSplit < iQuad ? i4 : i3 ]);
8694 aBadRate += getBadRate( &tria, aCrit );
8698 if ( aBadRate < aBestRate ) {
8700 aBestRate = aBadRate;
8705 // create new elements
8706 int aShapeId = FindShape( theFace );
8709 for ( iSplit = 0; iSplit < nbSplits - 1; iSplit++ ) {
8710 SMDS_MeshElement* newElem = 0;
8711 if ( iSplit == iBestQuad )
8712 newElem = aMesh->AddFace (linkNodes[ i1++ ],
8717 newElem = aMesh->AddFace (linkNodes[ i1++ ],
8719 nodes[ iSplit < iBestQuad ? i4 : i3 ]);
8720 myLastCreatedElems.Append(newElem);
8721 if ( aShapeId && newElem )
8722 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8725 // change nodes of theFace
8726 const SMDS_MeshNode* newNodes[ 4 ];
8727 newNodes[ 0 ] = linkNodes[ i1 ];
8728 newNodes[ 1 ] = linkNodes[ i2 ];
8729 newNodes[ 2 ] = nodes[ iSplit >= iBestQuad ? i3 : i4 ];
8730 newNodes[ 3 ] = nodes[ i4 ];
8731 //aMesh->ChangeElementNodes( theFace, newNodes, iSplit == iBestQuad ? 4 : 3 );
8732 const SMDS_MeshElement* newElem = 0;
8733 if (iSplit == iBestQuad)
8734 newElem = aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2], newNodes[3] );
8736 newElem = aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2] );
8737 myLastCreatedElems.Append(newElem);
8738 if ( aShapeId && newElem )
8739 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8740 } // end if(!theFace->IsQuadratic())
8741 else { // theFace is quadratic
8742 // we have to split theFace on simple triangles and one simple quadrangle
8744 int nbshift = tmp*2;
8745 // shift nodes in nodes[] by nbshift
8747 for(i=0; i<nbshift; i++) {
8748 const SMDS_MeshNode* n = nodes[0];
8749 for(j=0; j<nbFaceNodes-1; j++) {
8750 nodes[j] = nodes[j+1];
8752 nodes[nbFaceNodes-1] = n;
8754 il1 = il1 - nbshift;
8755 // now have to insert nodes between n0 and n1 or n1 and n2 (see below)
8756 // n0 n1 n2 n0 n1 n2
8757 // +-----+-----+ +-----+-----+
8766 // create new elements
8767 int aShapeId = FindShape( theFace );
8770 if(nbFaceNodes==6) { // quadratic triangle
8771 SMDS_MeshElement* newElem =
8772 aMesh->AddFace(nodes[3],nodes[4],nodes[5]);
8773 myLastCreatedElems.Append(newElem);
8774 if ( aShapeId && newElem )
8775 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8776 if(theFace->IsMediumNode(nodes[il1])) {
8777 // create quadrangle
8778 newElem = aMesh->AddFace(nodes[0],nodes[1],nodes[3],nodes[5]);
8779 myLastCreatedElems.Append(newElem);
8780 if ( aShapeId && newElem )
8781 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8787 // create quadrangle
8788 newElem = aMesh->AddFace(nodes[1],nodes[2],nodes[3],nodes[5]);
8789 myLastCreatedElems.Append(newElem);
8790 if ( aShapeId && newElem )
8791 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8797 else { // nbFaceNodes==8 - quadratic quadrangle
8798 SMDS_MeshElement* newElem =
8799 aMesh->AddFace(nodes[3],nodes[4],nodes[5]);
8800 myLastCreatedElems.Append(newElem);
8801 if ( aShapeId && newElem )
8802 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8803 newElem = aMesh->AddFace(nodes[5],nodes[6],nodes[7]);
8804 myLastCreatedElems.Append(newElem);
8805 if ( aShapeId && newElem )
8806 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8807 newElem = aMesh->AddFace(nodes[5],nodes[7],nodes[3]);
8808 myLastCreatedElems.Append(newElem);
8809 if ( aShapeId && newElem )
8810 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8811 if(theFace->IsMediumNode(nodes[il1])) {
8812 // create quadrangle
8813 newElem = aMesh->AddFace(nodes[0],nodes[1],nodes[3],nodes[7]);
8814 myLastCreatedElems.Append(newElem);
8815 if ( aShapeId && newElem )
8816 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8822 // create quadrangle
8823 newElem = aMesh->AddFace(nodes[1],nodes[2],nodes[3],nodes[7]);
8824 myLastCreatedElems.Append(newElem);
8825 if ( aShapeId && newElem )
8826 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8832 // create needed triangles using n1,n2,n3 and inserted nodes
8833 int nbn = 2 + aNodesToInsert.size();
8834 //const SMDS_MeshNode* aNodes[nbn];
8835 vector<const SMDS_MeshNode*> aNodes(nbn);
8836 aNodes[0] = nodes[n1];
8837 aNodes[nbn-1] = nodes[n2];
8838 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8839 for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
8840 aNodes[iNode++] = *nIt;
8842 for(i=1; i<nbn; i++) {
8843 SMDS_MeshElement* newElem =
8844 aMesh->AddFace(aNodes[i-1],aNodes[i],nodes[n3]);
8845 myLastCreatedElems.Append(newElem);
8846 if ( aShapeId && newElem )
8847 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8851 aMesh->RemoveElement(theFace);
8854 //=======================================================================
8855 //function : UpdateVolumes
8857 //=======================================================================
8858 void SMESH_MeshEditor::UpdateVolumes (const SMDS_MeshNode* theBetweenNode1,
8859 const SMDS_MeshNode* theBetweenNode2,
8860 list<const SMDS_MeshNode*>& theNodesToInsert)
8862 myLastCreatedElems.Clear();
8863 myLastCreatedNodes.Clear();
8865 SMDS_ElemIteratorPtr invElemIt = theBetweenNode1->GetInverseElementIterator(SMDSAbs_Volume);
8866 while (invElemIt->more()) { // loop on inverse elements of theBetweenNode1
8867 const SMDS_MeshElement* elem = invElemIt->next();
8869 // check, if current volume has link theBetweenNode1 - theBetweenNode2
8870 SMDS_VolumeTool aVolume (elem);
8871 if (!aVolume.IsLinked(theBetweenNode1, theBetweenNode2))
8874 // insert new nodes in all faces of the volume, sharing link theBetweenNode1 - theBetweenNode2
8875 int iface, nbFaces = aVolume.NbFaces();
8876 vector<const SMDS_MeshNode *> poly_nodes;
8877 vector<int> quantities (nbFaces);
8879 for (iface = 0; iface < nbFaces; iface++) {
8880 int nbFaceNodes = aVolume.NbFaceNodes(iface), nbInserted = 0;
8881 // faceNodes will contain (nbFaceNodes + 1) nodes, last = first
8882 const SMDS_MeshNode** faceNodes = aVolume.GetFaceNodes(iface);
8884 for (int inode = 0; inode < nbFaceNodes; inode++) {
8885 poly_nodes.push_back(faceNodes[inode]);
8887 if (nbInserted == 0) {
8888 if (faceNodes[inode] == theBetweenNode1) {
8889 if (faceNodes[inode + 1] == theBetweenNode2) {
8890 nbInserted = theNodesToInsert.size();
8892 // add nodes to insert
8893 list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.begin();
8894 for (; nIt != theNodesToInsert.end(); nIt++) {
8895 poly_nodes.push_back(*nIt);
8899 else if (faceNodes[inode] == theBetweenNode2) {
8900 if (faceNodes[inode + 1] == theBetweenNode1) {
8901 nbInserted = theNodesToInsert.size();
8903 // add nodes to insert in reversed order
8904 list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.end();
8906 for (; nIt != theNodesToInsert.begin(); nIt--) {
8907 poly_nodes.push_back(*nIt);
8909 poly_nodes.push_back(*nIt);
8916 quantities[iface] = nbFaceNodes + nbInserted;
8919 // Replace or update the volume
8920 SMESHDS_Mesh *aMesh = GetMeshDS();
8922 if (elem->IsPoly()) {
8923 aMesh->ChangePolyhedronNodes(elem, poly_nodes, quantities);
8927 int aShapeId = FindShape( elem );
8929 SMDS_MeshElement* newElem =
8930 aMesh->AddPolyhedralVolume(poly_nodes, quantities);
8931 myLastCreatedElems.Append(newElem);
8932 if (aShapeId && newElem)
8933 aMesh->SetMeshElementOnShape(newElem, aShapeId);
8935 aMesh->RemoveElement(elem);
8942 //================================================================================
8944 * \brief Transform any volume into data of SMDSEntity_Polyhedra
8946 //================================================================================
8948 void volumeToPolyhedron( const SMDS_MeshElement* elem,
8949 vector<const SMDS_MeshNode *> & nodes,
8950 vector<int> & nbNodeInFaces )
8953 nbNodeInFaces.clear();
8954 SMDS_VolumeTool vTool ( elem );
8955 for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
8957 const SMDS_MeshNode** fNodes = vTool.GetFaceNodes( iF );
8958 nodes.insert( nodes.end(), fNodes, fNodes + vTool.NbFaceNodes( iF ));
8959 nbNodeInFaces.push_back( vTool.NbFaceNodes( iF ));
8964 //=======================================================================
8966 * \brief Convert elements contained in a submesh to quadratic
8967 * \return int - nb of checked elements
8969 //=======================================================================
8971 int SMESH_MeshEditor::convertElemToQuadratic(SMESHDS_SubMesh * theSm,
8972 SMESH_MesherHelper& theHelper,
8973 const bool theForce3d)
8976 if( !theSm ) return nbElem;
8978 vector<int> nbNodeInFaces;
8979 vector<const SMDS_MeshNode *> nodes;
8980 SMDS_ElemIteratorPtr ElemItr = theSm->GetElements();
8981 while(ElemItr->more())
8984 const SMDS_MeshElement* elem = ElemItr->next();
8985 if( !elem ) continue;
8987 // analyse a necessity of conversion
8988 const SMDSAbs_ElementType aType = elem->GetType();
8989 if ( aType < SMDSAbs_Edge || aType > SMDSAbs_Volume )
8991 const SMDSAbs_EntityType aGeomType = elem->GetEntityType();
8992 bool hasCentralNodes = false;
8993 if ( elem->IsQuadratic() )
8996 switch ( aGeomType ) {
8997 case SMDSEntity_Quad_Triangle:
8998 case SMDSEntity_Quad_Quadrangle:
8999 case SMDSEntity_Quad_Hexa:
9000 alreadyOK = !theHelper.GetIsBiQuadratic(); break;
9002 case SMDSEntity_BiQuad_Triangle:
9003 case SMDSEntity_BiQuad_Quadrangle:
9004 case SMDSEntity_TriQuad_Hexa:
9005 alreadyOK = theHelper.GetIsBiQuadratic();
9006 hasCentralNodes = true;
9011 // take into account already present modium nodes
9013 case SMDSAbs_Volume:
9014 theHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( elem )); break;
9016 theHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( elem )); break;
9018 theHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( elem )); break;
9024 // get elem data needed to re-create it
9026 const int id = elem->GetID();
9027 const int nbNodes = elem->NbCornerNodes();
9028 nodes.assign(elem->begin_nodes(), elem->end_nodes());
9029 if ( aGeomType == SMDSEntity_Polyhedra )
9030 nbNodeInFaces = static_cast<const SMDS_VtkVolume* >( elem )->GetQuantities();
9031 else if ( aGeomType == SMDSEntity_Hexagonal_Prism )
9032 volumeToPolyhedron( elem, nodes, nbNodeInFaces );
9034 // remove a linear element
9035 GetMeshDS()->RemoveFreeElement(elem, theSm, /*fromGroups=*/false);
9037 // remove central nodes of biquadratic elements (biquad->quad convertion)
9038 if ( hasCentralNodes )
9039 for ( size_t i = nbNodes * 2; i < nodes.size(); ++i )
9040 if ( nodes[i]->NbInverseElements() == 0 )
9041 GetMeshDS()->RemoveFreeNode( nodes[i], theSm, /*fromGroups=*/true );
9043 const SMDS_MeshElement* NewElem = 0;
9049 NewElem = theHelper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9057 NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
9060 NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9063 NewElem = theHelper.AddPolygonalFace(nodes, id, theForce3d);
9067 case SMDSAbs_Volume :
9071 case SMDSEntity_Tetra:
9072 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9074 case SMDSEntity_Pyramid:
9075 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], id, theForce3d);
9077 case SMDSEntity_Penta:
9078 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], nodes[5], id, theForce3d);
9080 case SMDSEntity_Hexa:
9081 case SMDSEntity_Quad_Hexa:
9082 case SMDSEntity_TriQuad_Hexa:
9083 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9084 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9086 case SMDSEntity_Hexagonal_Prism:
9088 NewElem = theHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9095 ReplaceElemInGroups( elem, NewElem, GetMeshDS());
9096 if( NewElem && NewElem->getshapeId() < 1 )
9097 theSm->AddElement( NewElem );
9101 //=======================================================================
9102 //function : ConvertToQuadratic
9104 //=======================================================================
9106 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d, const bool theToBiQuad)
9108 SMESHDS_Mesh* meshDS = GetMeshDS();
9110 SMESH_MesherHelper aHelper(*myMesh);
9112 aHelper.SetIsQuadratic( true );
9113 aHelper.SetIsBiQuadratic( theToBiQuad );
9114 aHelper.SetElementsOnShape(true);
9115 aHelper.ToFixNodeParameters( true );
9117 // convert elements assigned to sub-meshes
9118 int nbCheckedElems = 0;
9119 if ( myMesh->HasShapeToMesh() )
9121 if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9123 SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9124 while ( smIt->more() ) {
9125 SMESH_subMesh* sm = smIt->next();
9126 if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() ) {
9127 aHelper.SetSubShape( sm->GetSubShape() );
9128 nbCheckedElems += convertElemToQuadratic(smDS, aHelper, theForce3d);
9134 // convert elements NOT assigned to sub-meshes
9135 int totalNbElems = meshDS->NbEdges() + meshDS->NbFaces() + meshDS->NbVolumes();
9136 if ( nbCheckedElems < totalNbElems ) // not all elements are in sub-meshes
9138 aHelper.SetElementsOnShape(false);
9139 SMESHDS_SubMesh *smDS = 0;
9142 SMDS_EdgeIteratorPtr aEdgeItr = meshDS->edgesIterator();
9143 while( aEdgeItr->more() )
9145 const SMDS_MeshEdge* edge = aEdgeItr->next();
9146 if ( !edge->IsQuadratic() )
9148 int id = edge->GetID();
9149 const SMDS_MeshNode* n1 = edge->GetNode(0);
9150 const SMDS_MeshNode* n2 = edge->GetNode(1);
9152 meshDS->RemoveFreeElement(edge, smDS, /*fromGroups=*/false);
9154 const SMDS_MeshEdge* NewEdge = aHelper.AddEdge(n1, n2, id, theForce3d);
9155 ReplaceElemInGroups( edge, NewEdge, GetMeshDS());
9159 aHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( edge ));
9164 SMDS_FaceIteratorPtr aFaceItr = meshDS->facesIterator();
9165 while( aFaceItr->more() )
9167 const SMDS_MeshFace* face = aFaceItr->next();
9168 if ( !face ) continue;
9170 const SMDSAbs_EntityType type = face->GetEntityType();
9174 case SMDSEntity_Quad_Triangle:
9175 case SMDSEntity_Quad_Quadrangle:
9176 alreadyOK = !theToBiQuad;
9177 aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
9179 case SMDSEntity_BiQuad_Triangle:
9180 case SMDSEntity_BiQuad_Quadrangle:
9181 alreadyOK = theToBiQuad;
9182 aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
9184 default: alreadyOK = false;
9189 const int id = face->GetID();
9190 vector<const SMDS_MeshNode *> nodes ( face->begin_nodes(), face->end_nodes());
9192 meshDS->RemoveFreeElement(face, smDS, /*fromGroups=*/false);
9194 SMDS_MeshFace * NewFace = 0;
9197 case SMDSEntity_Triangle:
9198 case SMDSEntity_Quad_Triangle:
9199 case SMDSEntity_BiQuad_Triangle:
9200 NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
9201 if ( nodes.size() == 7 && nodes[6]->NbInverseElements() == 0 ) // rm a central node
9202 GetMeshDS()->RemoveFreeNode( nodes[6], /*sm=*/0, /*fromGroups=*/true );
9205 case SMDSEntity_Quadrangle:
9206 case SMDSEntity_Quad_Quadrangle:
9207 case SMDSEntity_BiQuad_Quadrangle:
9208 NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9209 if ( nodes.size() == 9 && nodes[8]->NbInverseElements() == 0 ) // rm a central node
9210 GetMeshDS()->RemoveFreeNode( nodes[8], /*sm=*/0, /*fromGroups=*/true );
9214 NewFace = aHelper.AddPolygonalFace(nodes, id, theForce3d);
9216 ReplaceElemInGroups( face, NewFace, GetMeshDS());
9220 vector<int> nbNodeInFaces;
9221 SMDS_VolumeIteratorPtr aVolumeItr = meshDS->volumesIterator();
9222 while(aVolumeItr->more())
9224 const SMDS_MeshVolume* volume = aVolumeItr->next();
9225 if ( !volume ) continue;
9227 const SMDSAbs_EntityType type = volume->GetEntityType();
9228 if ( volume->IsQuadratic() )
9233 case SMDSEntity_Quad_Hexa: alreadyOK = !theToBiQuad; break;
9234 case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; break;
9235 default: alreadyOK = true;
9239 aHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( volume ));
9243 const int id = volume->GetID();
9244 vector<const SMDS_MeshNode *> nodes (volume->begin_nodes(), volume->end_nodes());
9245 if ( type == SMDSEntity_Polyhedra )
9246 nbNodeInFaces = static_cast<const SMDS_VtkVolume* >(volume)->GetQuantities();
9247 else if ( type == SMDSEntity_Hexagonal_Prism )
9248 volumeToPolyhedron( volume, nodes, nbNodeInFaces );
9250 meshDS->RemoveFreeElement(volume, smDS, /*fromGroups=*/false);
9252 SMDS_MeshVolume * NewVolume = 0;
9255 case SMDSEntity_Tetra:
9256 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d );
9258 case SMDSEntity_Hexa:
9259 case SMDSEntity_Quad_Hexa:
9260 case SMDSEntity_TriQuad_Hexa:
9261 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9262 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9263 for ( size_t i = 20; i < nodes.size(); ++i ) // rm central nodes
9264 if ( nodes[i]->NbInverseElements() == 0 )
9265 GetMeshDS()->RemoveFreeNode( nodes[i], /*sm=*/0, /*fromGroups=*/true );
9267 case SMDSEntity_Pyramid:
9268 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9269 nodes[3], nodes[4], id, theForce3d);
9271 case SMDSEntity_Penta:
9272 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9273 nodes[3], nodes[4], nodes[5], id, theForce3d);
9275 case SMDSEntity_Hexagonal_Prism:
9277 NewVolume = aHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9279 ReplaceElemInGroups(volume, NewVolume, meshDS);
9284 { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9285 // aHelper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9286 // aHelper.FixQuadraticElements(myError);
9287 SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
9291 //================================================================================
9293 * \brief Makes given elements quadratic
9294 * \param theForce3d - if true, the medium nodes will be placed in the middle of link
9295 * \param theElements - elements to make quadratic
9297 //================================================================================
9299 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d,
9300 TIDSortedElemSet& theElements,
9301 const bool theToBiQuad)
9303 if ( theElements.empty() ) return;
9305 // we believe that all theElements are of the same type
9306 const SMDSAbs_ElementType elemType = (*theElements.begin())->GetType();
9308 // get all nodes shared by theElements
9309 TIDSortedNodeSet allNodes;
9310 TIDSortedElemSet::iterator eIt = theElements.begin();
9311 for ( ; eIt != theElements.end(); ++eIt )
9312 allNodes.insert( (*eIt)->begin_nodes(), (*eIt)->end_nodes() );
9314 // complete theElements with elements of lower dim whose all nodes are in allNodes
9316 TIDSortedElemSet quadAdjacentElems [ SMDSAbs_NbElementTypes ]; // quadratic adjacent elements
9317 TIDSortedElemSet checkedAdjacentElems [ SMDSAbs_NbElementTypes ];
9318 TIDSortedNodeSet::iterator nIt = allNodes.begin();
9319 for ( ; nIt != allNodes.end(); ++nIt )
9321 const SMDS_MeshNode* n = *nIt;
9322 SMDS_ElemIteratorPtr invIt = n->GetInverseElementIterator();
9323 while ( invIt->more() )
9325 const SMDS_MeshElement* e = invIt->next();
9326 const SMDSAbs_ElementType type = e->GetType();
9327 if ( e->IsQuadratic() )
9329 quadAdjacentElems[ type ].insert( e );
9332 switch ( e->GetEntityType() ) {
9333 case SMDSEntity_Quad_Triangle:
9334 case SMDSEntity_Quad_Quadrangle:
9335 case SMDSEntity_Quad_Hexa: alreadyOK = !theToBiQuad; break;
9336 case SMDSEntity_BiQuad_Triangle:
9337 case SMDSEntity_BiQuad_Quadrangle:
9338 case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; break;
9339 default: alreadyOK = true;
9344 if ( type >= elemType )
9345 continue; // same type or more complex linear element
9347 if ( !checkedAdjacentElems[ type ].insert( e ).second )
9348 continue; // e is already checked
9352 SMDS_NodeIteratorPtr nodeIt = e->nodeIterator();
9353 while ( nodeIt->more() && allIn )
9354 allIn = allNodes.count( nodeIt->next() );
9356 theElements.insert(e );
9360 SMESH_MesherHelper helper(*myMesh);
9361 helper.SetIsQuadratic( true );
9362 helper.SetIsBiQuadratic( theToBiQuad );
9364 // add links of quadratic adjacent elements to the helper
9366 if ( !quadAdjacentElems[SMDSAbs_Edge].empty() )
9367 for ( eIt = quadAdjacentElems[SMDSAbs_Edge].begin();
9368 eIt != quadAdjacentElems[SMDSAbs_Edge].end(); ++eIt )
9370 helper.AddTLinks( static_cast< const SMDS_MeshEdge*> (*eIt) );
9372 if ( !quadAdjacentElems[SMDSAbs_Face].empty() )
9373 for ( eIt = quadAdjacentElems[SMDSAbs_Face].begin();
9374 eIt != quadAdjacentElems[SMDSAbs_Face].end(); ++eIt )
9376 helper.AddTLinks( static_cast< const SMDS_MeshFace*> (*eIt) );
9378 if ( !quadAdjacentElems[SMDSAbs_Volume].empty() )
9379 for ( eIt = quadAdjacentElems[SMDSAbs_Volume].begin();
9380 eIt != quadAdjacentElems[SMDSAbs_Volume].end(); ++eIt )
9382 helper.AddTLinks( static_cast< const SMDS_MeshVolume*> (*eIt) );
9385 // make quadratic (or bi-tri-quadratic) elements instead of linear ones
9387 SMESHDS_Mesh* meshDS = GetMeshDS();
9388 SMESHDS_SubMesh* smDS = 0;
9389 for ( eIt = theElements.begin(); eIt != theElements.end(); ++eIt )
9391 const SMDS_MeshElement* elem = *eIt;
9394 int nbCentralNodes = 0;
9395 switch ( elem->GetEntityType() ) {
9396 // linear convertible
9397 case SMDSEntity_Edge:
9398 case SMDSEntity_Triangle:
9399 case SMDSEntity_Quadrangle:
9400 case SMDSEntity_Tetra:
9401 case SMDSEntity_Pyramid:
9402 case SMDSEntity_Hexa:
9403 case SMDSEntity_Penta: alreadyOK = false; nbCentralNodes = 0; break;
9404 // quadratic that can become bi-quadratic
9405 case SMDSEntity_Quad_Triangle:
9406 case SMDSEntity_Quad_Quadrangle:
9407 case SMDSEntity_Quad_Hexa: alreadyOK =!theToBiQuad; nbCentralNodes = 0; break;
9409 case SMDSEntity_BiQuad_Triangle:
9410 case SMDSEntity_BiQuad_Quadrangle: alreadyOK = theToBiQuad; nbCentralNodes = 1; break;
9411 case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; nbCentralNodes = 7; break;
9413 default: alreadyOK = true;
9415 if ( alreadyOK ) continue;
9417 const SMDSAbs_ElementType type = elem->GetType();
9418 const int id = elem->GetID();
9419 const int nbNodes = elem->NbCornerNodes();
9420 vector<const SMDS_MeshNode *> nodes ( elem->begin_nodes(), elem->end_nodes());
9422 helper.SetSubShape( elem->getshapeId() );
9424 if ( !smDS || !smDS->Contains( elem ))
9425 smDS = meshDS->MeshElements( elem->getshapeId() );
9426 meshDS->RemoveFreeElement(elem, smDS, /*fromGroups=*/false);
9428 SMDS_MeshElement * newElem = 0;
9431 case 4: // cases for most frequently used element types go first (for optimization)
9432 if ( type == SMDSAbs_Volume )
9433 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9435 newElem = helper.AddFace (nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9438 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9439 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9442 newElem = helper.AddFace (nodes[0], nodes[1], nodes[2], id, theForce3d);
9445 newElem = helper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9448 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9449 nodes[4], id, theForce3d);
9452 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9453 nodes[4], nodes[5], id, theForce3d);
9457 ReplaceElemInGroups( elem, newElem, meshDS);
9458 if( newElem && smDS )
9459 smDS->AddElement( newElem );
9461 // remove central nodes
9462 for ( size_t i = nodes.size() - nbCentralNodes; i < nodes.size(); ++i )
9463 if ( nodes[i]->NbInverseElements() == 0 )
9464 meshDS->RemoveFreeNode( nodes[i], smDS, /*fromGroups=*/true );
9466 } // loop on theElements
9469 { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9470 // helper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9471 // helper.FixQuadraticElements( myError );
9472 SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
9476 //=======================================================================
9478 * \brief Convert quadratic elements to linear ones and remove quadratic nodes
9479 * \return int - nb of checked elements
9481 //=======================================================================
9483 int SMESH_MeshEditor::removeQuadElem(SMESHDS_SubMesh * theSm,
9484 SMDS_ElemIteratorPtr theItr,
9485 const int theShapeID)
9488 SMESHDS_Mesh* meshDS = GetMeshDS();
9490 while( theItr->more() )
9492 const SMDS_MeshElement* elem = theItr->next();
9494 if( elem && elem->IsQuadratic())
9496 int id = elem->GetID();
9497 int nbCornerNodes = elem->NbCornerNodes();
9498 SMDSAbs_ElementType aType = elem->GetType();
9500 vector<const SMDS_MeshNode *> nodes( elem->begin_nodes(), elem->end_nodes() );
9502 //remove a quadratic element
9503 if ( !theSm || !theSm->Contains( elem ))
9504 theSm = meshDS->MeshElements( elem->getshapeId() );
9505 meshDS->RemoveFreeElement( elem, theSm, /*fromGroups=*/false );
9507 // remove medium nodes
9508 for ( unsigned i = nbCornerNodes; i < nodes.size(); ++i )
9509 if ( nodes[i]->NbInverseElements() == 0 )
9510 meshDS->RemoveFreeNode( nodes[i], theSm );
9512 // add a linear element
9513 nodes.resize( nbCornerNodes );
9514 SMDS_MeshElement * newElem = AddElement( nodes, aType, false, id );
9515 ReplaceElemInGroups(elem, newElem, meshDS);
9516 if( theSm && newElem )
9517 theSm->AddElement( newElem );
9523 //=======================================================================
9524 //function : ConvertFromQuadratic
9526 //=======================================================================
9528 bool SMESH_MeshEditor::ConvertFromQuadratic()
9530 int nbCheckedElems = 0;
9531 if ( myMesh->HasShapeToMesh() )
9533 if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9535 SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9536 while ( smIt->more() ) {
9537 SMESH_subMesh* sm = smIt->next();
9538 if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() )
9539 nbCheckedElems += removeQuadElem( smDS, smDS->GetElements(), sm->GetId() );
9545 GetMeshDS()->NbEdges() + GetMeshDS()->NbFaces() + GetMeshDS()->NbVolumes();
9546 if ( nbCheckedElems < totalNbElems ) // not all elements are in submeshes
9548 SMESHDS_SubMesh *aSM = 0;
9549 removeQuadElem( aSM, GetMeshDS()->elementsIterator(), 0 );
9557 //================================================================================
9559 * \brief Return true if all medium nodes of the element are in the node set
9561 //================================================================================
9563 bool allMediumNodesIn(const SMDS_MeshElement* elem, TIDSortedNodeSet& nodeSet )
9565 for ( int i = elem->NbCornerNodes(); i < elem->NbNodes(); ++i )
9566 if ( !nodeSet.count( elem->GetNode(i) ))
9572 //================================================================================
9574 * \brief Makes given elements linear
9576 //================================================================================
9578 void SMESH_MeshEditor::ConvertFromQuadratic(TIDSortedElemSet& theElements)
9580 if ( theElements.empty() ) return;
9582 // collect IDs of medium nodes of theElements; some of these nodes will be removed
9583 set<int> mediumNodeIDs;
9584 TIDSortedElemSet::iterator eIt = theElements.begin();
9585 for ( ; eIt != theElements.end(); ++eIt )
9587 const SMDS_MeshElement* e = *eIt;
9588 for ( int i = e->NbCornerNodes(); i < e->NbNodes(); ++i )
9589 mediumNodeIDs.insert( e->GetNode(i)->GetID() );
9592 // replace given elements by linear ones
9593 SMDS_ElemIteratorPtr elemIt = elemSetIterator( theElements );
9594 removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9596 // we need to convert remaining elements whose all medium nodes are in mediumNodeIDs
9597 // except those elements sharing medium nodes of quadratic element whose medium nodes
9598 // are not all in mediumNodeIDs
9600 // get remaining medium nodes
9601 TIDSortedNodeSet mediumNodes;
9602 set<int>::iterator nIdsIt = mediumNodeIDs.begin();
9603 for ( ; nIdsIt != mediumNodeIDs.end(); ++nIdsIt )
9604 if ( const SMDS_MeshNode* n = GetMeshDS()->FindNode( *nIdsIt ))
9605 mediumNodes.insert( mediumNodes.end(), n );
9607 // find more quadratic elements to convert
9608 TIDSortedElemSet moreElemsToConvert;
9609 TIDSortedNodeSet::iterator nIt = mediumNodes.begin();
9610 for ( ; nIt != mediumNodes.end(); ++nIt )
9612 SMDS_ElemIteratorPtr invIt = (*nIt)->GetInverseElementIterator();
9613 while ( invIt->more() )
9615 const SMDS_MeshElement* e = invIt->next();
9616 if ( e->IsQuadratic() && allMediumNodesIn( e, mediumNodes ))
9618 // find a more complex element including e and
9619 // whose medium nodes are not in mediumNodes
9620 bool complexFound = false;
9621 for ( int type = e->GetType() + 1; type < SMDSAbs_0DElement; ++type )
9623 SMDS_ElemIteratorPtr invIt2 =
9624 (*nIt)->GetInverseElementIterator( SMDSAbs_ElementType( type ));
9625 while ( invIt2->more() )
9627 const SMDS_MeshElement* eComplex = invIt2->next();
9628 if ( eComplex->IsQuadratic() && !allMediumNodesIn( eComplex, mediumNodes))
9630 int nbCommonNodes = SMESH_MeshAlgos::GetCommonNodes( e, eComplex ).size();
9631 if ( nbCommonNodes == e->NbNodes())
9633 complexFound = true;
9634 type = SMDSAbs_NbElementTypes; // to quit from the outer loop
9640 if ( !complexFound )
9641 moreElemsToConvert.insert( e );
9645 elemIt = elemSetIterator( moreElemsToConvert );
9646 removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9649 //=======================================================================
9650 //function : SewSideElements
9652 //=======================================================================
9654 SMESH_MeshEditor::Sew_Error
9655 SMESH_MeshEditor::SewSideElements (TIDSortedElemSet& theSide1,
9656 TIDSortedElemSet& theSide2,
9657 const SMDS_MeshNode* theFirstNode1,
9658 const SMDS_MeshNode* theFirstNode2,
9659 const SMDS_MeshNode* theSecondNode1,
9660 const SMDS_MeshNode* theSecondNode2)
9662 myLastCreatedElems.Clear();
9663 myLastCreatedNodes.Clear();
9665 MESSAGE ("::::SewSideElements()");
9666 if ( theSide1.size() != theSide2.size() )
9667 return SEW_DIFF_NB_OF_ELEMENTS;
9669 Sew_Error aResult = SEW_OK;
9671 // 1. Build set of faces representing each side
9672 // 2. Find which nodes of the side 1 to merge with ones on the side 2
9673 // 3. Replace nodes in elements of the side 1 and remove replaced nodes
9675 // =======================================================================
9676 // 1. Build set of faces representing each side:
9677 // =======================================================================
9678 // a. build set of nodes belonging to faces
9679 // b. complete set of faces: find missing faces whose nodes are in set of nodes
9680 // c. create temporary faces representing side of volumes if correspondent
9681 // face does not exist
9683 SMESHDS_Mesh* aMesh = GetMeshDS();
9684 // TODO algoritm not OK with vtkUnstructuredGrid: 2 meshes can't share nodes
9685 //SMDS_Mesh aTmpFacesMesh; // try to use the same mesh
9686 TIDSortedElemSet faceSet1, faceSet2;
9687 set<const SMDS_MeshElement*> volSet1, volSet2;
9688 set<const SMDS_MeshNode*> nodeSet1, nodeSet2;
9689 TIDSortedElemSet * faceSetPtr[] = { &faceSet1, &faceSet2 };
9690 set<const SMDS_MeshElement*> * volSetPtr[] = { &volSet1, &volSet2 };
9691 set<const SMDS_MeshNode*> * nodeSetPtr[] = { &nodeSet1, &nodeSet2 };
9692 TIDSortedElemSet * elemSetPtr[] = { &theSide1, &theSide2 };
9693 int iSide, iFace, iNode;
9695 list<const SMDS_MeshElement* > tempFaceList;
9696 for ( iSide = 0; iSide < 2; iSide++ ) {
9697 set<const SMDS_MeshNode*> * nodeSet = nodeSetPtr[ iSide ];
9698 TIDSortedElemSet * elemSet = elemSetPtr[ iSide ];
9699 TIDSortedElemSet * faceSet = faceSetPtr[ iSide ];
9700 set<const SMDS_MeshElement*> * volSet = volSetPtr [ iSide ];
9701 set<const SMDS_MeshElement*>::iterator vIt;
9702 TIDSortedElemSet::iterator eIt;
9703 set<const SMDS_MeshNode*>::iterator nIt;
9705 // check that given nodes belong to given elements
9706 const SMDS_MeshNode* n1 = ( iSide == 0 ) ? theFirstNode1 : theFirstNode2;
9707 const SMDS_MeshNode* n2 = ( iSide == 0 ) ? theSecondNode1 : theSecondNode2;
9708 int firstIndex = -1, secondIndex = -1;
9709 for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9710 const SMDS_MeshElement* elem = *eIt;
9711 if ( firstIndex < 0 ) firstIndex = elem->GetNodeIndex( n1 );
9712 if ( secondIndex < 0 ) secondIndex = elem->GetNodeIndex( n2 );
9713 if ( firstIndex > -1 && secondIndex > -1 ) break;
9715 if ( firstIndex < 0 || secondIndex < 0 ) {
9716 // we can simply return until temporary faces created
9717 return (iSide == 0 ) ? SEW_BAD_SIDE1_NODES : SEW_BAD_SIDE2_NODES;
9720 // -----------------------------------------------------------
9721 // 1a. Collect nodes of existing faces
9722 // and build set of face nodes in order to detect missing
9723 // faces corresponding to sides of volumes
9724 // -----------------------------------------------------------
9726 set< set <const SMDS_MeshNode*> > setOfFaceNodeSet;
9728 // loop on the given element of a side
9729 for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9730 //const SMDS_MeshElement* elem = *eIt;
9731 const SMDS_MeshElement* elem = *eIt;
9732 if ( elem->GetType() == SMDSAbs_Face ) {
9733 faceSet->insert( elem );
9734 set <const SMDS_MeshNode*> faceNodeSet;
9735 SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
9736 while ( nodeIt->more() ) {
9737 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9738 nodeSet->insert( n );
9739 faceNodeSet.insert( n );
9741 setOfFaceNodeSet.insert( faceNodeSet );
9743 else if ( elem->GetType() == SMDSAbs_Volume )
9744 volSet->insert( elem );
9746 // ------------------------------------------------------------------------------
9747 // 1b. Complete set of faces: find missing faces whose nodes are in set of nodes
9748 // ------------------------------------------------------------------------------
9750 for ( nIt = nodeSet->begin(); nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
9751 SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
9752 while ( fIt->more() ) { // loop on faces sharing a node
9753 const SMDS_MeshElement* f = fIt->next();
9754 if ( faceSet->find( f ) == faceSet->end() ) {
9755 // check if all nodes are in nodeSet and
9756 // complete setOfFaceNodeSet if they are
9757 set <const SMDS_MeshNode*> faceNodeSet;
9758 SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
9759 bool allInSet = true;
9760 while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
9761 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9762 if ( nodeSet->find( n ) == nodeSet->end() )
9765 faceNodeSet.insert( n );
9768 faceSet->insert( f );
9769 setOfFaceNodeSet.insert( faceNodeSet );
9775 // -------------------------------------------------------------------------
9776 // 1c. Create temporary faces representing sides of volumes if correspondent
9777 // face does not exist
9778 // -------------------------------------------------------------------------
9780 if ( !volSet->empty() ) {
9781 //int nodeSetSize = nodeSet->size();
9783 // loop on given volumes
9784 for ( vIt = volSet->begin(); vIt != volSet->end(); vIt++ ) {
9785 SMDS_VolumeTool vol (*vIt);
9786 // loop on volume faces: find free faces
9787 // --------------------------------------
9788 list<const SMDS_MeshElement* > freeFaceList;
9789 for ( iFace = 0; iFace < vol.NbFaces(); iFace++ ) {
9790 if ( !vol.IsFreeFace( iFace ))
9792 // check if there is already a face with same nodes in a face set
9793 const SMDS_MeshElement* aFreeFace = 0;
9794 const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iFace );
9795 int nbNodes = vol.NbFaceNodes( iFace );
9796 set <const SMDS_MeshNode*> faceNodeSet;
9797 vol.GetFaceNodes( iFace, faceNodeSet );
9798 bool isNewFace = setOfFaceNodeSet.insert( faceNodeSet ).second;
9800 // no such a face is given but it still can exist, check it
9801 vector<const SMDS_MeshNode *> nodes ( fNodes, fNodes + nbNodes);
9802 aFreeFace = aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false );
9805 // create a temporary face
9806 if ( nbNodes == 3 ) {
9807 //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2] );
9808 aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2] );
9810 else if ( nbNodes == 4 ) {
9811 //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
9812 aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
9815 vector<const SMDS_MeshNode *> poly_nodes ( fNodes, & fNodes[nbNodes]);
9816 //aFreeFace = aTmpFacesMesh.AddPolygonalFace(poly_nodes);
9817 aFreeFace = aMesh->AddPolygonalFace(poly_nodes);
9820 tempFaceList.push_back( aFreeFace );
9824 freeFaceList.push_back( aFreeFace );
9826 } // loop on faces of a volume
9828 // choose one of several free faces of a volume
9829 // --------------------------------------------
9830 if ( freeFaceList.size() > 1 ) {
9831 // choose a face having max nb of nodes shared by other elems of a side
9832 int maxNbNodes = -1;
9833 list<const SMDS_MeshElement* >::iterator fIt = freeFaceList.begin();
9834 while ( fIt != freeFaceList.end() ) { // loop on free faces
9835 int nbSharedNodes = 0;
9836 SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
9837 while ( nodeIt->more() ) { // loop on free face nodes
9838 const SMDS_MeshNode* n =
9839 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9840 SMDS_ElemIteratorPtr invElemIt = n->GetInverseElementIterator();
9841 while ( invElemIt->more() ) {
9842 const SMDS_MeshElement* e = invElemIt->next();
9843 nbSharedNodes += faceSet->count( e );
9844 nbSharedNodes += elemSet->count( e );
9847 if ( nbSharedNodes > maxNbNodes ) {
9848 maxNbNodes = nbSharedNodes;
9849 freeFaceList.erase( freeFaceList.begin(), fIt++ );
9851 else if ( nbSharedNodes == maxNbNodes ) {
9855 freeFaceList.erase( fIt++ ); // here fIt++ occurs before erase
9858 if ( freeFaceList.size() > 1 )
9860 // could not choose one face, use another way
9861 // choose a face most close to the bary center of the opposite side
9862 gp_XYZ aBC( 0., 0., 0. );
9863 set <const SMDS_MeshNode*> addedNodes;
9864 TIDSortedElemSet * elemSet2 = elemSetPtr[ 1 - iSide ];
9865 eIt = elemSet2->begin();
9866 for ( eIt = elemSet2->begin(); eIt != elemSet2->end(); eIt++ ) {
9867 SMDS_ElemIteratorPtr nodeIt = (*eIt)->nodesIterator();
9868 while ( nodeIt->more() ) { // loop on free face nodes
9869 const SMDS_MeshNode* n =
9870 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9871 if ( addedNodes.insert( n ).second )
9872 aBC += gp_XYZ( n->X(),n->Y(),n->Z() );
9875 aBC /= addedNodes.size();
9876 double minDist = DBL_MAX;
9877 fIt = freeFaceList.begin();
9878 while ( fIt != freeFaceList.end() ) { // loop on free faces
9880 SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
9881 while ( nodeIt->more() ) { // loop on free face nodes
9882 const SMDS_MeshNode* n =
9883 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9884 gp_XYZ p( n->X(),n->Y(),n->Z() );
9885 dist += ( aBC - p ).SquareModulus();
9887 if ( dist < minDist ) {
9889 freeFaceList.erase( freeFaceList.begin(), fIt++ );
9892 fIt = freeFaceList.erase( fIt++ );
9895 } // choose one of several free faces of a volume
9897 if ( freeFaceList.size() == 1 ) {
9898 const SMDS_MeshElement* aFreeFace = freeFaceList.front();
9899 faceSet->insert( aFreeFace );
9900 // complete a node set with nodes of a found free face
9901 // for ( iNode = 0; iNode < ; iNode++ )
9902 // nodeSet->insert( fNodes[ iNode ] );
9905 } // loop on volumes of a side
9907 // // complete a set of faces if new nodes in a nodeSet appeared
9908 // // ----------------------------------------------------------
9909 // if ( nodeSetSize != nodeSet->size() ) {
9910 // for ( ; nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
9911 // SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
9912 // while ( fIt->more() ) { // loop on faces sharing a node
9913 // const SMDS_MeshElement* f = fIt->next();
9914 // if ( faceSet->find( f ) == faceSet->end() ) {
9915 // // check if all nodes are in nodeSet and
9916 // // complete setOfFaceNodeSet if they are
9917 // set <const SMDS_MeshNode*> faceNodeSet;
9918 // SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
9919 // bool allInSet = true;
9920 // while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
9921 // const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9922 // if ( nodeSet->find( n ) == nodeSet->end() )
9923 // allInSet = false;
9925 // faceNodeSet.insert( n );
9927 // if ( allInSet ) {
9928 // faceSet->insert( f );
9929 // setOfFaceNodeSet.insert( faceNodeSet );
9935 } // Create temporary faces, if there are volumes given
9938 if ( faceSet1.size() != faceSet2.size() ) {
9939 // delete temporary faces: they are in reverseElements of actual nodes
9940 // SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
9941 // while ( tmpFaceIt->more() )
9942 // aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
9943 // list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
9944 // for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
9945 // aMesh->RemoveElement(*tmpFaceIt);
9946 MESSAGE("Diff nb of faces");
9947 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9950 // ============================================================
9951 // 2. Find nodes to merge:
9952 // bind a node to remove to a node to put instead
9953 // ============================================================
9955 TNodeNodeMap nReplaceMap; // bind a node to remove to a node to put instead
9956 if ( theFirstNode1 != theFirstNode2 )
9957 nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
9958 if ( theSecondNode1 != theSecondNode2 )
9959 nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
9961 LinkID_Gen aLinkID_Gen( GetMeshDS() );
9962 set< long > linkIdSet; // links to process
9963 linkIdSet.insert( aLinkID_Gen.GetLinkID( theFirstNode1, theSecondNode1 ));
9965 typedef pair< const SMDS_MeshNode*, const SMDS_MeshNode* > NLink;
9966 list< NLink > linkList[2];
9967 linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
9968 linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
9969 // loop on links in linkList; find faces by links and append links
9970 // of the found faces to linkList
9971 list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
9972 for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ )
9974 NLink link[] = { *linkIt[0], *linkIt[1] };
9975 long linkID = aLinkID_Gen.GetLinkID( link[0].first, link[0].second );
9976 if ( !linkIdSet.count( linkID ) )
9979 // by links, find faces in the face sets,
9980 // and find indices of link nodes in the found faces;
9981 // in a face set, there is only one or no face sharing a link
9982 // ---------------------------------------------------------------
9984 const SMDS_MeshElement* face[] = { 0, 0 };
9985 vector<const SMDS_MeshNode*> fnodes[2];
9986 int iLinkNode[2][2];
9987 TIDSortedElemSet avoidSet;
9988 for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
9989 const SMDS_MeshNode* n1 = link[iSide].first;
9990 const SMDS_MeshNode* n2 = link[iSide].second;
9991 //cout << "Side " << iSide << " ";
9992 //cout << "L( " << n1->GetID() << ", " << n2->GetID() << " ) " << endl;
9993 // find a face by two link nodes
9994 face[ iSide ] = SMESH_MeshAlgos::FindFaceInSet( n1, n2,
9995 *faceSetPtr[ iSide ], avoidSet,
9996 &iLinkNode[iSide][0],
9997 &iLinkNode[iSide][1] );
10000 //cout << " F " << face[ iSide]->GetID() <<endl;
10001 faceSetPtr[ iSide ]->erase( face[ iSide ]);
10002 // put face nodes to fnodes
10003 if ( face[ iSide ]->IsQuadratic() )
10005 // use interlaced nodes iterator
10006 const SMDS_VtkFace* F = dynamic_cast<const SMDS_VtkFace*>( face[ iSide ]);
10007 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
10008 SMDS_ElemIteratorPtr nIter = F->interlacedNodesElemIterator();
10009 while ( nIter->more() )
10010 fnodes[ iSide ].push_back( cast2Node( nIter->next() ));
10014 fnodes[ iSide ].assign( face[ iSide ]->begin_nodes(),
10015 face[ iSide ]->end_nodes() );
10017 fnodes[ iSide ].push_back( fnodes[ iSide ].front());
10021 // check similarity of elements of the sides
10022 if (aResult == SEW_OK && (( face[0] && !face[1] ) || ( !face[0] && face[1] ))) {
10023 MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
10024 if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
10025 aResult = ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10028 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10030 break; // do not return because it's necessary to remove tmp faces
10033 // set nodes to merge
10034 // -------------------
10036 if ( face[0] && face[1] ) {
10037 const int nbNodes = face[0]->NbNodes();
10038 if ( nbNodes != face[1]->NbNodes() ) {
10039 MESSAGE("Diff nb of face nodes");
10040 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10041 break; // do not return because it s necessary to remove tmp faces
10043 bool reverse[] = { false, false }; // order of nodes in the link
10044 for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
10045 // analyse link orientation in faces
10046 int i1 = iLinkNode[ iSide ][ 0 ];
10047 int i2 = iLinkNode[ iSide ][ 1 ];
10048 reverse[ iSide ] = Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1;
10050 int di1 = reverse[0] ? -1 : +1, i1 = iLinkNode[0][1] + di1;
10051 int di2 = reverse[1] ? -1 : +1, i2 = iLinkNode[1][1] + di2;
10052 for ( int i = nbNodes - 2; i > 0; --i, i1 += di1, i2 += di2 )
10054 nReplaceMap.insert ( make_pair ( fnodes[0][ ( i1 + nbNodes ) % nbNodes ],
10055 fnodes[1][ ( i2 + nbNodes ) % nbNodes ]));
10058 // add other links of the faces to linkList
10059 // -----------------------------------------
10061 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
10062 linkID = aLinkID_Gen.GetLinkID( fnodes[0][iNode], fnodes[0][iNode+1] );
10063 pair< set<long>::iterator, bool > iter_isnew = linkIdSet.insert( linkID );
10064 if ( !iter_isnew.second ) { // already in a set: no need to process
10065 linkIdSet.erase( iter_isnew.first );
10067 else // new in set == encountered for the first time: add
10069 const SMDS_MeshNode* n1 = fnodes[0][ iNode ];
10070 const SMDS_MeshNode* n2 = fnodes[0][ iNode + 1];
10071 linkList[0].push_back ( NLink( n1, n2 ));
10072 linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10077 if ( faceSetPtr[0]->empty() || faceSetPtr[1]->empty() )
10080 } // loop on link lists
10082 if ( aResult == SEW_OK &&
10083 ( //linkIt[0] != linkList[0].end() ||
10084 !faceSetPtr[0]->empty() || !faceSetPtr[1]->empty() )) {
10085 MESSAGE( (linkIt[0] != linkList[0].end()) <<" "<< (faceSetPtr[0]->empty()) <<
10086 " " << (faceSetPtr[1]->empty()));
10087 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10090 // ====================================================================
10091 // 3. Replace nodes in elements of the side 1 and remove replaced nodes
10092 // ====================================================================
10094 // delete temporary faces
10095 // SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
10096 // while ( tmpFaceIt->more() )
10097 // aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
10098 list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
10099 for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
10100 aMesh->RemoveElement(*tmpFaceIt);
10102 if ( aResult != SEW_OK)
10105 list< int > nodeIDsToRemove/*, elemIDsToRemove*/;
10106 // loop on nodes replacement map
10107 TNodeNodeMap::iterator nReplaceMapIt = nReplaceMap.begin(), nnIt;
10108 for ( ; nReplaceMapIt != nReplaceMap.end(); nReplaceMapIt++ )
10109 if ( (*nReplaceMapIt).first != (*nReplaceMapIt).second ) {
10110 const SMDS_MeshNode* nToRemove = (*nReplaceMapIt).first;
10111 nodeIDsToRemove.push_back( nToRemove->GetID() );
10112 // loop on elements sharing nToRemove
10113 SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
10114 while ( invElemIt->more() ) {
10115 const SMDS_MeshElement* e = invElemIt->next();
10116 // get a new suite of nodes: make replacement
10117 int nbReplaced = 0, i = 0, nbNodes = e->NbNodes();
10118 vector< const SMDS_MeshNode*> nodes( nbNodes );
10119 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
10120 while ( nIt->more() ) {
10121 const SMDS_MeshNode* n =
10122 static_cast<const SMDS_MeshNode*>( nIt->next() );
10123 nnIt = nReplaceMap.find( n );
10124 if ( nnIt != nReplaceMap.end() ) {
10126 n = (*nnIt).second;
10130 // if ( nbReplaced == nbNodes && e->GetType() == SMDSAbs_Face )
10131 // elemIDsToRemove.push_back( e->GetID() );
10135 SMDSAbs_ElementType etyp = e->GetType();
10136 SMDS_MeshElement* newElem = this->AddElement(nodes, etyp, false);
10139 myLastCreatedElems.Append(newElem);
10140 AddToSameGroups(newElem, e, aMesh);
10141 int aShapeId = e->getshapeId();
10144 aMesh->SetMeshElementOnShape( newElem, aShapeId );
10147 aMesh->RemoveElement(e);
10152 Remove( nodeIDsToRemove, true );
10157 //================================================================================
10159 * \brief Find corresponding nodes in two sets of faces
10160 * \param theSide1 - first face set
10161 * \param theSide2 - second first face
10162 * \param theFirstNode1 - a boundary node of set 1
10163 * \param theFirstNode2 - a node of set 2 corresponding to theFirstNode1
10164 * \param theSecondNode1 - a boundary node of set 1 linked with theFirstNode1
10165 * \param theSecondNode2 - a node of set 2 corresponding to theSecondNode1
10166 * \param nReplaceMap - output map of corresponding nodes
10167 * \return bool - is a success or not
10169 //================================================================================
10172 //#define DEBUG_MATCHING_NODES
10175 SMESH_MeshEditor::Sew_Error
10176 SMESH_MeshEditor::FindMatchingNodes(set<const SMDS_MeshElement*>& theSide1,
10177 set<const SMDS_MeshElement*>& theSide2,
10178 const SMDS_MeshNode* theFirstNode1,
10179 const SMDS_MeshNode* theFirstNode2,
10180 const SMDS_MeshNode* theSecondNode1,
10181 const SMDS_MeshNode* theSecondNode2,
10182 TNodeNodeMap & nReplaceMap)
10184 set<const SMDS_MeshElement*> * faceSetPtr[] = { &theSide1, &theSide2 };
10186 nReplaceMap.clear();
10187 if ( theFirstNode1 != theFirstNode2 )
10188 nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
10189 if ( theSecondNode1 != theSecondNode2 )
10190 nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
10192 set< SMESH_TLink > linkSet; // set of nodes where order of nodes is ignored
10193 linkSet.insert( SMESH_TLink( theFirstNode1, theSecondNode1 ));
10195 list< NLink > linkList[2];
10196 linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
10197 linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
10199 // loop on links in linkList; find faces by links and append links
10200 // of the found faces to linkList
10201 list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
10202 for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ ) {
10203 NLink link[] = { *linkIt[0], *linkIt[1] };
10204 if ( linkSet.find( link[0] ) == linkSet.end() )
10207 // by links, find faces in the face sets,
10208 // and find indices of link nodes in the found faces;
10209 // in a face set, there is only one or no face sharing a link
10210 // ---------------------------------------------------------------
10212 const SMDS_MeshElement* face[] = { 0, 0 };
10213 list<const SMDS_MeshNode*> notLinkNodes[2];
10214 //bool reverse[] = { false, false }; // order of notLinkNodes
10216 for ( int iSide = 0; iSide < 2; iSide++ ) // loop on 2 sides
10218 const SMDS_MeshNode* n1 = link[iSide].first;
10219 const SMDS_MeshNode* n2 = link[iSide].second;
10220 set<const SMDS_MeshElement*> * faceSet = faceSetPtr[ iSide ];
10221 set< const SMDS_MeshElement* > facesOfNode1;
10222 for ( int iNode = 0; iNode < 2; iNode++ ) // loop on 2 nodes of a link
10224 // during a loop of the first node, we find all faces around n1,
10225 // during a loop of the second node, we find one face sharing both n1 and n2
10226 const SMDS_MeshNode* n = iNode ? n1 : n2; // a node of a link
10227 SMDS_ElemIteratorPtr fIt = n->GetInverseElementIterator(SMDSAbs_Face);
10228 while ( fIt->more() ) { // loop on faces sharing a node
10229 const SMDS_MeshElement* f = fIt->next();
10230 if (faceSet->find( f ) != faceSet->end() && // f is in face set
10231 ! facesOfNode1.insert( f ).second ) // f encounters twice
10233 if ( face[ iSide ] ) {
10234 MESSAGE( "2 faces per link " );
10235 return ( iSide ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10238 faceSet->erase( f );
10240 // get not link nodes
10241 int nbN = f->NbNodes();
10242 if ( f->IsQuadratic() )
10244 nbNodes[ iSide ] = nbN;
10245 list< const SMDS_MeshNode* > & nodes = notLinkNodes[ iSide ];
10246 int i1 = f->GetNodeIndex( n1 );
10247 int i2 = f->GetNodeIndex( n2 );
10248 int iEnd = nbN, iBeg = -1, iDelta = 1;
10249 bool reverse = ( Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1 );
10251 std::swap( iEnd, iBeg ); iDelta = -1;
10256 if ( i == iEnd ) i = iBeg + iDelta;
10257 if ( i == i1 ) break;
10258 nodes.push_back ( f->GetNode( i ) );
10264 // check similarity of elements of the sides
10265 if (( face[0] && !face[1] ) || ( !face[0] && face[1] )) {
10266 MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
10267 if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
10268 return ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10271 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10275 // set nodes to merge
10276 // -------------------
10278 if ( face[0] && face[1] ) {
10279 if ( nbNodes[0] != nbNodes[1] ) {
10280 MESSAGE("Diff nb of face nodes");
10281 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10283 #ifdef DEBUG_MATCHING_NODES
10284 MESSAGE ( " Link 1: " << link[0].first->GetID() <<" "<< link[0].second->GetID()
10285 << " F 1: " << face[0] << "| Link 2: " << link[1].first->GetID() <<" "
10286 << link[1].second->GetID() << " F 2: " << face[1] << " | Bind: " ) ;
10288 int nbN = nbNodes[0];
10290 list<const SMDS_MeshNode*>::iterator n1 = notLinkNodes[0].begin();
10291 list<const SMDS_MeshNode*>::iterator n2 = notLinkNodes[1].begin();
10292 for ( int i = 0 ; i < nbN - 2; ++i ) {
10293 #ifdef DEBUG_MATCHING_NODES
10294 MESSAGE ( (*n1)->GetID() << " to " << (*n2)->GetID() );
10296 nReplaceMap.insert( make_pair( *(n1++), *(n2++) ));
10300 // add other links of the face 1 to linkList
10301 // -----------------------------------------
10303 const SMDS_MeshElement* f0 = face[0];
10304 const SMDS_MeshNode* n1 = f0->GetNode( nbN - 1 );
10305 for ( int i = 0; i < nbN; i++ )
10307 const SMDS_MeshNode* n2 = f0->GetNode( i );
10308 pair< set< SMESH_TLink >::iterator, bool > iter_isnew =
10309 linkSet.insert( SMESH_TLink( n1, n2 ));
10310 if ( !iter_isnew.second ) { // already in a set: no need to process
10311 linkSet.erase( iter_isnew.first );
10313 else // new in set == encountered for the first time: add
10315 #ifdef DEBUG_MATCHING_NODES
10316 MESSAGE ( "Add link 1: " << n1->GetID() << " " << n2->GetID() << " "
10317 << " | link 2: " << nReplaceMap[n1]->GetID() << " " << nReplaceMap[n2]->GetID() << " " );
10319 linkList[0].push_back ( NLink( n1, n2 ));
10320 linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10325 } // loop on link lists
10330 //================================================================================
10332 * \brief Create elements equal (on same nodes) to given ones
10333 * \param [in] theElements - a set of elems to duplicate. If it is empty, all
10334 * elements of the uppest dimension are duplicated.
10336 //================================================================================
10338 void SMESH_MeshEditor::DoubleElements( const TIDSortedElemSet& theElements )
10340 CrearLastCreated();
10341 SMESHDS_Mesh* mesh = GetMeshDS();
10343 // get an element type and an iterator over elements
10345 SMDSAbs_ElementType type;
10346 SMDS_ElemIteratorPtr elemIt;
10347 vector< const SMDS_MeshElement* > allElems;
10348 if ( theElements.empty() )
10350 if ( mesh->NbNodes() == 0 )
10352 // get most complex type
10353 SMDSAbs_ElementType types[SMDSAbs_NbElementTypes] = {
10354 SMDSAbs_Volume, SMDSAbs_Face, SMDSAbs_Edge,
10355 SMDSAbs_0DElement, SMDSAbs_Ball, SMDSAbs_Node
10357 for ( int i = 0; i < SMDSAbs_NbElementTypes; ++i )
10358 if ( mesh->GetMeshInfo().NbElements( types[i] ))
10363 // put all elements in the vector <allElems>
10364 allElems.reserve( mesh->GetMeshInfo().NbElements( type ));
10365 elemIt = mesh->elementsIterator( type );
10366 while ( elemIt->more() )
10367 allElems.push_back( elemIt->next());
10368 elemIt = elemSetIterator( allElems );
10372 type = (*theElements.begin())->GetType();
10373 elemIt = elemSetIterator( theElements );
10376 // duplicate elements
10378 if ( type == SMDSAbs_Ball )
10380 SMDS_UnstructuredGrid* vtkGrid = mesh->getGrid();
10381 while ( elemIt->more() )
10383 const SMDS_MeshElement* elem = elemIt->next();
10384 if ( elem->GetType() != SMDSAbs_Ball )
10386 if (( elem = mesh->AddBall( elem->GetNode(0),
10387 vtkGrid->GetBallDiameter( elem->getVtkId() ))))
10388 myLastCreatedElems.Append( elem );
10393 vector< const SMDS_MeshNode* > nodes;
10394 while ( elemIt->more() )
10396 const SMDS_MeshElement* elem = elemIt->next();
10397 if ( elem->GetType() != type )
10400 nodes.assign( elem->begin_nodes(), elem->end_nodes() );
10402 if ( type == SMDSAbs_Volume && elem->GetVtkType() == VTK_POLYHEDRON )
10404 std::vector<int> quantities =
10405 static_cast< const SMDS_VtkVolume* >( elem )->GetQuantities();
10406 elem = mesh->AddPolyhedralVolume( nodes, quantities );
10410 AddElement( nodes, type, elem->IsPoly() );
10411 elem = 0; // myLastCreatedElems is already filled
10414 myLastCreatedElems.Append( elem );
10419 //================================================================================
10421 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10422 \param theElems - the list of elements (edges or faces) to be replicated
10423 The nodes for duplication could be found from these elements
10424 \param theNodesNot - list of nodes to NOT replicate
10425 \param theAffectedElems - the list of elements (cells and edges) to which the
10426 replicated nodes should be associated to.
10427 \return TRUE if operation has been completed successfully, FALSE otherwise
10429 //================================================================================
10431 bool SMESH_MeshEditor::DoubleNodes( const TIDSortedElemSet& theElems,
10432 const TIDSortedElemSet& theNodesNot,
10433 const TIDSortedElemSet& theAffectedElems )
10435 myLastCreatedElems.Clear();
10436 myLastCreatedNodes.Clear();
10438 if ( theElems.size() == 0 )
10441 SMESHDS_Mesh* aMeshDS = GetMeshDS();
10446 std::map< const SMDS_MeshNode*, const SMDS_MeshNode* > anOldNodeToNewNode;
10447 // duplicate elements and nodes
10448 res = doubleNodes( aMeshDS, theElems, theNodesNot, anOldNodeToNewNode, true );
10449 // replce nodes by duplications
10450 res = doubleNodes( aMeshDS, theAffectedElems, theNodesNot, anOldNodeToNewNode, false );
10454 //================================================================================
10456 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10457 \param theMeshDS - mesh instance
10458 \param theElems - the elements replicated or modified (nodes should be changed)
10459 \param theNodesNot - nodes to NOT replicate
10460 \param theNodeNodeMap - relation of old node to new created node
10461 \param theIsDoubleElem - flag os to replicate element or modify
10462 \return TRUE if operation has been completed successfully, FALSE otherwise
10464 //================================================================================
10466 bool SMESH_MeshEditor::doubleNodes( SMESHDS_Mesh* theMeshDS,
10467 const TIDSortedElemSet& theElems,
10468 const TIDSortedElemSet& theNodesNot,
10469 std::map< const SMDS_MeshNode*,
10470 const SMDS_MeshNode* >& theNodeNodeMap,
10471 const bool theIsDoubleElem )
10473 MESSAGE("doubleNodes");
10474 // iterate on through element and duplicate them (by nodes duplication)
10476 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10477 for ( ; elemItr != theElems.end(); ++elemItr )
10479 const SMDS_MeshElement* anElem = *elemItr;
10483 bool isDuplicate = false;
10484 // duplicate nodes to duplicate element
10485 std::vector<const SMDS_MeshNode*> newNodes( anElem->NbNodes() );
10486 SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
10488 while ( anIter->more() )
10491 SMDS_MeshNode* aCurrNode = (SMDS_MeshNode*)anIter->next();
10492 SMDS_MeshNode* aNewNode = aCurrNode;
10493 if ( theNodeNodeMap.find( aCurrNode ) != theNodeNodeMap.end() )
10494 aNewNode = (SMDS_MeshNode*)theNodeNodeMap[ aCurrNode ];
10495 else if ( theIsDoubleElem && theNodesNot.find( aCurrNode ) == theNodesNot.end() )
10498 aNewNode = theMeshDS->AddNode( aCurrNode->X(), aCurrNode->Y(), aCurrNode->Z() );
10499 copyPosition( aCurrNode, aNewNode );
10500 theNodeNodeMap[ aCurrNode ] = aNewNode;
10501 myLastCreatedNodes.Append( aNewNode );
10503 isDuplicate |= (aCurrNode != aNewNode);
10504 newNodes[ ind++ ] = aNewNode;
10506 if ( !isDuplicate )
10509 if ( theIsDoubleElem )
10510 AddElement(newNodes, anElem->GetType(), anElem->IsPoly());
10512 theMeshDS->ChangeElementNodes( anElem, &newNodes[ 0 ], anElem->NbNodes() );
10519 //================================================================================
10521 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10522 \param theNodes - identifiers of nodes to be doubled
10523 \param theModifiedElems - identifiers of elements to be updated by the new (doubled)
10524 nodes. If list of element identifiers is empty then nodes are doubled but
10525 they not assigned to elements
10526 \return TRUE if operation has been completed successfully, FALSE otherwise
10528 //================================================================================
10530 bool SMESH_MeshEditor::DoubleNodes( const std::list< int >& theListOfNodes,
10531 const std::list< int >& theListOfModifiedElems )
10533 MESSAGE("DoubleNodes");
10534 myLastCreatedElems.Clear();
10535 myLastCreatedNodes.Clear();
10537 if ( theListOfNodes.size() == 0 )
10540 SMESHDS_Mesh* aMeshDS = GetMeshDS();
10544 // iterate through nodes and duplicate them
10546 std::map< const SMDS_MeshNode*, const SMDS_MeshNode* > anOldNodeToNewNode;
10548 std::list< int >::const_iterator aNodeIter;
10549 for ( aNodeIter = theListOfNodes.begin(); aNodeIter != theListOfNodes.end(); ++aNodeIter )
10551 int aCurr = *aNodeIter;
10552 SMDS_MeshNode* aNode = (SMDS_MeshNode*)aMeshDS->FindNode( aCurr );
10558 const SMDS_MeshNode* aNewNode = aMeshDS->AddNode( aNode->X(), aNode->Y(), aNode->Z() );
10561 copyPosition( aNode, aNewNode );
10562 anOldNodeToNewNode[ aNode ] = aNewNode;
10563 myLastCreatedNodes.Append( aNewNode );
10567 // Create map of new nodes for modified elements
10569 std::map< SMDS_MeshElement*, vector<const SMDS_MeshNode*> > anElemToNodes;
10571 std::list< int >::const_iterator anElemIter;
10572 for ( anElemIter = theListOfModifiedElems.begin();
10573 anElemIter != theListOfModifiedElems.end(); ++anElemIter )
10575 int aCurr = *anElemIter;
10576 SMDS_MeshElement* anElem = (SMDS_MeshElement*)aMeshDS->FindElement( aCurr );
10580 vector<const SMDS_MeshNode*> aNodeArr( anElem->NbNodes() );
10582 SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
10584 while ( anIter->more() )
10586 SMDS_MeshNode* aCurrNode = (SMDS_MeshNode*)anIter->next();
10587 if ( aCurr && anOldNodeToNewNode.find( aCurrNode ) != anOldNodeToNewNode.end() )
10589 const SMDS_MeshNode* aNewNode = anOldNodeToNewNode[ aCurrNode ];
10590 aNodeArr[ ind++ ] = aNewNode;
10593 aNodeArr[ ind++ ] = aCurrNode;
10595 anElemToNodes[ anElem ] = aNodeArr;
10598 // Change nodes of elements
10600 std::map< SMDS_MeshElement*, vector<const SMDS_MeshNode*> >::iterator
10601 anElemToNodesIter = anElemToNodes.begin();
10602 for ( ; anElemToNodesIter != anElemToNodes.end(); ++anElemToNodesIter )
10604 const SMDS_MeshElement* anElem = anElemToNodesIter->first;
10605 vector<const SMDS_MeshNode*> aNodeArr = anElemToNodesIter->second;
10608 MESSAGE("ChangeElementNodes");
10609 aMeshDS->ChangeElementNodes( anElem, &aNodeArr[ 0 ], anElem->NbNodes() );
10618 //================================================================================
10620 \brief Check if element located inside shape
10621 \return TRUE if IN or ON shape, FALSE otherwise
10623 //================================================================================
10625 template<class Classifier>
10626 bool isInside(const SMDS_MeshElement* theElem,
10627 Classifier& theClassifier,
10628 const double theTol)
10630 gp_XYZ centerXYZ (0, 0, 0);
10631 SMDS_ElemIteratorPtr aNodeItr = theElem->nodesIterator();
10632 while (aNodeItr->more())
10633 centerXYZ += SMESH_TNodeXYZ(cast2Node( aNodeItr->next()));
10635 gp_Pnt aPnt = centerXYZ / theElem->NbNodes();
10636 theClassifier.Perform(aPnt, theTol);
10637 TopAbs_State aState = theClassifier.State();
10638 return (aState == TopAbs_IN || aState == TopAbs_ON );
10641 //================================================================================
10643 * \brief Classifier of the 3D point on the TopoDS_Face
10644 * with interaface suitable for isInside()
10646 //================================================================================
10648 struct _FaceClassifier
10650 Extrema_ExtPS _extremum;
10651 BRepAdaptor_Surface _surface;
10652 TopAbs_State _state;
10654 _FaceClassifier(const TopoDS_Face& face):_extremum(),_surface(face),_state(TopAbs_OUT)
10656 _extremum.Initialize( _surface,
10657 _surface.FirstUParameter(), _surface.LastUParameter(),
10658 _surface.FirstVParameter(), _surface.LastVParameter(),
10659 _surface.Tolerance(), _surface.Tolerance() );
10661 void Perform(const gp_Pnt& aPnt, double theTol)
10664 _state = TopAbs_OUT;
10665 _extremum.Perform(aPnt);
10666 if ( _extremum.IsDone() )
10667 for ( int iSol = 1; iSol <= _extremum.NbExt() && _state == TopAbs_OUT; ++iSol)
10668 _state = ( _extremum.SquareDistance(iSol) <= theTol ? TopAbs_IN : TopAbs_OUT );
10670 TopAbs_State State() const
10677 //================================================================================
10679 \brief Identify the elements that will be affected by node duplication (actual duplication is not performed).
10680 This method is the first step of DoubleNodeElemGroupsInRegion.
10681 \param theElems - list of groups of elements (edges or faces) to be replicated
10682 \param theNodesNot - list of groups of nodes not to replicated
10683 \param theShape - shape to detect affected elements (element which geometric center
10684 located on or inside shape). If the shape is null, detection is done on faces orientations
10685 (select elements with a gravity center on the side given by faces normals).
10686 This mode (null shape) is faster, but works only when theElems are faces, with coherents orientations.
10687 The replicated nodes should be associated to affected elements.
10688 \return groups of affected elements
10689 \sa DoubleNodeElemGroupsInRegion()
10691 //================================================================================
10693 bool SMESH_MeshEditor::AffectedElemGroupsInRegion( const TIDSortedElemSet& theElems,
10694 const TIDSortedElemSet& theNodesNot,
10695 const TopoDS_Shape& theShape,
10696 TIDSortedElemSet& theAffectedElems)
10698 if ( theShape.IsNull() )
10700 std::set<const SMDS_MeshNode*> alreadyCheckedNodes;
10701 std::set<const SMDS_MeshElement*> alreadyCheckedElems;
10702 std::set<const SMDS_MeshElement*> edgesToCheck;
10703 alreadyCheckedNodes.clear();
10704 alreadyCheckedElems.clear();
10705 edgesToCheck.clear();
10707 // --- iterates on elements to be replicated and get elements by back references from their nodes
10709 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10711 for ( ielem=1; elemItr != theElems.end(); ++elemItr )
10713 SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
10714 if (!anElem || (anElem->GetType() != SMDSAbs_Face))
10717 SMESH_MeshAlgos::FaceNormal( anElem, normal, /*normalized=*/true );
10718 MESSAGE("element " << ielem++ << " normal " << normal.X() << " " << normal.Y() << " " << normal.Z());
10719 std::set<const SMDS_MeshNode*> nodesElem;
10721 SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
10722 while ( nodeItr->more() )
10724 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10725 nodesElem.insert(aNode);
10727 std::set<const SMDS_MeshNode*>::iterator nodit = nodesElem.begin();
10728 for (; nodit != nodesElem.end(); nodit++)
10730 MESSAGE(" noeud ");
10731 const SMDS_MeshNode* aNode = *nodit;
10732 if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
10734 if (alreadyCheckedNodes.find(aNode) != alreadyCheckedNodes.end())
10736 alreadyCheckedNodes.insert(aNode);
10737 SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
10738 while ( backElemItr->more() )
10740 MESSAGE(" backelem ");
10741 const SMDS_MeshElement* curElem = backElemItr->next();
10742 if (alreadyCheckedElems.find(curElem) != alreadyCheckedElems.end())
10744 if (theElems.find(curElem) != theElems.end())
10746 alreadyCheckedElems.insert(curElem);
10747 double x=0, y=0, z=0;
10749 SMDS_ElemIteratorPtr nodeItr2 = curElem->nodesIterator();
10750 while ( nodeItr2->more() )
10752 const SMDS_MeshNode* anotherNode = cast2Node(nodeItr2->next());
10753 x += anotherNode->X();
10754 y += anotherNode->Y();
10755 z += anotherNode->Z();
10759 p.SetCoord( x/nb -aNode->X(),
10761 z/nb -aNode->Z() );
10762 MESSAGE(" check " << p.X() << " " << p.Y() << " " << p.Z());
10765 MESSAGE(" --- inserted")
10766 theAffectedElems.insert( curElem );
10768 else if (curElem->GetType() == SMDSAbs_Edge)
10769 edgesToCheck.insert(curElem);
10773 // --- add also edges lying on the set of faces (all nodes in alreadyCheckedNodes)
10774 std::set<const SMDS_MeshElement*>::iterator eit = edgesToCheck.begin();
10775 for( ; eit != edgesToCheck.end(); eit++)
10777 bool onside = true;
10778 const SMDS_MeshElement* anEdge = *eit;
10779 SMDS_ElemIteratorPtr nodeItr = anEdge->nodesIterator();
10780 while ( nodeItr->more() )
10782 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10783 if (alreadyCheckedNodes.find(aNode) == alreadyCheckedNodes.end())
10791 MESSAGE(" --- edge onside inserted")
10792 theAffectedElems.insert(anEdge);
10798 const double aTol = Precision::Confusion();
10799 auto_ptr< BRepClass3d_SolidClassifier> bsc3d;
10800 auto_ptr<_FaceClassifier> aFaceClassifier;
10801 if ( theShape.ShapeType() == TopAbs_SOLID )
10803 bsc3d.reset( new BRepClass3d_SolidClassifier(theShape));;
10804 bsc3d->PerformInfinitePoint(aTol);
10806 else if (theShape.ShapeType() == TopAbs_FACE )
10808 aFaceClassifier.reset( new _FaceClassifier(TopoDS::Face(theShape)));
10811 // iterates on indicated elements and get elements by back references from their nodes
10812 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10814 for ( ielem = 1; elemItr != theElems.end(); ++elemItr )
10816 MESSAGE("element " << ielem++);
10817 SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
10820 SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
10821 while ( nodeItr->more() )
10823 MESSAGE(" noeud ");
10824 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10825 if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
10827 SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
10828 while ( backElemItr->more() )
10830 MESSAGE(" backelem ");
10831 const SMDS_MeshElement* curElem = backElemItr->next();
10832 if ( curElem && theElems.find(curElem) == theElems.end() &&
10834 isInside( curElem, *bsc3d, aTol ) :
10835 isInside( curElem, *aFaceClassifier, aTol )))
10836 theAffectedElems.insert( curElem );
10844 //================================================================================
10846 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10847 \param theElems - group of of elements (edges or faces) to be replicated
10848 \param theNodesNot - group of nodes not to replicate
10849 \param theShape - shape to detect affected elements (element which geometric center
10850 located on or inside shape).
10851 The replicated nodes should be associated to affected elements.
10852 \return TRUE if operation has been completed successfully, FALSE otherwise
10854 //================================================================================
10856 bool SMESH_MeshEditor::DoubleNodesInRegion( const TIDSortedElemSet& theElems,
10857 const TIDSortedElemSet& theNodesNot,
10858 const TopoDS_Shape& theShape )
10860 if ( theShape.IsNull() )
10863 const double aTol = Precision::Confusion();
10864 auto_ptr< BRepClass3d_SolidClassifier> bsc3d;
10865 auto_ptr<_FaceClassifier> aFaceClassifier;
10866 if ( theShape.ShapeType() == TopAbs_SOLID )
10868 bsc3d.reset( new BRepClass3d_SolidClassifier(theShape));;
10869 bsc3d->PerformInfinitePoint(aTol);
10871 else if (theShape.ShapeType() == TopAbs_FACE )
10873 aFaceClassifier.reset( new _FaceClassifier(TopoDS::Face(theShape)));
10876 // iterates on indicated elements and get elements by back references from their nodes
10877 TIDSortedElemSet anAffected;
10878 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10879 for ( ; elemItr != theElems.end(); ++elemItr )
10881 SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
10885 SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
10886 while ( nodeItr->more() )
10888 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10889 if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
10891 SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
10892 while ( backElemItr->more() )
10894 const SMDS_MeshElement* curElem = backElemItr->next();
10895 if ( curElem && theElems.find(curElem) == theElems.end() &&
10897 isInside( curElem, *bsc3d, aTol ) :
10898 isInside( curElem, *aFaceClassifier, aTol )))
10899 anAffected.insert( curElem );
10903 return DoubleNodes( theElems, theNodesNot, anAffected );
10907 * \brief compute an oriented angle between two planes defined by four points.
10908 * The vector (p0,p1) defines the intersection of the 2 planes (p0,p1,g1) and (p0,p1,g2)
10909 * @param p0 base of the rotation axe
10910 * @param p1 extremity of the rotation axe
10911 * @param g1 belongs to the first plane
10912 * @param g2 belongs to the second plane
10914 double SMESH_MeshEditor::OrientedAngle(const gp_Pnt& p0, const gp_Pnt& p1, const gp_Pnt& g1, const gp_Pnt& g2)
10916 // MESSAGE(" p0: " << p0.X() << " " << p0.Y() << " " << p0.Z());
10917 // MESSAGE(" p1: " << p1.X() << " " << p1.Y() << " " << p1.Z());
10918 // MESSAGE(" g1: " << g1.X() << " " << g1.Y() << " " << g1.Z());
10919 // MESSAGE(" g2: " << g2.X() << " " << g2.Y() << " " << g2.Z());
10920 gp_Vec vref(p0, p1);
10923 gp_Vec n1 = vref.Crossed(v1);
10924 gp_Vec n2 = vref.Crossed(v2);
10926 return n2.AngleWithRef(n1, vref);
10928 catch ( Standard_Failure ) {
10930 return Max( v1.Magnitude(), v2.Magnitude() );
10934 * \brief Double nodes on shared faces between groups of volumes and create flat elements on demand.
10935 * The list of groups must contain at least two groups. The groups have to be disjoint: no common element into two different groups.
10936 * The nodes of the internal faces at the boundaries of the groups are doubled. Optionally, the internal faces are replaced by flat elements.
10937 * Triangles are transformed into prisms, and quadrangles into hexahedrons.
10938 * The flat elements are stored in groups of volumes. These groups are named according to the position of the group in the list:
10939 * 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.
10940 * 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.
10941 * All the flat elements are gathered into the group named "joints3D" (or "joints2D" in 2D situation).
10942 * The flat element of the multiple junctions between the simple junction are stored in a group named "jointsMultiples".
10943 * \param theElems - list of groups of volumes, where a group of volume is a set of
10944 * SMDS_MeshElements sorted by Id.
10945 * \param createJointElems - if TRUE, create the elements
10946 * \param onAllBoundaries - if TRUE, the nodes and elements are also created on
10947 * the boundary between \a theDomains and the rest mesh
10948 * \return TRUE if operation has been completed successfully, FALSE otherwise
10950 bool SMESH_MeshEditor::DoubleNodesOnGroupBoundaries( const std::vector<TIDSortedElemSet>& theElems,
10951 bool createJointElems,
10952 bool onAllBoundaries)
10954 MESSAGE("----------------------------------------------");
10955 MESSAGE("SMESH_MeshEditor::doubleNodesOnGroupBoundaries");
10956 MESSAGE("----------------------------------------------");
10958 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
10959 meshDS->BuildDownWardConnectivity(true);
10961 SMDS_UnstructuredGrid *grid = meshDS->getGrid();
10963 // --- build the list of faces shared by 2 domains (group of elements), with their domain and volume indexes
10964 // build the list of cells with only a node or an edge on the border, with their domain and volume indexes
10965 // build the list of nodes shared by 2 or more domains, with their domain indexes
10967 std::map<DownIdType, std::map<int,int>, DownIdCompare> faceDomains; // face --> (id domain --> id volume)
10968 std::map<int,int>celldom; // cell vtkId --> domain
10969 std::map<DownIdType, std::map<int,int>, DownIdCompare> cellDomains; // oldNode --> (id domain --> id cell)
10970 std::map<int, std::map<int,int> > nodeDomains; // oldId --> (domainId --> newId)
10971 faceDomains.clear();
10973 cellDomains.clear();
10974 nodeDomains.clear();
10975 std::map<int,int> emptyMap;
10976 std::set<int> emptySet;
10979 MESSAGE(".. Number of domains :"<<theElems.size());
10981 TIDSortedElemSet theRestDomElems;
10982 const int iRestDom = -1;
10983 const int idom0 = onAllBoundaries ? iRestDom : 0;
10984 const int nbDomains = theElems.size();
10986 // Check if the domains do not share an element
10987 for (int idom = 0; idom < nbDomains-1; idom++)
10989 // MESSAGE("... Check of domain #" << idom);
10990 const TIDSortedElemSet& domain = theElems[idom];
10991 TIDSortedElemSet::const_iterator elemItr = domain.begin();
10992 for (; elemItr != domain.end(); ++elemItr)
10994 const SMDS_MeshElement* anElem = *elemItr;
10995 int idombisdeb = idom + 1 ;
10996 for (int idombis = idombisdeb; idombis < theElems.size(); idombis++) // check if the element belongs to a domain further in the list
10998 const TIDSortedElemSet& domainbis = theElems[idombis];
10999 if ( domainbis.count(anElem) )
11001 MESSAGE(".... Domain #" << idom);
11002 MESSAGE(".... Domain #" << idombis);
11003 throw SALOME_Exception("The domains are not disjoint.");
11010 for (int idom = 0; idom < nbDomains; idom++)
11013 // --- build a map (face to duplicate --> volume to modify)
11014 // with all the faces shared by 2 domains (group of elements)
11015 // and corresponding volume of this domain, for each shared face.
11016 // a volume has a face shared by 2 domains if it has a neighbor which is not in his domain.
11018 MESSAGE("... Neighbors of domain #" << idom);
11019 const TIDSortedElemSet& domain = theElems[idom];
11020 TIDSortedElemSet::const_iterator elemItr = domain.begin();
11021 for (; elemItr != domain.end(); ++elemItr)
11023 const SMDS_MeshElement* anElem = *elemItr;
11026 int vtkId = anElem->getVtkId();
11027 //MESSAGE(" vtkId " << vtkId << " smdsId " << anElem->GetID());
11028 int neighborsVtkIds[NBMAXNEIGHBORS];
11029 int downIds[NBMAXNEIGHBORS];
11030 unsigned char downTypes[NBMAXNEIGHBORS];
11031 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
11032 for (int n = 0; n < nbNeighbors; n++)
11034 int smdsId = meshDS->fromVtkToSmds(neighborsVtkIds[n]);
11035 const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
11036 if (elem && ! domain.count(elem)) // neighbor is in another domain : face is shared
11039 for (int idombis = 0; idombis < theElems.size() && !ok; idombis++) // check if the neighbor belongs to another domain of the list
11041 // MESSAGE("Domain " << idombis);
11042 const TIDSortedElemSet& domainbis = theElems[idombis];
11043 if ( domainbis.count(elem)) ok = true ; // neighbor is in a correct domain : face is kept
11045 if ( ok || onAllBoundaries ) // the characteristics of the face is stored
11047 DownIdType face(downIds[n], downTypes[n]);
11048 if (!faceDomains[face].count(idom))
11050 faceDomains[face][idom] = vtkId; // volume associated to face in this domain
11051 celldom[vtkId] = idom;
11052 //MESSAGE(" cell with a border " << vtkId << " domain " << idom);
11056 theRestDomElems.insert( elem );
11057 faceDomains[face][iRestDom] = neighborsVtkIds[n];
11058 celldom[neighborsVtkIds[n]] = iRestDom;
11066 //MESSAGE("Number of shared faces " << faceDomains.size());
11067 std::map<DownIdType, std::map<int, int>, DownIdCompare>::iterator itface;
11069 // --- explore the shared faces domain by domain,
11070 // explore the nodes of the face and see if they belong to a cell in the domain,
11071 // which has only a node or an edge on the border (not a shared face)
11073 for (int idomain = idom0; idomain < nbDomains; idomain++)
11075 //MESSAGE("Domain " << idomain);
11076 const TIDSortedElemSet& domain = (idomain == iRestDom) ? theRestDomElems : theElems[idomain];
11077 itface = faceDomains.begin();
11078 for (; itface != faceDomains.end(); ++itface)
11080 const std::map<int, int>& domvol = itface->second;
11081 if (!domvol.count(idomain))
11083 DownIdType face = itface->first;
11084 //MESSAGE(" --- face " << face.cellId);
11085 std::set<int> oldNodes;
11087 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11088 std::set<int>::iterator itn = oldNodes.begin();
11089 for (; itn != oldNodes.end(); ++itn)
11092 //MESSAGE(" node " << oldId);
11093 vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
11094 for (int i=0; i<l.ncells; i++)
11096 int vtkId = l.cells[i];
11097 const SMDS_MeshElement* anElem = GetMeshDS()->FindElement(GetMeshDS()->fromVtkToSmds(vtkId));
11098 if (!domain.count(anElem))
11100 int vtkType = grid->GetCellType(vtkId);
11101 int downId = grid->CellIdToDownId(vtkId);
11104 MESSAGE("doubleNodesOnGroupBoundaries: internal algorithm problem");
11105 continue; // not OK at this stage of the algorithm:
11106 //no cells created after BuildDownWardConnectivity
11108 DownIdType aCell(downId, vtkType);
11109 cellDomains[aCell][idomain] = vtkId;
11110 celldom[vtkId] = idomain;
11111 //MESSAGE(" cell " << vtkId << " domain " << idomain);
11117 // --- explore the shared faces domain by domain, to duplicate the nodes in a coherent way
11118 // for each shared face, get the nodes
11119 // for each node, for each domain of the face, create a clone of the node
11121 // --- edges at the intersection of 3 or 4 domains, with the order of domains to build
11122 // junction elements of type prism or hexa. the key is the pair of nodesId (lower first)
11123 // the value is the ordered domain ids. (more than 4 domains not taken into account)
11125 std::map<std::vector<int>, std::vector<int> > edgesMultiDomains; // nodes of edge --> ordered domains
11126 std::map<int, std::vector<int> > mutipleNodes; // nodes multi domains with domain order
11127 std::map<int, std::vector<int> > mutipleNodesToFace; // nodes multi domains with domain order to transform in Face (junction between 3 or more 2D domains)
11129 MESSAGE(".. Duplication of the nodes");
11130 for (int idomain = idom0; idomain < nbDomains; idomain++)
11132 itface = faceDomains.begin();
11133 for (; itface != faceDomains.end(); ++itface)
11135 const std::map<int, int>& domvol = itface->second;
11136 if (!domvol.count(idomain))
11138 DownIdType face = itface->first;
11139 //MESSAGE(" --- face " << face.cellId);
11140 std::set<int> oldNodes;
11142 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11143 std::set<int>::iterator itn = oldNodes.begin();
11144 for (; itn != oldNodes.end(); ++itn)
11147 if (nodeDomains[oldId].empty())
11149 nodeDomains[oldId][idomain] = oldId; // keep the old node in the first domain
11150 //MESSAGE("-+-+-b oldNode " << oldId << " domain " << idomain);
11152 std::map<int, int>::const_iterator itdom = domvol.begin();
11153 for (; itdom != domvol.end(); ++itdom)
11155 int idom = itdom->first;
11156 //MESSAGE(" domain " << idom);
11157 if (!nodeDomains[oldId].count(idom)) // --- node to clone
11159 if (nodeDomains[oldId].size() >= 2) // a multiple node
11161 vector<int> orderedDoms;
11162 //MESSAGE("multiple node " << oldId);
11163 if (mutipleNodes.count(oldId))
11164 orderedDoms = mutipleNodes[oldId];
11167 map<int,int>::iterator it = nodeDomains[oldId].begin();
11168 for (; it != nodeDomains[oldId].end(); ++it)
11169 orderedDoms.push_back(it->first);
11171 orderedDoms.push_back(idom); // TODO order ==> push_front or back
11172 //stringstream txt;
11173 //for (int i=0; i<orderedDoms.size(); i++)
11174 // txt << orderedDoms[i] << " ";
11175 //MESSAGE("orderedDoms " << txt.str());
11176 mutipleNodes[oldId] = orderedDoms;
11178 double *coords = grid->GetPoint(oldId);
11179 SMDS_MeshNode *newNode = meshDS->AddNode(coords[0], coords[1], coords[2]);
11180 copyPosition( meshDS->FindNodeVtk( oldId ), newNode );
11181 int newId = newNode->getVtkId();
11182 nodeDomains[oldId][idom] = newId; // cloned node for other domains
11183 //MESSAGE("-+-+-c oldNode " << oldId << " domain " << idomain << " newNode " << newId << " domain " << idom << " size=" <<nodeDomains[oldId].size());
11190 MESSAGE(".. Creation of elements");
11191 for (int idomain = idom0; idomain < nbDomains; idomain++)
11193 itface = faceDomains.begin();
11194 for (; itface != faceDomains.end(); ++itface)
11196 std::map<int, int> domvol = itface->second;
11197 if (!domvol.count(idomain))
11199 DownIdType face = itface->first;
11200 //MESSAGE(" --- face " << face.cellId);
11201 std::set<int> oldNodes;
11203 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11204 int nbMultipleNodes = 0;
11205 std::set<int>::iterator itn = oldNodes.begin();
11206 for (; itn != oldNodes.end(); ++itn)
11209 if (mutipleNodes.count(oldId))
11212 if (nbMultipleNodes > 1) // check if an edge of the face is shared between 3 or more domains
11214 //MESSAGE("multiple Nodes detected on a shared face");
11215 int downId = itface->first.cellId;
11216 unsigned char cellType = itface->first.cellType;
11217 // --- shared edge or shared face ?
11218 if ((cellType == VTK_LINE) || (cellType == VTK_QUADRATIC_EDGE)) // shared edge (between two faces)
11221 int nbNodes = grid->getDownArray(cellType)->getNodes(downId, nodes);
11222 for (int i=0; i< nbNodes; i=i+nbNodes-1) // i=0 , i=nbNodes-1
11223 if (mutipleNodes.count(nodes[i]))
11224 if (!mutipleNodesToFace.count(nodes[i]))
11225 mutipleNodesToFace[nodes[i]] = mutipleNodes[nodes[i]];
11227 else // shared face (between two volumes)
11229 int nbEdges = grid->getDownArray(cellType)->getNumberOfDownCells(downId);
11230 const int* downEdgeIds = grid->getDownArray(cellType)->getDownCells(downId);
11231 const unsigned char* edgeType = grid->getDownArray(cellType)->getDownTypes(downId);
11232 for (int ie =0; ie < nbEdges; ie++)
11235 int nbNodes = grid->getDownArray(edgeType[ie])->getNodes(downEdgeIds[ie], nodes);
11236 if (mutipleNodes.count(nodes[0]) && mutipleNodes.count(nodes[nbNodes-1]))
11238 vector<int> vn0 = mutipleNodes[nodes[0]];
11239 vector<int> vn1 = mutipleNodes[nodes[nbNodes - 1]];
11241 for (int i0 = 0; i0 < vn0.size(); i0++)
11242 for (int i1 = 0; i1 < vn1.size(); i1++)
11243 if (vn0[i0] == vn1[i1])
11244 doms.push_back(vn0[i0]);
11245 if (doms.size() >2)
11247 //MESSAGE(" detect edgesMultiDomains " << nodes[0] << " " << nodes[nbNodes - 1]);
11248 double *coords = grid->GetPoint(nodes[0]);
11249 gp_Pnt p0(coords[0], coords[1], coords[2]);
11250 coords = grid->GetPoint(nodes[nbNodes - 1]);
11251 gp_Pnt p1(coords[0], coords[1], coords[2]);
11253 int vtkVolIds[1000]; // an edge can belong to a lot of volumes
11254 map<int, SMDS_VtkVolume*> domvol; // domain --> a volume with the edge
11255 map<int, double> angleDom; // oriented angles between planes defined by edge and volume centers
11256 int nbvol = grid->GetParentVolumes(vtkVolIds, downEdgeIds[ie], edgeType[ie]);
11257 for (int id=0; id < doms.size(); id++)
11259 int idom = doms[id];
11260 const TIDSortedElemSet& domain = (idom == iRestDom) ? theRestDomElems : theElems[idom];
11261 for (int ivol=0; ivol<nbvol; ivol++)
11263 int smdsId = meshDS->fromVtkToSmds(vtkVolIds[ivol]);
11264 SMDS_MeshElement* elem = (SMDS_MeshElement*)meshDS->FindElement(smdsId);
11265 if (domain.count(elem))
11267 SMDS_VtkVolume* svol = dynamic_cast<SMDS_VtkVolume*>(elem);
11268 domvol[idom] = svol;
11269 //MESSAGE(" domain " << idom << " volume " << elem->GetID());
11271 vtkIdType npts = 0;
11272 vtkIdType* pts = 0;
11273 grid->GetCellPoints(vtkVolIds[ivol], npts, pts);
11274 SMDS_VtkVolume::gravityCenter(grid, pts, npts, values);
11277 gref.SetXYZ(gp_XYZ(values[0], values[1], values[2]));
11278 angleDom[idom] = 0;
11282 gp_Pnt g(values[0], values[1], values[2]);
11283 angleDom[idom] = OrientedAngle(p0, p1, gref, g); // -pi<angle<+pi
11284 //MESSAGE(" angle=" << angleDom[idom]);
11290 map<double, int> sortedDom; // sort domains by angle
11291 for (map<int, double>::iterator ia = angleDom.begin(); ia != angleDom.end(); ++ia)
11292 sortedDom[ia->second] = ia->first;
11293 vector<int> vnodes;
11295 for (map<double, int>::iterator ib = sortedDom.begin(); ib != sortedDom.end(); ++ib)
11297 vdom.push_back(ib->second);
11298 //MESSAGE(" ordered domain " << ib->second << " angle " << ib->first);
11300 for (int ino = 0; ino < nbNodes; ino++)
11301 vnodes.push_back(nodes[ino]);
11302 edgesMultiDomains[vnodes] = vdom; // nodes vector --> ordered domains
11311 // --- iterate on shared faces (volumes to modify, face to extrude)
11312 // get node id's of the face (id SMDS = id VTK)
11313 // create flat element with old and new nodes if requested
11315 // --- new quad nodes on flat quad elements: oldId --> ((domain1 X domain2) --> newId)
11316 // (domain1 X domain2) = domain1 + MAXINT*domain2
11318 std::map<int, std::map<long,int> > nodeQuadDomains;
11319 std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11321 MESSAGE(".. Creation of elements: simple junction");
11322 if (createJointElems)
11325 string joints2DName = "joints2D";
11326 mapOfJunctionGroups[joints2DName] = this->myMesh->AddGroup(SMDSAbs_Face, joints2DName.c_str(), idg);
11327 SMESHDS_Group *joints2DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints2DName]->GetGroupDS());
11328 string joints3DName = "joints3D";
11329 mapOfJunctionGroups[joints3DName] = this->myMesh->AddGroup(SMDSAbs_Volume, joints3DName.c_str(), idg);
11330 SMESHDS_Group *joints3DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints3DName]->GetGroupDS());
11332 itface = faceDomains.begin();
11333 for (; itface != faceDomains.end(); ++itface)
11335 DownIdType face = itface->first;
11336 std::set<int> oldNodes;
11337 std::set<int>::iterator itn;
11339 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11341 std::map<int, int> domvol = itface->second;
11342 std::map<int, int>::iterator itdom = domvol.begin();
11343 int dom1 = itdom->first;
11344 int vtkVolId = itdom->second;
11346 int dom2 = itdom->first;
11347 SMDS_MeshCell *vol = grid->extrudeVolumeFromFace(vtkVolId, dom1, dom2, oldNodes, nodeDomains,
11349 stringstream grpname;
11352 grpname << dom1 << "_" << dom2;
11354 grpname << dom2 << "_" << dom1;
11355 string namegrp = grpname.str();
11356 if (!mapOfJunctionGroups.count(namegrp))
11357 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(vol->GetType(), namegrp.c_str(), idg);
11358 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11360 sgrp->Add(vol->GetID());
11361 if (vol->GetType() == SMDSAbs_Volume)
11362 joints3DGrp->Add(vol->GetID());
11363 else if (vol->GetType() == SMDSAbs_Face)
11364 joints2DGrp->Add(vol->GetID());
11368 // --- create volumes on multiple domain intersection if requested
11369 // iterate on mutipleNodesToFace
11370 // iterate on edgesMultiDomains
11372 MESSAGE(".. Creation of elements: multiple junction");
11373 if (createJointElems)
11375 // --- iterate on mutipleNodesToFace
11377 std::map<int, std::vector<int> >::iterator itn = mutipleNodesToFace.begin();
11378 for (; itn != mutipleNodesToFace.end(); ++itn)
11380 int node = itn->first;
11381 vector<int> orderDom = itn->second;
11382 vector<vtkIdType> orderedNodes;
11383 for (int idom = 0; idom <orderDom.size(); idom++)
11384 orderedNodes.push_back( nodeDomains[node][orderDom[idom]] );
11385 SMDS_MeshFace* face = this->GetMeshDS()->AddFaceFromVtkIds(orderedNodes);
11387 stringstream grpname;
11389 grpname << 0 << "_" << 0;
11391 string namegrp = grpname.str();
11392 if (!mapOfJunctionGroups.count(namegrp))
11393 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Face, namegrp.c_str(), idg);
11394 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11396 sgrp->Add(face->GetID());
11399 // --- iterate on edgesMultiDomains
11401 std::map<std::vector<int>, std::vector<int> >::iterator ite = edgesMultiDomains.begin();
11402 for (; ite != edgesMultiDomains.end(); ++ite)
11404 vector<int> nodes = ite->first;
11405 vector<int> orderDom = ite->second;
11406 vector<vtkIdType> orderedNodes;
11407 if (nodes.size() == 2)
11409 //MESSAGE(" use edgesMultiDomains " << nodes[0] << " " << nodes[1]);
11410 for (int ino=0; ino < nodes.size(); ino++)
11411 if (orderDom.size() == 3)
11412 for (int idom = 0; idom <orderDom.size(); idom++)
11413 orderedNodes.push_back( nodeDomains[nodes[ino]][orderDom[idom]] );
11415 for (int idom = orderDom.size()-1; idom >=0; idom--)
11416 orderedNodes.push_back( nodeDomains[nodes[ino]][orderDom[idom]] );
11417 SMDS_MeshVolume* vol = this->GetMeshDS()->AddVolumeFromVtkIds(orderedNodes);
11420 string namegrp = "jointsMultiples";
11421 if (!mapOfJunctionGroups.count(namegrp))
11422 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
11423 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11425 sgrp->Add(vol->GetID());
11429 INFOS("Quadratic multiple joints not implemented");
11430 // TODO quadratic nodes
11435 // --- list the explicit faces and edges of the mesh that need to be modified,
11436 // i.e. faces and edges built with one or more duplicated nodes.
11437 // associate these faces or edges to their corresponding domain.
11438 // only the first domain found is kept when a face or edge is shared
11440 std::map<DownIdType, std::map<int,int>, DownIdCompare> faceOrEdgeDom; // cellToModify --> (id domain --> id cell)
11441 std::map<int,int> feDom; // vtk id of cell to modify --> id domain
11442 faceOrEdgeDom.clear();
11445 MESSAGE(".. Modification of elements");
11446 for (int idomain = idom0; idomain < nbDomains; idomain++)
11448 std::map<int, std::map<int, int> >::const_iterator itnod = nodeDomains.begin();
11449 for (; itnod != nodeDomains.end(); ++itnod)
11451 int oldId = itnod->first;
11452 //MESSAGE(" node " << oldId);
11453 vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
11454 for (int i = 0; i < l.ncells; i++)
11456 int vtkId = l.cells[i];
11457 int vtkType = grid->GetCellType(vtkId);
11458 int downId = grid->CellIdToDownId(vtkId);
11460 continue; // new cells: not to be modified
11461 DownIdType aCell(downId, vtkType);
11462 int volParents[1000];
11463 int nbvol = grid->GetParentVolumes(volParents, vtkId);
11464 for (int j = 0; j < nbvol; j++)
11465 if (celldom.count(volParents[j]) && (celldom[volParents[j]] == idomain))
11466 if (!feDom.count(vtkId))
11468 feDom[vtkId] = idomain;
11469 faceOrEdgeDom[aCell] = emptyMap;
11470 faceOrEdgeDom[aCell][idomain] = vtkId; // affect face or edge to the first domain only
11471 //MESSAGE("affect cell " << this->GetMeshDS()->fromVtkToSmds(vtkId) << " domain " << idomain
11472 // << " type " << vtkType << " downId " << downId);
11478 // --- iterate on shared faces (volumes to modify, face to extrude)
11479 // get node id's of the face
11480 // replace old nodes by new nodes in volumes, and update inverse connectivity
11482 std::map<DownIdType, std::map<int,int>, DownIdCompare>* maps[3] = {&faceDomains, &cellDomains, &faceOrEdgeDom};
11483 for (int m=0; m<3; m++)
11485 std::map<DownIdType, std::map<int,int>, DownIdCompare>* amap = maps[m];
11486 itface = (*amap).begin();
11487 for (; itface != (*amap).end(); ++itface)
11489 DownIdType face = itface->first;
11490 std::set<int> oldNodes;
11491 std::set<int>::iterator itn;
11493 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11494 //MESSAGE("examine cell, downId " << face.cellId << " type " << int(face.cellType));
11495 std::map<int, int> localClonedNodeIds;
11497 std::map<int, int> domvol = itface->second;
11498 std::map<int, int>::iterator itdom = domvol.begin();
11499 for (; itdom != domvol.end(); ++itdom)
11501 int idom = itdom->first;
11502 int vtkVolId = itdom->second;
11503 //MESSAGE("modify nodes of cell " << this->GetMeshDS()->fromVtkToSmds(vtkVolId) << " domain " << idom);
11504 localClonedNodeIds.clear();
11505 for (itn = oldNodes.begin(); itn != oldNodes.end(); ++itn)
11508 if (nodeDomains[oldId].count(idom))
11510 localClonedNodeIds[oldId] = nodeDomains[oldId][idom];
11511 //MESSAGE(" node " << oldId << " --> " << localClonedNodeIds[oldId]);
11514 meshDS->ModifyCellNodes(vtkVolId, localClonedNodeIds);
11519 // Remove empty groups (issue 0022812)
11520 std::map<std::string, SMESH_Group*>::iterator name_group = mapOfJunctionGroups.begin();
11521 for ( ; name_group != mapOfJunctionGroups.end(); ++name_group )
11523 if ( name_group->second && name_group->second->GetGroupDS()->IsEmpty() )
11524 myMesh->RemoveGroup( name_group->second->GetGroupDS()->GetID() );
11527 meshDS->CleanDownWardConnectivity(); // Mesh has been modified, downward connectivity is no more usable, free memory
11528 grid->BuildLinks();
11536 * \brief Double nodes on some external faces and create flat elements.
11537 * Flat elements are mainly used by some types of mechanic calculations.
11539 * Each group of the list must be constituted of faces.
11540 * Triangles are transformed in prisms, and quadrangles in hexahedrons.
11541 * @param theElems - list of groups of faces, where a group of faces is a set of
11542 * SMDS_MeshElements sorted by Id.
11543 * @return TRUE if operation has been completed successfully, FALSE otherwise
11545 bool SMESH_MeshEditor::CreateFlatElementsOnFacesGroups(const std::vector<TIDSortedElemSet>& theElems)
11547 MESSAGE("-------------------------------------------------");
11548 MESSAGE("SMESH_MeshEditor::CreateFlatElementsOnFacesGroups");
11549 MESSAGE("-------------------------------------------------");
11551 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11553 // --- For each group of faces
11554 // duplicate the nodes, create a flat element based on the face
11555 // replace the nodes of the faces by their clones
11557 std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> clonedNodes;
11558 std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> intermediateNodes;
11559 clonedNodes.clear();
11560 intermediateNodes.clear();
11561 std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11562 mapOfJunctionGroups.clear();
11564 for (int idom = 0; idom < theElems.size(); idom++)
11566 const TIDSortedElemSet& domain = theElems[idom];
11567 TIDSortedElemSet::const_iterator elemItr = domain.begin();
11568 for (; elemItr != domain.end(); ++elemItr)
11570 SMDS_MeshElement* anElem = (SMDS_MeshElement*) *elemItr;
11571 SMDS_MeshFace* aFace = dynamic_cast<SMDS_MeshFace*> (anElem);
11574 // MESSAGE("aFace=" << aFace->GetID());
11575 bool isQuad = aFace->IsQuadratic();
11576 vector<const SMDS_MeshNode*> ln0, ln1, ln2, ln3, ln4;
11578 // --- clone the nodes, create intermediate nodes for non medium nodes of a quad face
11580 SMDS_ElemIteratorPtr nodeIt = aFace->nodesIterator();
11581 while (nodeIt->more())
11583 const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*> (nodeIt->next());
11584 bool isMedium = isQuad && (aFace->IsMediumNode(node));
11586 ln2.push_back(node);
11588 ln0.push_back(node);
11590 const SMDS_MeshNode* clone = 0;
11591 if (!clonedNodes.count(node))
11593 clone = meshDS->AddNode(node->X(), node->Y(), node->Z());
11594 copyPosition( node, clone );
11595 clonedNodes[node] = clone;
11598 clone = clonedNodes[node];
11601 ln3.push_back(clone);
11603 ln1.push_back(clone);
11605 const SMDS_MeshNode* inter = 0;
11606 if (isQuad && (!isMedium))
11608 if (!intermediateNodes.count(node))
11610 inter = meshDS->AddNode(node->X(), node->Y(), node->Z());
11611 copyPosition( node, inter );
11612 intermediateNodes[node] = inter;
11615 inter = intermediateNodes[node];
11616 ln4.push_back(inter);
11620 // --- extrude the face
11622 vector<const SMDS_MeshNode*> ln;
11623 SMDS_MeshVolume* vol = 0;
11624 vtkIdType aType = aFace->GetVtkType();
11628 vol = meshDS->AddVolume(ln0[2], ln0[1], ln0[0], ln1[2], ln1[1], ln1[0]);
11629 // MESSAGE("vol prism " << vol->GetID());
11630 ln.push_back(ln1[0]);
11631 ln.push_back(ln1[1]);
11632 ln.push_back(ln1[2]);
11635 vol = meshDS->AddVolume(ln0[3], ln0[2], ln0[1], ln0[0], ln1[3], ln1[2], ln1[1], ln1[0]);
11636 // MESSAGE("vol hexa " << vol->GetID());
11637 ln.push_back(ln1[0]);
11638 ln.push_back(ln1[1]);
11639 ln.push_back(ln1[2]);
11640 ln.push_back(ln1[3]);
11642 case VTK_QUADRATIC_TRIANGLE:
11643 vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln0[0], ln0[1], ln0[2], ln3[0], ln3[1], ln3[2],
11644 ln2[0], ln2[1], ln2[2], ln4[0], ln4[1], ln4[2]);
11645 // MESSAGE("vol quad prism " << vol->GetID());
11646 ln.push_back(ln1[0]);
11647 ln.push_back(ln1[1]);
11648 ln.push_back(ln1[2]);
11649 ln.push_back(ln3[0]);
11650 ln.push_back(ln3[1]);
11651 ln.push_back(ln3[2]);
11653 case VTK_QUADRATIC_QUAD:
11654 // vol = meshDS->AddVolume(ln0[0], ln0[1], ln0[2], ln0[3], ln1[0], ln1[1], ln1[2], ln1[3],
11655 // ln2[0], ln2[1], ln2[2], ln2[3], ln3[0], ln3[1], ln3[2], ln3[3],
11656 // ln4[0], ln4[1], ln4[2], ln4[3]);
11657 vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln1[3], ln0[0], ln0[1], ln0[2], ln0[3],
11658 ln3[0], ln3[1], ln3[2], ln3[3], ln2[0], ln2[1], ln2[2], ln2[3],
11659 ln4[0], ln4[1], ln4[2], ln4[3]);
11660 // MESSAGE("vol quad hexa " << vol->GetID());
11661 ln.push_back(ln1[0]);
11662 ln.push_back(ln1[1]);
11663 ln.push_back(ln1[2]);
11664 ln.push_back(ln1[3]);
11665 ln.push_back(ln3[0]);
11666 ln.push_back(ln3[1]);
11667 ln.push_back(ln3[2]);
11668 ln.push_back(ln3[3]);
11678 stringstream grpname;
11682 string namegrp = grpname.str();
11683 if (!mapOfJunctionGroups.count(namegrp))
11684 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
11685 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11687 sgrp->Add(vol->GetID());
11690 // --- modify the face
11692 aFace->ChangeNodes(&ln[0], ln.size());
11699 * \brief identify all the elements around a geom shape, get the faces delimiting the hole
11700 * Build groups of volume to remove, groups of faces to replace on the skin of the object,
11701 * groups of faces to remove inside the object, (idem edges).
11702 * Build ordered list of nodes at the border of each group of faces to replace (to be used to build a geom subshape)
11704 void SMESH_MeshEditor::CreateHoleSkin(double radius,
11705 const TopoDS_Shape& theShape,
11706 SMESH_NodeSearcher* theNodeSearcher,
11707 const char* groupName,
11708 std::vector<double>& nodesCoords,
11709 std::vector<std::vector<int> >& listOfListOfNodes)
11711 MESSAGE("--------------------------------");
11712 MESSAGE("SMESH_MeshEditor::CreateHoleSkin");
11713 MESSAGE("--------------------------------");
11715 // --- zone of volumes to remove is given :
11716 // 1 either by a geom shape (one or more vertices) and a radius,
11717 // 2 either by a group of nodes (representative of the shape)to use with the radius,
11718 // 3 either by a group of nodes where all the elements build on one of this nodes are to remove,
11719 // In the case 2, the group of nodes is an external group of nodes from another mesh,
11720 // In the case 3, the group of nodes is an internal group of the mesh (obtained for instance by a filter),
11721 // defined by it's name.
11723 SMESHDS_GroupBase* groupDS = 0;
11724 SMESH_Mesh::GroupIteratorPtr groupIt = this->myMesh->GetGroups();
11725 while ( groupIt->more() )
11728 SMESH_Group * group = groupIt->next();
11729 if ( !group ) continue;
11730 groupDS = group->GetGroupDS();
11731 if ( !groupDS || groupDS->IsEmpty() ) continue;
11732 std::string grpName = group->GetName();
11733 //MESSAGE("grpName=" << grpName);
11734 if (grpName == groupName)
11740 bool isNodeGroup = false;
11741 bool isNodeCoords = false;
11744 if (groupDS->GetType() != SMDSAbs_Node)
11746 isNodeGroup = true; // a group of nodes exists and it is in this mesh
11749 if (nodesCoords.size() > 0)
11750 isNodeCoords = true; // a list o nodes given by their coordinates
11751 //MESSAGE("---" << isNodeGroup << " " << isNodeCoords);
11753 // --- define groups to build
11755 int idg; // --- group of SMDS volumes
11756 string grpvName = groupName;
11757 grpvName += "_vol";
11758 SMESH_Group *grp = this->myMesh->AddGroup(SMDSAbs_Volume, grpvName.c_str(), idg);
11761 MESSAGE("group not created " << grpvName);
11764 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(grp->GetGroupDS());
11766 int idgs; // --- group of SMDS faces on the skin
11767 string grpsName = groupName;
11768 grpsName += "_skin";
11769 SMESH_Group *grps = this->myMesh->AddGroup(SMDSAbs_Face, grpsName.c_str(), idgs);
11772 MESSAGE("group not created " << grpsName);
11775 SMESHDS_Group *sgrps = dynamic_cast<SMESHDS_Group*>(grps->GetGroupDS());
11777 int idgi; // --- group of SMDS faces internal (several shapes)
11778 string grpiName = groupName;
11779 grpiName += "_internalFaces";
11780 SMESH_Group *grpi = this->myMesh->AddGroup(SMDSAbs_Face, grpiName.c_str(), idgi);
11783 MESSAGE("group not created " << grpiName);
11786 SMESHDS_Group *sgrpi = dynamic_cast<SMESHDS_Group*>(grpi->GetGroupDS());
11788 int idgei; // --- group of SMDS faces internal (several shapes)
11789 string grpeiName = groupName;
11790 grpeiName += "_internalEdges";
11791 SMESH_Group *grpei = this->myMesh->AddGroup(SMDSAbs_Edge, grpeiName.c_str(), idgei);
11794 MESSAGE("group not created " << grpeiName);
11797 SMESHDS_Group *sgrpei = dynamic_cast<SMESHDS_Group*>(grpei->GetGroupDS());
11799 // --- build downward connectivity
11801 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11802 meshDS->BuildDownWardConnectivity(true);
11803 SMDS_UnstructuredGrid* grid = meshDS->getGrid();
11805 // --- set of volumes detected inside
11807 std::set<int> setOfInsideVol;
11808 std::set<int> setOfVolToCheck;
11810 std::vector<gp_Pnt> gpnts;
11813 if (isNodeGroup) // --- a group of nodes is provided : find all the volumes using one or more of this nodes
11815 MESSAGE("group of nodes provided");
11816 SMDS_ElemIteratorPtr elemIt = groupDS->GetElements();
11817 while ( elemIt->more() )
11819 const SMDS_MeshElement* elem = elemIt->next();
11822 const SMDS_MeshNode* node = dynamic_cast<const SMDS_MeshNode*>(elem);
11825 SMDS_MeshElement* vol = 0;
11826 SMDS_ElemIteratorPtr volItr = node->GetInverseElementIterator(SMDSAbs_Volume);
11827 while (volItr->more())
11829 vol = (SMDS_MeshElement*)volItr->next();
11830 setOfInsideVol.insert(vol->getVtkId());
11831 sgrp->Add(vol->GetID());
11835 else if (isNodeCoords)
11837 MESSAGE("list of nodes coordinates provided");
11840 while (i < nodesCoords.size()-2)
11842 double x = nodesCoords[i++];
11843 double y = nodesCoords[i++];
11844 double z = nodesCoords[i++];
11845 gp_Pnt p = gp_Pnt(x, y ,z);
11846 gpnts.push_back(p);
11847 MESSAGE("TopoDS_Vertex " << k << " " << p.X() << " " << p.Y() << " " << p.Z());
11851 else // --- no group, no coordinates : use the vertices of the geom shape provided, and radius
11853 MESSAGE("no group of nodes provided, using vertices from geom shape, and radius");
11854 TopTools_IndexedMapOfShape vertexMap;
11855 TopExp::MapShapes( theShape, TopAbs_VERTEX, vertexMap );
11856 gp_Pnt p = gp_Pnt(0,0,0);
11857 if (vertexMap.Extent() < 1)
11860 for ( int i = 1; i <= vertexMap.Extent(); ++i )
11862 const TopoDS_Vertex& vertex = TopoDS::Vertex( vertexMap( i ));
11863 p = BRep_Tool::Pnt(vertex);
11864 gpnts.push_back(p);
11865 MESSAGE("TopoDS_Vertex " << i << " " << p.X() << " " << p.Y() << " " << p.Z());
11869 if (gpnts.size() > 0)
11872 const SMDS_MeshNode* startNode = theNodeSearcher->FindClosestTo(gpnts[0]);
11874 nodeId = startNode->GetID();
11875 MESSAGE("nodeId " << nodeId);
11877 double radius2 = radius*radius;
11878 MESSAGE("radius2 " << radius2);
11880 // --- volumes on start node
11882 setOfVolToCheck.clear();
11883 SMDS_MeshElement* startVol = 0;
11884 SMDS_ElemIteratorPtr volItr = startNode->GetInverseElementIterator(SMDSAbs_Volume);
11885 while (volItr->more())
11887 startVol = (SMDS_MeshElement*)volItr->next();
11888 setOfVolToCheck.insert(startVol->getVtkId());
11890 if (setOfVolToCheck.empty())
11892 MESSAGE("No volumes found");
11896 // --- starting with central volumes then their neighbors, check if they are inside
11897 // or outside the domain, until no more new neighbor volume is inside.
11898 // Fill the group of inside volumes
11900 std::map<int, double> mapOfNodeDistance2;
11901 mapOfNodeDistance2.clear();
11902 std::set<int> setOfOutsideVol;
11903 while (!setOfVolToCheck.empty())
11905 std::set<int>::iterator it = setOfVolToCheck.begin();
11907 MESSAGE("volume to check, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
11908 bool volInside = false;
11909 vtkIdType npts = 0;
11910 vtkIdType* pts = 0;
11911 grid->GetCellPoints(vtkId, npts, pts);
11912 for (int i=0; i<npts; i++)
11914 double distance2 = 0;
11915 if (mapOfNodeDistance2.count(pts[i]))
11917 distance2 = mapOfNodeDistance2[pts[i]];
11918 MESSAGE("point " << pts[i] << " distance2 " << distance2);
11922 double *coords = grid->GetPoint(pts[i]);
11923 gp_Pnt aPoint = gp_Pnt(coords[0], coords[1], coords[2]);
11925 for (int j=0; j<gpnts.size(); j++)
11927 double d2 = aPoint.SquareDistance(gpnts[j]);
11928 if (d2 < distance2)
11931 if (distance2 < radius2)
11935 mapOfNodeDistance2[pts[i]] = distance2;
11936 MESSAGE(" point " << pts[i] << " distance2 " << distance2 << " coords " << coords[0] << " " << coords[1] << " " << coords[2]);
11938 if (distance2 < radius2)
11940 volInside = true; // one or more nodes inside the domain
11941 sgrp->Add(meshDS->fromVtkToSmds(vtkId));
11947 setOfInsideVol.insert(vtkId);
11948 MESSAGE(" volume inside, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
11949 int neighborsVtkIds[NBMAXNEIGHBORS];
11950 int downIds[NBMAXNEIGHBORS];
11951 unsigned char downTypes[NBMAXNEIGHBORS];
11952 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
11953 for (int n = 0; n < nbNeighbors; n++)
11954 if (!setOfInsideVol.count(neighborsVtkIds[n]) ||setOfOutsideVol.count(neighborsVtkIds[n]))
11955 setOfVolToCheck.insert(neighborsVtkIds[n]);
11959 setOfOutsideVol.insert(vtkId);
11960 MESSAGE(" volume outside, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
11962 setOfVolToCheck.erase(vtkId);
11966 // --- for outside hexahedrons, check if they have more than one neighbor volume inside
11967 // If yes, add the volume to the inside set
11969 bool addedInside = true;
11970 std::set<int> setOfVolToReCheck;
11971 while (addedInside)
11973 MESSAGE(" --------------------------- re check");
11974 addedInside = false;
11975 std::set<int>::iterator itv = setOfInsideVol.begin();
11976 for (; itv != setOfInsideVol.end(); ++itv)
11979 int neighborsVtkIds[NBMAXNEIGHBORS];
11980 int downIds[NBMAXNEIGHBORS];
11981 unsigned char downTypes[NBMAXNEIGHBORS];
11982 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
11983 for (int n = 0; n < nbNeighbors; n++)
11984 if (!setOfInsideVol.count(neighborsVtkIds[n]))
11985 setOfVolToReCheck.insert(neighborsVtkIds[n]);
11987 setOfVolToCheck = setOfVolToReCheck;
11988 setOfVolToReCheck.clear();
11989 while (!setOfVolToCheck.empty())
11991 std::set<int>::iterator it = setOfVolToCheck.begin();
11993 if (grid->GetCellType(vtkId) == VTK_HEXAHEDRON)
11995 MESSAGE("volume to recheck, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
11996 int countInside = 0;
11997 int neighborsVtkIds[NBMAXNEIGHBORS];
11998 int downIds[NBMAXNEIGHBORS];
11999 unsigned char downTypes[NBMAXNEIGHBORS];
12000 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12001 for (int n = 0; n < nbNeighbors; n++)
12002 if (setOfInsideVol.count(neighborsVtkIds[n]))
12004 MESSAGE("countInside " << countInside);
12005 if (countInside > 1)
12007 MESSAGE(" volume inside, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12008 setOfInsideVol.insert(vtkId);
12009 sgrp->Add(meshDS->fromVtkToSmds(vtkId));
12010 addedInside = true;
12013 setOfVolToReCheck.insert(vtkId);
12015 setOfVolToCheck.erase(vtkId);
12019 // --- map of Downward faces at the boundary, inside the global volume
12020 // map of Downward faces on the skin of the global volume (equivalent to SMDS faces on the skin)
12021 // fill group of SMDS faces inside the volume (when several volume shapes)
12022 // fill group of SMDS faces on the skin of the global volume (if skin)
12024 std::map<DownIdType, int, DownIdCompare> boundaryFaces; // boundary faces inside the volume --> corresponding cell
12025 std::map<DownIdType, int, DownIdCompare> skinFaces; // faces on the skin of the global volume --> corresponding cell
12026 std::set<int>::iterator it = setOfInsideVol.begin();
12027 for (; it != setOfInsideVol.end(); ++it)
12030 //MESSAGE(" vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12031 int neighborsVtkIds[NBMAXNEIGHBORS];
12032 int downIds[NBMAXNEIGHBORS];
12033 unsigned char downTypes[NBMAXNEIGHBORS];
12034 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId, true);
12035 for (int n = 0; n < nbNeighbors; n++)
12037 int neighborDim = SMDS_Downward::getCellDimension(grid->GetCellType(neighborsVtkIds[n]));
12038 if (neighborDim == 3)
12040 if (! setOfInsideVol.count(neighborsVtkIds[n])) // neighbor volume is not inside : face is boundary
12042 DownIdType face(downIds[n], downTypes[n]);
12043 boundaryFaces[face] = vtkId;
12045 // if the face between to volumes is in the mesh, get it (internal face between shapes)
12046 int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12047 if (vtkFaceId >= 0)
12049 sgrpi->Add(meshDS->fromVtkToSmds(vtkFaceId));
12050 // find also the smds edges on this face
12051 int nbEdges = grid->getDownArray(downTypes[n])->getNumberOfDownCells(downIds[n]);
12052 const int* dEdges = grid->getDownArray(downTypes[n])->getDownCells(downIds[n]);
12053 const unsigned char* dTypes = grid->getDownArray(downTypes[n])->getDownTypes(downIds[n]);
12054 for (int i = 0; i < nbEdges; i++)
12056 int vtkEdgeId = grid->getDownArray(dTypes[i])->getVtkCellId(dEdges[i]);
12057 if (vtkEdgeId >= 0)
12058 sgrpei->Add(meshDS->fromVtkToSmds(vtkEdgeId));
12062 else if (neighborDim == 2) // skin of the volume
12064 DownIdType face(downIds[n], downTypes[n]);
12065 skinFaces[face] = vtkId;
12066 int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12067 if (vtkFaceId >= 0)
12068 sgrps->Add(meshDS->fromVtkToSmds(vtkFaceId));
12073 // --- identify the edges constituting the wire of each subshape on the skin
12074 // define polylines with the nodes of edges, equivalent to wires
12075 // project polylines on subshapes, and partition, to get geom faces
12077 std::map<int, std::set<int> > shapeIdToVtkIdSet; // shapeId --> set of vtkId on skin
12078 std::set<int> emptySet;
12080 std::set<int> shapeIds;
12082 SMDS_ElemIteratorPtr itelem = sgrps->GetElements();
12083 while (itelem->more())
12085 const SMDS_MeshElement *elem = itelem->next();
12086 int shapeId = elem->getshapeId();
12087 int vtkId = elem->getVtkId();
12088 if (!shapeIdToVtkIdSet.count(shapeId))
12090 shapeIdToVtkIdSet[shapeId] = emptySet;
12091 shapeIds.insert(shapeId);
12093 shapeIdToVtkIdSet[shapeId].insert(vtkId);
12096 std::map<int, std::set<DownIdType, DownIdCompare> > shapeIdToEdges; // shapeId --> set of downward edges
12097 std::set<DownIdType, DownIdCompare> emptyEdges;
12098 emptyEdges.clear();
12100 std::map<int, std::set<int> >::iterator itShape = shapeIdToVtkIdSet.begin();
12101 for (; itShape != shapeIdToVtkIdSet.end(); ++itShape)
12103 int shapeId = itShape->first;
12104 MESSAGE(" --- Shape ID --- "<< shapeId);
12105 shapeIdToEdges[shapeId] = emptyEdges;
12107 std::vector<int> nodesEdges;
12109 std::set<int>::iterator its = itShape->second.begin();
12110 for (; its != itShape->second.end(); ++its)
12113 MESSAGE(" " << vtkId);
12114 int neighborsVtkIds[NBMAXNEIGHBORS];
12115 int downIds[NBMAXNEIGHBORS];
12116 unsigned char downTypes[NBMAXNEIGHBORS];
12117 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12118 for (int n = 0; n < nbNeighbors; n++)
12120 if (neighborsVtkIds[n]<0) // only smds faces are considered as neighbors here
12122 int smdsId = meshDS->fromVtkToSmds(neighborsVtkIds[n]);
12123 const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
12124 if ( shapeIds.count(elem->getshapeId()) && !sgrps->Contains(elem)) // edge : neighbor in the set of shape, not in the group
12126 DownIdType edge(downIds[n], downTypes[n]);
12127 if (!shapeIdToEdges[shapeId].count(edge))
12129 shapeIdToEdges[shapeId].insert(edge);
12131 int nbNodes = grid->getDownArray(downTypes[n])->getNodes(downIds[n],vtkNodeId);
12132 nodesEdges.push_back(vtkNodeId[0]);
12133 nodesEdges.push_back(vtkNodeId[nbNodes-1]);
12134 MESSAGE(" --- nodes " << vtkNodeId[0]+1 << " " << vtkNodeId[nbNodes-1]+1);
12140 std::list<int> order;
12142 if (nodesEdges.size() > 0)
12144 order.push_back(nodesEdges[0]); MESSAGE(" --- back " << order.back()+1); // SMDS id = VTK id + 1;
12145 nodesEdges[0] = -1;
12146 order.push_back(nodesEdges[1]); MESSAGE(" --- back " << order.back()+1);
12147 nodesEdges[1] = -1; // do not reuse this edge
12151 int nodeTofind = order.back(); // try first to push back
12153 for (i = 0; i<nodesEdges.size(); i++)
12154 if (nodesEdges[i] == nodeTofind)
12156 if (i == nodesEdges.size())
12157 found = false; // no follower found on back
12160 if (i%2) // odd ==> use the previous one
12161 if (nodesEdges[i-1] < 0)
12165 order.push_back(nodesEdges[i-1]); MESSAGE(" --- back " << order.back()+1);
12166 nodesEdges[i-1] = -1;
12168 else // even ==> use the next one
12169 if (nodesEdges[i+1] < 0)
12173 order.push_back(nodesEdges[i+1]); MESSAGE(" --- back " << order.back()+1);
12174 nodesEdges[i+1] = -1;
12179 // try to push front
12181 nodeTofind = order.front(); // try to push front
12182 for (i = 0; i<nodesEdges.size(); i++)
12183 if (nodesEdges[i] == nodeTofind)
12185 if (i == nodesEdges.size())
12187 found = false; // no predecessor found on front
12190 if (i%2) // odd ==> use the previous one
12191 if (nodesEdges[i-1] < 0)
12195 order.push_front(nodesEdges[i-1]); MESSAGE(" --- front " << order.front()+1);
12196 nodesEdges[i-1] = -1;
12198 else // even ==> use the next one
12199 if (nodesEdges[i+1] < 0)
12203 order.push_front(nodesEdges[i+1]); MESSAGE(" --- front " << order.front()+1);
12204 nodesEdges[i+1] = -1;
12210 std::vector<int> nodes;
12211 nodes.push_back(shapeId);
12212 std::list<int>::iterator itl = order.begin();
12213 for (; itl != order.end(); itl++)
12215 nodes.push_back((*itl) + 1); // SMDS id = VTK id + 1;
12216 MESSAGE(" ordered node " << nodes[nodes.size()-1]);
12218 listOfListOfNodes.push_back(nodes);
12221 // partition geom faces with blocFissure
12222 // mesh blocFissure and geom faces of the skin (external wires given, triangle algo to choose)
12223 // mesh volume around blocFissure (skin triangles and quadrangle given, tetra algo to choose)
12229 //================================================================================
12231 * \brief Generates skin mesh (containing 2D cells) from 3D mesh
12232 * The created 2D mesh elements based on nodes of free faces of boundary volumes
12233 * \return TRUE if operation has been completed successfully, FALSE otherwise
12235 //================================================================================
12237 bool SMESH_MeshEditor::Make2DMeshFrom3D()
12239 // iterates on volume elements and detect all free faces on them
12240 SMESHDS_Mesh* aMesh = GetMeshDS();
12243 //bool res = false;
12244 int nbFree = 0, nbExisted = 0, nbCreated = 0;
12245 SMDS_VolumeIteratorPtr vIt = aMesh->volumesIterator();
12248 const SMDS_MeshVolume* volume = vIt->next();
12249 SMDS_VolumeTool vTool( volume, /*ignoreCentralNodes=*/false );
12250 vTool.SetExternalNormal();
12251 //const bool isPoly = volume->IsPoly();
12252 const int iQuad = volume->IsQuadratic();
12253 for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12255 if (!vTool.IsFreeFace(iface))
12258 vector<const SMDS_MeshNode *> nodes;
12259 int nbFaceNodes = vTool.NbFaceNodes(iface);
12260 const SMDS_MeshNode** faceNodes = vTool.GetFaceNodes(iface);
12262 for ( ; inode < nbFaceNodes; inode += iQuad+1)
12263 nodes.push_back(faceNodes[inode]);
12264 if (iQuad) { // add medium nodes
12265 for ( inode = 1; inode < nbFaceNodes; inode += 2)
12266 nodes.push_back(faceNodes[inode]);
12267 if ( nbFaceNodes == 9 ) // bi-quadratic quad
12268 nodes.push_back(faceNodes[8]);
12270 // add new face based on volume nodes
12271 if (aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false) ) {
12273 continue; // face already exsist
12275 AddElement(nodes, SMDSAbs_Face, ( !iQuad && nbFaceNodes/(iQuad+1) > 4 ));
12279 return ( nbFree==(nbExisted+nbCreated) );
12284 inline const SMDS_MeshNode* getNodeWithSameID(SMESHDS_Mesh* mesh, const SMDS_MeshNode* node)
12286 if ( const SMDS_MeshNode* n = mesh->FindNode( node->GetID() ))
12288 return mesh->AddNodeWithID( node->X(),node->Y(),node->Z(), node->GetID() );
12291 //================================================================================
12293 * \brief Creates missing boundary elements
12294 * \param elements - elements whose boundary is to be checked
12295 * \param dimension - defines type of boundary elements to create
12296 * \param group - a group to store created boundary elements in
12297 * \param targetMesh - a mesh to store created boundary elements in
12298 * \param toCopyElements - if true, the checked elements will be copied into the targetMesh
12299 * \param toCopyExistingBoundary - if true, not only new but also pre-existing
12300 * boundary elements will be copied into the targetMesh
12301 * \param toAddExistingBondary - if true, not only new but also pre-existing
12302 * boundary elements will be added into the new group
12303 * \param aroundElements - if true, elements will be created on boundary of given
12304 * elements else, on boundary of the whole mesh.
12305 * \return nb of added boundary elements
12307 //================================================================================
12309 int SMESH_MeshEditor::MakeBoundaryMesh(const TIDSortedElemSet& elements,
12310 Bnd_Dimension dimension,
12311 SMESH_Group* group/*=0*/,
12312 SMESH_Mesh* targetMesh/*=0*/,
12313 bool toCopyElements/*=false*/,
12314 bool toCopyExistingBoundary/*=false*/,
12315 bool toAddExistingBondary/*= false*/,
12316 bool aroundElements/*= false*/)
12318 SMDSAbs_ElementType missType = (dimension == BND_2DFROM3D) ? SMDSAbs_Face : SMDSAbs_Edge;
12319 SMDSAbs_ElementType elemType = (dimension == BND_1DFROM2D) ? SMDSAbs_Face : SMDSAbs_Volume;
12320 // hope that all elements are of the same type, do not check them all
12321 if ( !elements.empty() && (*elements.begin())->GetType() != elemType )
12322 throw SALOME_Exception(LOCALIZED("wrong element type"));
12325 toCopyElements = toCopyExistingBoundary = false;
12327 SMESH_MeshEditor tgtEditor( targetMesh ? targetMesh : myMesh );
12328 SMESHDS_Mesh* aMesh = GetMeshDS(), *tgtMeshDS = tgtEditor.GetMeshDS();
12329 int nbAddedBnd = 0;
12331 // editor adding present bnd elements and optionally holding elements to add to the group
12332 SMESH_MeshEditor* presentEditor;
12333 SMESH_MeshEditor tgtEditor2( tgtEditor.GetMesh() );
12334 presentEditor = toAddExistingBondary ? &tgtEditor : &tgtEditor2;
12336 SMESH_MesherHelper helper( *myMesh );
12337 const TopAbs_ShapeEnum missShapeType = ( missType==SMDSAbs_Face ? TopAbs_FACE : TopAbs_EDGE );
12338 SMDS_VolumeTool vTool;
12339 TIDSortedElemSet avoidSet;
12340 const TIDSortedElemSet emptySet, *elemSet = aroundElements ? &elements : &emptySet;
12343 typedef vector<const SMDS_MeshNode*> TConnectivity;
12345 SMDS_ElemIteratorPtr eIt;
12346 if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
12347 else eIt = elemSetIterator( elements );
12349 while (eIt->more())
12351 const SMDS_MeshElement* elem = eIt->next();
12352 const int iQuad = elem->IsQuadratic();
12354 // ------------------------------------------------------------------------------------
12355 // 1. For an elem, get present bnd elements and connectivities of missing bnd elements
12356 // ------------------------------------------------------------------------------------
12357 vector<const SMDS_MeshElement*> presentBndElems;
12358 vector<TConnectivity> missingBndElems;
12359 TConnectivity nodes, elemNodes;
12360 if ( vTool.Set(elem, /*ignoreCentralNodes=*/true) ) // elem is a volume --------------
12362 vTool.SetExternalNormal();
12363 const SMDS_MeshElement* otherVol = 0;
12364 for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12366 if ( !vTool.IsFreeFace(iface, &otherVol) &&
12367 ( !aroundElements || elements.count( otherVol )))
12369 const SMDS_MeshNode** nn = vTool.GetFaceNodes(iface);
12370 const int nbFaceNodes = vTool.NbFaceNodes (iface);
12371 if ( missType == SMDSAbs_Edge ) // boundary edges
12373 nodes.resize( 2+iQuad );
12374 for ( int i = 0; i < nbFaceNodes; i += 1+iQuad)
12376 for ( int j = 0; j < nodes.size(); ++j )
12378 if ( const SMDS_MeshElement* edge =
12379 aMesh->FindElement(nodes,SMDSAbs_Edge,/*noMedium=*/false))
12380 presentBndElems.push_back( edge );
12382 missingBndElems.push_back( nodes );
12385 else // boundary face
12388 for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12389 nodes.push_back( nn[inode] ); // add corner nodes
12391 for ( inode = 1; inode < nbFaceNodes; inode += 2)
12392 nodes.push_back( nn[inode] ); // add medium nodes
12393 int iCenter = vTool.GetCenterNodeIndex(iface); // for HEX27
12395 nodes.push_back( vTool.GetNodes()[ iCenter ] );
12397 if (const SMDS_MeshElement * f = aMesh->FindElement( nodes,
12398 SMDSAbs_Face, /*noMedium=*/false ))
12399 presentBndElems.push_back( f );
12401 missingBndElems.push_back( nodes );
12403 if ( targetMesh != myMesh )
12405 // add 1D elements on face boundary to be added to a new mesh
12406 const SMDS_MeshElement* edge;
12407 for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12410 edge = aMesh->FindEdge( nn[inode], nn[inode+1], nn[inode+2]);
12412 edge = aMesh->FindEdge( nn[inode], nn[inode+1]);
12413 if ( edge && avoidSet.insert( edge ).second )
12414 presentBndElems.push_back( edge );
12420 else if ( elem->GetType() == SMDSAbs_Face ) // elem is a face ------------------------
12422 avoidSet.clear(), avoidSet.insert( elem );
12423 elemNodes.assign( SMDS_MeshElement::iterator( elem->interlacedNodesElemIterator() ),
12424 SMDS_MeshElement::iterator() );
12425 elemNodes.push_back( elemNodes[0] );
12426 nodes.resize( 2 + iQuad );
12427 const int nbLinks = elem->NbCornerNodes();
12428 for ( int i = 0, iN = 0; i < nbLinks; i++, iN += 1+iQuad )
12430 nodes[0] = elemNodes[iN];
12431 nodes[1] = elemNodes[iN+1+iQuad];
12432 if ( SMESH_MeshAlgos::FindFaceInSet( nodes[0], nodes[1], *elemSet, avoidSet))
12433 continue; // not free link
12435 if ( iQuad ) nodes[2] = elemNodes[iN+1];
12436 if ( const SMDS_MeshElement* edge =
12437 aMesh->FindElement(nodes,SMDSAbs_Edge,/*noMedium=*/false))
12438 presentBndElems.push_back( edge );
12440 missingBndElems.push_back( nodes );
12444 // ---------------------------------
12445 // 2. Add missing boundary elements
12446 // ---------------------------------
12447 if ( targetMesh != myMesh )
12448 // instead of making a map of nodes in this mesh and targetMesh,
12449 // we create nodes with same IDs.
12450 for ( int i = 0; i < missingBndElems.size(); ++i )
12452 TConnectivity& srcNodes = missingBndElems[i];
12453 TConnectivity nodes( srcNodes.size() );
12454 for ( inode = 0; inode < nodes.size(); ++inode )
12455 nodes[inode] = getNodeWithSameID( tgtMeshDS, srcNodes[inode] );
12456 if ( aroundElements && tgtEditor.GetMeshDS()->FindElement( nodes,
12458 /*noMedium=*/false))
12460 tgtEditor.AddElement(nodes, missType, !iQuad && nodes.size()/(iQuad+1)>4);
12464 for ( int i = 0; i < missingBndElems.size(); ++i )
12466 TConnectivity& nodes = missingBndElems[i];
12467 if ( aroundElements && tgtEditor.GetMeshDS()->FindElement( nodes,
12469 /*noMedium=*/false))
12471 SMDS_MeshElement* elem =
12472 tgtEditor.AddElement(nodes, missType, !iQuad && nodes.size()/(iQuad+1)>4);
12475 // try to set a new element to a shape
12476 if ( myMesh->HasShapeToMesh() )
12479 set< pair<TopAbs_ShapeEnum, int > > mediumShapes;
12480 const int nbN = nodes.size() / (iQuad+1 );
12481 for ( inode = 0; inode < nbN && ok; ++inode )
12483 pair<int, TopAbs_ShapeEnum> i_stype =
12484 helper.GetMediumPos( nodes[inode], nodes[(inode+1)%nbN]);
12485 if (( ok = ( i_stype.first > 0 && i_stype.second >= TopAbs_FACE )))
12486 mediumShapes.insert( make_pair ( i_stype.second, i_stype.first ));
12488 if ( ok && mediumShapes.size() > 1 )
12490 set< pair<TopAbs_ShapeEnum, int > >::iterator stype_i = mediumShapes.begin();
12491 pair<TopAbs_ShapeEnum, int> stype_i_0 = *stype_i;
12492 for ( ++stype_i; stype_i != mediumShapes.end() && ok; ++stype_i )
12494 if (( ok = ( stype_i->first != stype_i_0.first )))
12495 ok = helper.IsSubShape( aMesh->IndexToShape( stype_i->second ),
12496 aMesh->IndexToShape( stype_i_0.second ));
12499 if ( ok && mediumShapes.begin()->first == missShapeType )
12500 aMesh->SetMeshElementOnShape( elem, mediumShapes.begin()->second );
12504 // ----------------------------------
12505 // 3. Copy present boundary elements
12506 // ----------------------------------
12507 if ( toCopyExistingBoundary )
12508 for ( int i = 0 ; i < presentBndElems.size(); ++i )
12510 const SMDS_MeshElement* e = presentBndElems[i];
12511 TConnectivity nodes( e->NbNodes() );
12512 for ( inode = 0; inode < nodes.size(); ++inode )
12513 nodes[inode] = getNodeWithSameID( tgtMeshDS, e->GetNode(inode) );
12514 presentEditor->AddElement(nodes, e->GetType(), e->IsPoly());
12516 else // store present elements to add them to a group
12517 for ( int i = 0 ; i < presentBndElems.size(); ++i )
12519 presentEditor->myLastCreatedElems.Append(presentBndElems[i]);
12522 } // loop on given elements
12524 // ---------------------------------------------
12525 // 4. Fill group with boundary elements
12526 // ---------------------------------------------
12529 if ( SMESHDS_Group* g = dynamic_cast<SMESHDS_Group*>( group->GetGroupDS() ))
12530 for ( int i = 0; i < tgtEditor.myLastCreatedElems.Size(); ++i )
12531 g->SMDSGroup().Add( tgtEditor.myLastCreatedElems( i+1 ));
12533 tgtEditor.myLastCreatedElems.Clear();
12534 tgtEditor2.myLastCreatedElems.Clear();
12536 // -----------------------
12537 // 5. Copy given elements
12538 // -----------------------
12539 if ( toCopyElements && targetMesh != myMesh )
12541 if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
12542 else eIt = elemSetIterator( elements );
12543 while (eIt->more())
12545 const SMDS_MeshElement* elem = eIt->next();
12546 TConnectivity nodes( elem->NbNodes() );
12547 for ( inode = 0; inode < nodes.size(); ++inode )
12548 nodes[inode] = getNodeWithSameID( tgtMeshDS, elem->GetNode(inode) );
12549 tgtEditor.AddElement(nodes, elemType, elem->IsPoly());
12551 tgtEditor.myLastCreatedElems.Clear();
12557 //================================================================================
12559 * \brief Copy node position and set \a to node on the same geometry
12561 //================================================================================
12563 void SMESH_MeshEditor::copyPosition( const SMDS_MeshNode* from,
12564 const SMDS_MeshNode* to )
12566 if ( !from || !to ) return;
12568 SMDS_PositionPtr pos = from->GetPosition();
12569 if ( !pos || from->getshapeId() < 1 ) return;
12571 switch ( pos->GetTypeOfPosition() )
12573 case SMDS_TOP_3DSPACE: break;
12575 case SMDS_TOP_FACE:
12577 const SMDS_FacePosition* fPos = static_cast< const SMDS_FacePosition* >( pos );
12578 GetMeshDS()->SetNodeOnFace( to, from->getshapeId(),
12579 fPos->GetUParameter(), fPos->GetVParameter() );
12582 case SMDS_TOP_EDGE:
12584 // WARNING: it is dangerous to set equal nodes on one EDGE!!!!!!!!
12585 const SMDS_EdgePosition* ePos = static_cast< const SMDS_EdgePosition* >( pos );
12586 GetMeshDS()->SetNodeOnEdge( to, from->getshapeId(), ePos->GetUParameter() );
12589 case SMDS_TOP_VERTEX:
12591 GetMeshDS()->SetNodeOnVertex( to, from->getshapeId() );
12594 case SMDS_TOP_UNSPEC: