1 // Copyright (C) 2007-2016 CEA/DEN, EDF R&D, OPEN CASCADE
3 // Copyright (C) 2003-2007 OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN,
4 // CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS
6 // This library is free software; you can redistribute it and/or
7 // modify it under the terms of the GNU Lesser General Public
8 // License as published by the Free Software Foundation; either
9 // version 2.1 of the License, or (at your option) any later version.
11 // This library is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 // Lesser General Public License for more details.
16 // You should have received a copy of the GNU Lesser General Public
17 // License along with this library; if not, write to the Free Software
18 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
23 // File : SMESH_MeshEditor.cxx
24 // Created : Mon Apr 12 16:10:22 2004
25 // Author : Edward AGAPOV (eap)
27 #include "SMESH_MeshEditor.hxx"
29 #include "SMDS_FaceOfNodes.hxx"
30 #include "SMDS_VolumeTool.hxx"
31 #include "SMDS_EdgePosition.hxx"
32 #include "SMDS_FacePosition.hxx"
33 #include "SMDS_SpacePosition.hxx"
34 #include "SMDS_MeshGroup.hxx"
35 #include "SMDS_LinearEdge.hxx"
36 #include "SMDS_Downward.hxx"
37 #include "SMDS_SetIterator.hxx"
39 #include "SMESHDS_Group.hxx"
40 #include "SMESHDS_Mesh.hxx"
42 #include "SMESH_Algo.hxx"
43 #include "SMESH_ControlsDef.hxx"
44 #include "SMESH_Group.hxx"
45 #include "SMESH_MeshAlgos.hxx"
46 #include "SMESH_MesherHelper.hxx"
47 #include "SMESH_OctreeNode.hxx"
48 #include "SMESH_subMesh.hxx"
50 #include <Basics_OCCTVersion.hxx>
52 #include "utilities.h"
55 #include <BRepAdaptor_Surface.hxx>
56 #include <BRepBuilderAPI_MakeEdge.hxx>
57 #include <BRepClass3d_SolidClassifier.hxx>
58 #include <BRep_Tool.hxx>
60 #include <Extrema_GenExtPS.hxx>
61 #include <Extrema_POnCurv.hxx>
62 #include <Extrema_POnSurf.hxx>
63 #include <Geom2d_Curve.hxx>
64 #include <GeomAdaptor_Surface.hxx>
65 #include <Geom_Curve.hxx>
66 #include <Geom_Surface.hxx>
67 #include <Precision.hxx>
68 #include <TColStd_ListOfInteger.hxx>
69 #include <TopAbs_State.hxx>
71 #include <TopExp_Explorer.hxx>
72 #include <TopTools_ListIteratorOfListOfShape.hxx>
73 #include <TopTools_ListOfShape.hxx>
74 #include <TopTools_SequenceOfShape.hxx>
76 #include <TopoDS_Face.hxx>
77 #include <TopoDS_Solid.hxx>
83 #include <gp_Trsf.hxx>
97 #include <boost/tuple/tuple.hpp>
99 #include <Standard_Failure.hxx>
100 #include <Standard_ErrorHandler.hxx>
102 #define cast2Node(elem) static_cast<const SMDS_MeshNode*>( elem )
105 using namespace SMESH::Controls;
109 template < class ELEM_SET >
110 SMDS_ElemIteratorPtr elemSetIterator( const ELEM_SET& elements )
112 typedef SMDS_SetIterator
113 < SMDS_pElement, typename ELEM_SET::const_iterator> TSetIterator;
114 return SMDS_ElemIteratorPtr( new TSetIterator( elements.begin(), elements.end() ));
118 //=======================================================================
119 //function : SMESH_MeshEditor
121 //=======================================================================
123 SMESH_MeshEditor::SMESH_MeshEditor( SMESH_Mesh* theMesh )
124 :myMesh( theMesh ) // theMesh may be NULL
128 //================================================================================
130 * \brief Clears myLastCreatedNodes and myLastCreatedElems
132 //================================================================================
134 void SMESH_MeshEditor::ClearLastCreated()
136 myLastCreatedNodes.Clear();
137 myLastCreatedElems.Clear();
140 //================================================================================
142 * \brief Initializes members by an existing element
143 * \param [in] elem - the source element
144 * \param [in] basicOnly - if true, does not set additional data of Ball and Polyhedron
146 //================================================================================
148 SMESH_MeshEditor::ElemFeatures&
149 SMESH_MeshEditor::ElemFeatures::Init( const SMDS_MeshElement* elem, bool basicOnly )
153 myType = elem->GetType();
154 if ( myType == SMDSAbs_Face || myType == SMDSAbs_Volume )
156 myIsPoly = elem->IsPoly();
159 myIsQuad = elem->IsQuadratic();
160 if ( myType == SMDSAbs_Volume && !basicOnly )
162 vector<int > quant = static_cast<const SMDS_VtkVolume* >( elem )->GetQuantities();
163 myPolyhedQuantities.swap( quant );
167 else if ( myType == SMDSAbs_Ball && !basicOnly )
169 myBallDiameter = static_cast<const SMDS_BallElement*>(elem)->GetDiameter();
175 //=======================================================================
179 //=======================================================================
182 SMESH_MeshEditor::AddElement(const vector<const SMDS_MeshNode*> & node,
183 const ElemFeatures& features)
185 SMDS_MeshElement* e = 0;
186 int nbnode = node.size();
187 SMESHDS_Mesh* mesh = GetMeshDS();
188 const int ID = features.myID;
190 switch ( features.myType ) {
192 if ( !features.myIsPoly ) {
194 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], ID);
195 else e = mesh->AddFace (node[0], node[1], node[2] );
197 else if (nbnode == 4) {
198 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3], ID);
199 else e = mesh->AddFace (node[0], node[1], node[2], node[3] );
201 else if (nbnode == 6) {
202 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
203 node[4], node[5], ID);
204 else e = mesh->AddFace (node[0], node[1], node[2], node[3],
207 else if (nbnode == 7) {
208 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
209 node[4], node[5], node[6], ID);
210 else e = mesh->AddFace (node[0], node[1], node[2], node[3],
211 node[4], node[5], node[6] );
213 else if (nbnode == 8) {
214 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
215 node[4], node[5], node[6], node[7], ID);
216 else e = mesh->AddFace (node[0], node[1], node[2], node[3],
217 node[4], node[5], node[6], node[7] );
219 else if (nbnode == 9) {
220 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
221 node[4], node[5], node[6], node[7], node[8], ID);
222 else e = mesh->AddFace (node[0], node[1], node[2], node[3],
223 node[4], node[5], node[6], node[7], node[8] );
226 else if ( !features.myIsQuad )
228 if ( ID >= 1 ) e = mesh->AddPolygonalFaceWithID(node, ID);
229 else e = mesh->AddPolygonalFace (node );
231 else if ( nbnode % 2 == 0 ) // just a protection
233 if ( ID >= 1 ) e = mesh->AddQuadPolygonalFaceWithID(node, ID);
234 else e = mesh->AddQuadPolygonalFace (node );
239 if ( !features.myIsPoly ) {
241 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3], ID);
242 else e = mesh->AddVolume (node[0], node[1], node[2], node[3] );
244 else if (nbnode == 5) {
245 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
247 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
250 else if (nbnode == 6) {
251 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
252 node[4], node[5], ID);
253 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
256 else if (nbnode == 8) {
257 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
258 node[4], node[5], node[6], node[7], ID);
259 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
260 node[4], node[5], node[6], node[7] );
262 else if (nbnode == 10) {
263 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
264 node[4], node[5], node[6], node[7],
265 node[8], node[9], ID);
266 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
267 node[4], node[5], node[6], node[7],
270 else if (nbnode == 12) {
271 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
272 node[4], node[5], node[6], node[7],
273 node[8], node[9], node[10], node[11], ID);
274 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
275 node[4], node[5], node[6], node[7],
276 node[8], node[9], node[10], node[11] );
278 else if (nbnode == 13) {
279 if ( ID >= 1 ) e = mesh->AddVolumeWithID(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],
283 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
284 node[4], node[5], node[6], node[7],
285 node[8], node[9], node[10],node[11],
288 else if (nbnode == 15) {
289 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
290 node[4], node[5], node[6], node[7],
291 node[8], node[9], node[10],node[11],
292 node[12],node[13],node[14],ID);
293 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
294 node[4], node[5], node[6], node[7],
295 node[8], node[9], node[10],node[11],
296 node[12],node[13],node[14] );
298 else if (nbnode == 20) {
299 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
300 node[4], node[5], node[6], node[7],
301 node[8], node[9], node[10],node[11],
302 node[12],node[13],node[14],node[15],
303 node[16],node[17],node[18],node[19],ID);
304 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
305 node[4], node[5], node[6], node[7],
306 node[8], node[9], node[10],node[11],
307 node[12],node[13],node[14],node[15],
308 node[16],node[17],node[18],node[19] );
310 else if (nbnode == 27) {
311 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
312 node[4], node[5], node[6], node[7],
313 node[8], node[9], node[10],node[11],
314 node[12],node[13],node[14],node[15],
315 node[16],node[17],node[18],node[19],
316 node[20],node[21],node[22],node[23],
317 node[24],node[25],node[26], ID);
318 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
319 node[4], node[5], node[6], node[7],
320 node[8], node[9], node[10],node[11],
321 node[12],node[13],node[14],node[15],
322 node[16],node[17],node[18],node[19],
323 node[20],node[21],node[22],node[23],
324 node[24],node[25],node[26] );
327 else if ( !features.myIsQuad )
329 if ( ID >= 1 ) e = mesh->AddPolyhedralVolumeWithID(node, features.myPolyhedQuantities, ID);
330 else e = mesh->AddPolyhedralVolume (node, features.myPolyhedQuantities );
334 // if ( ID >= 1 ) e = mesh->AddQuadPolyhedralVolumeWithID(node, features.myPolyhedQuantities,ID);
335 // else e = mesh->AddQuadPolyhedralVolume (node, features.myPolyhedQuantities );
341 if ( ID >= 1 ) e = mesh->AddEdgeWithID(node[0], node[1], ID);
342 else e = mesh->AddEdge (node[0], node[1] );
344 else if ( nbnode == 3 ) {
345 if ( ID >= 1 ) e = mesh->AddEdgeWithID(node[0], node[1], node[2], ID);
346 else e = mesh->AddEdge (node[0], node[1], node[2] );
350 case SMDSAbs_0DElement:
352 if ( ID >= 1 ) e = mesh->Add0DElementWithID(node[0], ID);
353 else e = mesh->Add0DElement (node[0] );
358 if ( ID >= 1 ) e = mesh->AddNodeWithID(node[0]->X(), node[0]->Y(), node[0]->Z(), ID);
359 else e = mesh->AddNode (node[0]->X(), node[0]->Y(), node[0]->Z() );
363 if ( ID >= 1 ) e = mesh->AddBallWithID(node[0], features.myBallDiameter, ID);
364 else e = mesh->AddBall (node[0], features.myBallDiameter );
369 if ( e ) myLastCreatedElems.Append( e );
373 //=======================================================================
377 //=======================================================================
379 SMDS_MeshElement* SMESH_MeshEditor::AddElement(const vector<int> & nodeIDs,
380 const ElemFeatures& features)
382 vector<const SMDS_MeshNode*> nodes;
383 nodes.reserve( nodeIDs.size() );
384 vector<int>::const_iterator id = nodeIDs.begin();
385 while ( id != nodeIDs.end() ) {
386 if ( const SMDS_MeshNode* node = GetMeshDS()->FindNode( *id++ ))
387 nodes.push_back( node );
391 return AddElement( nodes, features );
394 //=======================================================================
396 //purpose : Remove a node or an element.
397 // Modify a compute state of sub-meshes which become empty
398 //=======================================================================
400 int SMESH_MeshEditor::Remove (const list< int >& theIDs,
403 myLastCreatedElems.Clear();
404 myLastCreatedNodes.Clear();
406 SMESHDS_Mesh* aMesh = GetMeshDS();
407 set< SMESH_subMesh *> smmap;
410 list<int>::const_iterator it = theIDs.begin();
411 for ( ; it != theIDs.end(); it++ ) {
412 const SMDS_MeshElement * elem;
414 elem = aMesh->FindNode( *it );
416 elem = aMesh->FindElement( *it );
420 // Notify VERTEX sub-meshes about modification
422 const SMDS_MeshNode* node = cast2Node( elem );
423 if ( node->GetPosition()->GetTypeOfPosition() == SMDS_TOP_VERTEX )
424 if ( int aShapeID = node->getshapeId() )
425 if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( aShapeID ) )
428 // Find sub-meshes to notify about modification
429 // SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
430 // while ( nodeIt->more() ) {
431 // const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
432 // const SMDS_PositionPtr& aPosition = node->GetPosition();
433 // if ( aPosition.get() ) {
434 // if ( int aShapeID = aPosition->GetShapeId() ) {
435 // if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( aShapeID ) )
436 // smmap.insert( sm );
443 aMesh->RemoveNode( static_cast< const SMDS_MeshNode* >( elem ));
445 aMesh->RemoveElement( elem );
449 // Notify sub-meshes about modification
450 if ( !smmap.empty() ) {
451 set< SMESH_subMesh *>::iterator smIt;
452 for ( smIt = smmap.begin(); smIt != smmap.end(); smIt++ )
453 (*smIt)->ComputeStateEngine( SMESH_subMesh::MESH_ENTITY_REMOVED );
456 // // Check if the whole mesh becomes empty
457 // if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( 1 ) )
458 // sm->ComputeStateEngine( SMESH_subMesh::CHECK_COMPUTE_STATE );
463 //================================================================================
465 * \brief Create 0D elements on all nodes of the given object except those
466 * nodes on which a 0D element already exists.
467 * \param elements - Elements on whose nodes to create 0D elements; if empty,
468 * the all mesh is treated
469 * \param all0DElems - returns all 0D elements found or created on nodes of \a elements
471 //================================================================================
473 void SMESH_MeshEditor::Create0DElementsOnAllNodes( const TIDSortedElemSet& elements,
474 TIDSortedElemSet& all0DElems )
476 SMDS_ElemIteratorPtr elemIt;
477 vector< const SMDS_MeshElement* > allNodes;
478 if ( elements.empty() )
480 allNodes.reserve( GetMeshDS()->NbNodes() );
481 elemIt = GetMeshDS()->elementsIterator( SMDSAbs_Node );
482 while ( elemIt->more() )
483 allNodes.push_back( elemIt->next() );
485 elemIt = elemSetIterator( allNodes );
489 elemIt = elemSetIterator( elements );
492 while ( elemIt->more() )
494 const SMDS_MeshElement* e = elemIt->next();
495 SMDS_ElemIteratorPtr nodeIt = e->nodesIterator();
496 while ( nodeIt->more() )
498 const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
499 SMDS_ElemIteratorPtr it0D = n->GetInverseElementIterator( SMDSAbs_0DElement );
501 all0DElems.insert( it0D->next() );
503 myLastCreatedElems.Append( GetMeshDS()->Add0DElement( n ));
504 all0DElems.insert( myLastCreatedElems.Last() );
510 //=======================================================================
511 //function : FindShape
512 //purpose : Return an index of the shape theElem is on
513 // or zero if a shape not found
514 //=======================================================================
516 int SMESH_MeshEditor::FindShape (const SMDS_MeshElement * theElem)
518 myLastCreatedElems.Clear();
519 myLastCreatedNodes.Clear();
521 SMESHDS_Mesh * aMesh = GetMeshDS();
522 if ( aMesh->ShapeToMesh().IsNull() )
525 int aShapeID = theElem->getshapeId();
529 if ( SMESHDS_SubMesh * sm = aMesh->MeshElements( aShapeID ))
530 if ( sm->Contains( theElem ))
533 if ( theElem->GetType() == SMDSAbs_Node ) {
534 MESSAGE( ":( Error: invalid myShapeId of node " << theElem->GetID() );
537 MESSAGE( ":( Error: invalid myShapeId of element " << theElem->GetID() );
540 TopoDS_Shape aShape; // the shape a node of theElem is on
541 if ( theElem->GetType() != SMDSAbs_Node )
543 SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator();
544 while ( nodeIt->more() ) {
545 const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
546 if ((aShapeID = node->getshapeId()) > 0) {
547 if ( SMESHDS_SubMesh * sm = aMesh->MeshElements( aShapeID ) ) {
548 if ( sm->Contains( theElem ))
550 if ( aShape.IsNull() )
551 aShape = aMesh->IndexToShape( aShapeID );
557 // None of nodes is on a proper shape,
558 // find the shape among ancestors of aShape on which a node is
559 if ( !aShape.IsNull() ) {
560 TopTools_ListIteratorOfListOfShape ancIt( GetMesh()->GetAncestors( aShape ));
561 for ( ; ancIt.More(); ancIt.Next() ) {
562 SMESHDS_SubMesh * sm = aMesh->MeshElements( ancIt.Value() );
563 if ( sm && sm->Contains( theElem ))
564 return aMesh->ShapeToIndex( ancIt.Value() );
569 SMESHDS_SubMeshIteratorPtr smIt = GetMeshDS()->SubMeshes();
570 while ( const SMESHDS_SubMesh* sm = smIt->next() )
571 if ( sm->Contains( theElem ))
578 //=======================================================================
579 //function : IsMedium
581 //=======================================================================
583 bool SMESH_MeshEditor::IsMedium(const SMDS_MeshNode* node,
584 const SMDSAbs_ElementType typeToCheck)
586 bool isMedium = false;
587 SMDS_ElemIteratorPtr it = node->GetInverseElementIterator(typeToCheck);
588 while (it->more() && !isMedium ) {
589 const SMDS_MeshElement* elem = it->next();
590 isMedium = elem->IsMediumNode(node);
595 //=======================================================================
596 //function : shiftNodesQuadTria
597 //purpose : Shift nodes in the array corresponded to quadratic triangle
598 // example: (0,1,2,3,4,5) -> (1,2,0,4,5,3)
599 //=======================================================================
601 static void shiftNodesQuadTria(vector< const SMDS_MeshNode* >& aNodes)
603 const SMDS_MeshNode* nd1 = aNodes[0];
604 aNodes[0] = aNodes[1];
605 aNodes[1] = aNodes[2];
607 const SMDS_MeshNode* nd2 = aNodes[3];
608 aNodes[3] = aNodes[4];
609 aNodes[4] = aNodes[5];
613 //=======================================================================
614 //function : nbEdgeConnectivity
615 //purpose : return number of the edges connected with the theNode.
616 // if theEdges has connections with the other type of the
617 // elements, return -1
618 //=======================================================================
620 static int nbEdgeConnectivity(const SMDS_MeshNode* theNode)
622 // SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator();
624 // while(elemIt->more()) {
629 return theNode->NbInverseElements();
632 //=======================================================================
633 //function : getNodesFromTwoTria
635 //=======================================================================
637 static bool getNodesFromTwoTria(const SMDS_MeshElement * theTria1,
638 const SMDS_MeshElement * theTria2,
639 vector< const SMDS_MeshNode*>& N1,
640 vector< const SMDS_MeshNode*>& N2)
642 N1.assign( theTria1->begin_nodes(), theTria1->end_nodes() );
643 if ( N1.size() < 6 ) return false;
644 N2.assign( theTria2->begin_nodes(), theTria2->end_nodes() );
645 if ( N2.size() < 6 ) return false;
647 int sames[3] = {-1,-1,-1};
659 if(nbsames!=2) return false;
661 shiftNodesQuadTria(N1);
663 shiftNodesQuadTria(N1);
666 i = sames[0] + sames[1] + sames[2];
668 shiftNodesQuadTria(N2);
670 // now we receive following N1 and N2 (using numeration as in the image below)
671 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
672 // i.e. first nodes from both arrays form a new diagonal
676 //=======================================================================
677 //function : InverseDiag
678 //purpose : Replace two neighbour triangles with ones built on the same 4 nodes
679 // but having other common link.
680 // Return False if args are improper
681 //=======================================================================
683 bool SMESH_MeshEditor::InverseDiag (const SMDS_MeshElement * theTria1,
684 const SMDS_MeshElement * theTria2 )
686 myLastCreatedElems.Clear();
687 myLastCreatedNodes.Clear();
689 if (!theTria1 || !theTria2)
692 const SMDS_VtkFace* F1 = dynamic_cast<const SMDS_VtkFace*>( theTria1 );
693 if (!F1) return false;
694 const SMDS_VtkFace* F2 = dynamic_cast<const SMDS_VtkFace*>( theTria2 );
695 if (!F2) return false;
696 if ((theTria1->GetEntityType() == SMDSEntity_Triangle) &&
697 (theTria2->GetEntityType() == SMDSEntity_Triangle)) {
699 // 1 +--+ A theTria1: ( 1 A B ) A->2 ( 1 2 B ) 1 +--+ A
700 // | /| theTria2: ( B A 2 ) B->1 ( 1 A 2 ) |\ |
704 // put nodes in array and find out indices of the same ones
705 const SMDS_MeshNode* aNodes [6];
706 int sameInd [] = { -1, -1, -1, -1, -1, -1 };
708 SMDS_ElemIteratorPtr it = theTria1->nodesIterator();
709 while ( it->more() ) {
710 aNodes[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
712 if ( i > 2 ) // theTria2
713 // find same node of theTria1
714 for ( int j = 0; j < 3; j++ )
715 if ( aNodes[ i ] == aNodes[ j ]) {
724 return false; // theTria1 is not a triangle
725 it = theTria2->nodesIterator();
727 if ( i == 6 && it->more() )
728 return false; // theTria2 is not a triangle
731 // find indices of 1,2 and of A,B in theTria1
732 int iA = -1, iB = 0, i1 = 0, i2 = 0;
733 for ( i = 0; i < 6; i++ ) {
734 if ( sameInd [ i ] == -1 ) {
739 if ( iA >= 0) iB = i;
743 // nodes 1 and 2 should not be the same
744 if ( aNodes[ i1 ] == aNodes[ i2 ] )
748 aNodes[ iA ] = aNodes[ i2 ];
750 aNodes[ sameInd[ iB ]] = aNodes[ i1 ];
752 GetMeshDS()->ChangeElementNodes( theTria1, aNodes, 3 );
753 GetMeshDS()->ChangeElementNodes( theTria2, &aNodes[ 3 ], 3 );
757 } // end if(F1 && F2)
759 // check case of quadratic faces
760 if (theTria1->GetEntityType() != SMDSEntity_Quad_Triangle &&
761 theTria1->GetEntityType() != SMDSEntity_BiQuad_Triangle)
763 if (theTria2->GetEntityType() != SMDSEntity_Quad_Triangle&&
764 theTria2->GetEntityType() != SMDSEntity_BiQuad_Triangle)
768 // 1 +--+--+ 2 theTria1: (1 2 4 5 9 7) or (2 4 1 9 7 5) or (4 1 2 7 5 9)
769 // | /| theTria2: (2 3 4 6 8 9) or (3 4 2 8 9 6) or (4 2 3 9 6 8)
777 vector< const SMDS_MeshNode* > N1;
778 vector< const SMDS_MeshNode* > N2;
779 if(!getNodesFromTwoTria(theTria1,theTria2,N1,N2))
781 // now we receive following N1 and N2 (using numeration as above image)
782 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
783 // i.e. first nodes from both arrays determ new diagonal
785 vector< const SMDS_MeshNode*> N1new( N1.size() );
786 vector< const SMDS_MeshNode*> N2new( N2.size() );
787 N1new.back() = N1.back(); // central node of biquadratic
788 N2new.back() = N2.back();
789 N1new[0] = N1[0]; N2new[0] = N1[0];
790 N1new[1] = N2[0]; N2new[1] = N1[1];
791 N1new[2] = N2[1]; N2new[2] = N2[0];
792 N1new[3] = N1[4]; N2new[3] = N1[3];
793 N1new[4] = N2[3]; N2new[4] = N2[5];
794 N1new[5] = N1[5]; N2new[5] = N1[4];
795 // change nodes in faces
796 GetMeshDS()->ChangeElementNodes( theTria1, &N1new[0], N1new.size() );
797 GetMeshDS()->ChangeElementNodes( theTria2, &N2new[0], N2new.size() );
799 // move the central node of biquadratic triangle
800 SMESH_MesherHelper helper( *GetMesh() );
801 for ( int is2nd = 0; is2nd < 2; ++is2nd )
803 const SMDS_MeshElement* tria = is2nd ? theTria2 : theTria1;
804 vector< const SMDS_MeshNode*>& nodes = is2nd ? N2new : N1new;
805 if ( nodes.size() < 7 )
807 helper.SetSubShape( tria->getshapeId() );
808 const TopoDS_Face& F = TopoDS::Face( helper.GetSubShape() );
812 xyz = ( SMESH_TNodeXYZ( nodes[3] ) +
813 SMESH_TNodeXYZ( nodes[4] ) +
814 SMESH_TNodeXYZ( nodes[5] )) / 3.;
819 gp_XY uv = ( helper.GetNodeUV( F, nodes[3], nodes[2], &checkUV ) +
820 helper.GetNodeUV( F, nodes[4], nodes[0], &checkUV ) +
821 helper.GetNodeUV( F, nodes[5], nodes[1], &checkUV )) / 3.;
823 Handle(Geom_Surface) S = BRep_Tool::Surface(F,loc);
824 xyz = S->Value( uv.X(), uv.Y() );
825 xyz.Transform( loc );
826 if ( nodes[6]->GetPosition()->GetTypeOfPosition() == SMDS_TOP_FACE && // set UV
827 nodes[6]->getshapeId() > 0 )
828 GetMeshDS()->SetNodeOnFace( nodes[6], nodes[6]->getshapeId(), uv.X(), uv.Y() );
830 GetMeshDS()->MoveNode( nodes[6], xyz.X(), xyz.Y(), xyz.Z() );
835 //=======================================================================
836 //function : findTriangles
837 //purpose : find triangles sharing theNode1-theNode2 link
838 //=======================================================================
840 static bool findTriangles(const SMDS_MeshNode * theNode1,
841 const SMDS_MeshNode * theNode2,
842 const SMDS_MeshElement*& theTria1,
843 const SMDS_MeshElement*& theTria2)
845 if ( !theNode1 || !theNode2 ) return false;
847 theTria1 = theTria2 = 0;
849 set< const SMDS_MeshElement* > emap;
850 SMDS_ElemIteratorPtr it = theNode1->GetInverseElementIterator(SMDSAbs_Face);
852 const SMDS_MeshElement* elem = it->next();
853 if ( elem->NbCornerNodes() == 3 )
856 it = theNode2->GetInverseElementIterator(SMDSAbs_Face);
858 const SMDS_MeshElement* elem = it->next();
859 if ( emap.count( elem )) {
867 // theTria1 must be element with minimum ID
868 if ( theTria2->GetID() < theTria1->GetID() )
869 std::swap( theTria2, theTria1 );
877 //=======================================================================
878 //function : InverseDiag
879 //purpose : Replace two neighbour triangles sharing theNode1-theNode2 link
880 // with ones built on the same 4 nodes but having other common link.
881 // Return false if proper faces not found
882 //=======================================================================
884 bool SMESH_MeshEditor::InverseDiag (const SMDS_MeshNode * theNode1,
885 const SMDS_MeshNode * theNode2)
887 myLastCreatedElems.Clear();
888 myLastCreatedNodes.Clear();
890 const SMDS_MeshElement *tr1, *tr2;
891 if ( !findTriangles( theNode1, theNode2, tr1, tr2 ))
894 const SMDS_VtkFace* F1 = dynamic_cast<const SMDS_VtkFace*>( tr1 );
895 if (!F1) return false;
896 const SMDS_VtkFace* F2 = dynamic_cast<const SMDS_VtkFace*>( tr2 );
897 if (!F2) return false;
898 if ((tr1->GetEntityType() == SMDSEntity_Triangle) &&
899 (tr2->GetEntityType() == SMDSEntity_Triangle)) {
901 // 1 +--+ A tr1: ( 1 A B ) A->2 ( 1 2 B ) 1 +--+ A
902 // | /| tr2: ( B A 2 ) B->1 ( 1 A 2 ) |\ |
906 // put nodes in array
907 // and find indices of 1,2 and of A in tr1 and of B in tr2
908 int i, iA1 = 0, i1 = 0;
909 const SMDS_MeshNode* aNodes1 [3];
910 SMDS_ElemIteratorPtr it;
911 for (i = 0, it = tr1->nodesIterator(); it->more(); i++ ) {
912 aNodes1[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
913 if ( aNodes1[ i ] == theNode1 )
914 iA1 = i; // node A in tr1
915 else if ( aNodes1[ i ] != theNode2 )
919 const SMDS_MeshNode* aNodes2 [3];
920 for (i = 0, it = tr2->nodesIterator(); it->more(); i++ ) {
921 aNodes2[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
922 if ( aNodes2[ i ] == theNode2 )
923 iB2 = i; // node B in tr2
924 else if ( aNodes2[ i ] != theNode1 )
928 // nodes 1 and 2 should not be the same
929 if ( aNodes1[ i1 ] == aNodes2[ i2 ] )
933 aNodes1[ iA1 ] = aNodes2[ i2 ];
935 aNodes2[ iB2 ] = aNodes1[ i1 ];
937 GetMeshDS()->ChangeElementNodes( tr1, aNodes1, 3 );
938 GetMeshDS()->ChangeElementNodes( tr2, aNodes2, 3 );
943 // check case of quadratic faces
944 return InverseDiag(tr1,tr2);
947 //=======================================================================
948 //function : getQuadrangleNodes
949 //purpose : fill theQuadNodes - nodes of a quadrangle resulting from
950 // fusion of triangles tr1 and tr2 having shared link on
951 // theNode1 and theNode2
952 //=======================================================================
954 bool getQuadrangleNodes(const SMDS_MeshNode * theQuadNodes [],
955 const SMDS_MeshNode * theNode1,
956 const SMDS_MeshNode * theNode2,
957 const SMDS_MeshElement * tr1,
958 const SMDS_MeshElement * tr2 )
960 if( tr1->NbNodes() != tr2->NbNodes() )
962 // find the 4-th node to insert into tr1
963 const SMDS_MeshNode* n4 = 0;
964 SMDS_ElemIteratorPtr it = tr2->nodesIterator();
966 while ( !n4 && i<3 ) {
967 const SMDS_MeshNode * n = cast2Node( it->next() );
969 bool isDiag = ( n == theNode1 || n == theNode2 );
973 // Make an array of nodes to be in a quadrangle
974 int iNode = 0, iFirstDiag = -1;
975 it = tr1->nodesIterator();
978 const SMDS_MeshNode * n = cast2Node( it->next() );
980 bool isDiag = ( n == theNode1 || n == theNode2 );
982 if ( iFirstDiag < 0 )
984 else if ( iNode - iFirstDiag == 1 )
985 theQuadNodes[ iNode++ ] = n4; // insert the 4-th node between diagonal nodes
987 else if ( n == n4 ) {
988 return false; // tr1 and tr2 should not have all the same nodes
990 theQuadNodes[ iNode++ ] = n;
992 if ( iNode == 3 ) // diagonal nodes have 0 and 2 indices
993 theQuadNodes[ iNode ] = n4;
998 //=======================================================================
999 //function : DeleteDiag
1000 //purpose : Replace two neighbour triangles sharing theNode1-theNode2 link
1001 // with a quadrangle built on the same 4 nodes.
1002 // Return false if proper faces not found
1003 //=======================================================================
1005 bool SMESH_MeshEditor::DeleteDiag (const SMDS_MeshNode * theNode1,
1006 const SMDS_MeshNode * theNode2)
1008 myLastCreatedElems.Clear();
1009 myLastCreatedNodes.Clear();
1011 const SMDS_MeshElement *tr1, *tr2;
1012 if ( !findTriangles( theNode1, theNode2, tr1, tr2 ))
1015 const SMDS_VtkFace* F1 = dynamic_cast<const SMDS_VtkFace*>( tr1 );
1016 if (!F1) return false;
1017 const SMDS_VtkFace* F2 = dynamic_cast<const SMDS_VtkFace*>( tr2 );
1018 if (!F2) return false;
1019 SMESHDS_Mesh * aMesh = GetMeshDS();
1021 if ((tr1->GetEntityType() == SMDSEntity_Triangle) &&
1022 (tr2->GetEntityType() == SMDSEntity_Triangle)) {
1024 const SMDS_MeshNode* aNodes [ 4 ];
1025 if ( ! getQuadrangleNodes( aNodes, theNode1, theNode2, tr1, tr2 ))
1028 const SMDS_MeshElement* newElem = 0;
1029 newElem = aMesh->AddFace( aNodes[0], aNodes[1], aNodes[2], aNodes[3] );
1030 myLastCreatedElems.Append(newElem);
1031 AddToSameGroups( newElem, tr1, aMesh );
1032 int aShapeId = tr1->getshapeId();
1035 aMesh->SetMeshElementOnShape( newElem, aShapeId );
1037 aMesh->RemoveElement( tr1 );
1038 aMesh->RemoveElement( tr2 );
1043 // check case of quadratic faces
1044 if (tr1->GetEntityType() != SMDSEntity_Quad_Triangle)
1046 if (tr2->GetEntityType() != SMDSEntity_Quad_Triangle)
1050 // 1 +--+--+ 2 tr1: (1 2 4 5 9 7) or (2 4 1 9 7 5) or (4 1 2 7 5 9)
1051 // | /| tr2: (2 3 4 6 8 9) or (3 4 2 8 9 6) or (4 2 3 9 6 8)
1059 vector< const SMDS_MeshNode* > N1;
1060 vector< const SMDS_MeshNode* > N2;
1061 if(!getNodesFromTwoTria(tr1,tr2,N1,N2))
1063 // now we receive following N1 and N2 (using numeration as above image)
1064 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
1065 // i.e. first nodes from both arrays determ new diagonal
1067 const SMDS_MeshNode* aNodes[8];
1077 const SMDS_MeshElement* newElem = 0;
1078 newElem = aMesh->AddFace( aNodes[0], aNodes[1], aNodes[2], aNodes[3],
1079 aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
1080 myLastCreatedElems.Append(newElem);
1081 AddToSameGroups( newElem, tr1, aMesh );
1082 int aShapeId = tr1->getshapeId();
1085 aMesh->SetMeshElementOnShape( newElem, aShapeId );
1087 aMesh->RemoveElement( tr1 );
1088 aMesh->RemoveElement( tr2 );
1090 // remove middle node (9)
1091 GetMeshDS()->RemoveNode( N1[4] );
1096 //=======================================================================
1097 //function : Reorient
1098 //purpose : Reverse theElement orientation
1099 //=======================================================================
1101 bool SMESH_MeshEditor::Reorient (const SMDS_MeshElement * theElem)
1103 myLastCreatedElems.Clear();
1104 myLastCreatedNodes.Clear();
1108 SMDS_ElemIteratorPtr it = theElem->nodesIterator();
1109 if ( !it || !it->more() )
1112 const SMDSAbs_ElementType type = theElem->GetType();
1113 if ( type < SMDSAbs_Edge || type > SMDSAbs_Volume )
1116 const SMDSAbs_EntityType geomType = theElem->GetEntityType();
1117 if ( geomType == SMDSEntity_Polyhedra ) // polyhedron
1119 const SMDS_VtkVolume* aPolyedre =
1120 dynamic_cast<const SMDS_VtkVolume*>( theElem );
1122 MESSAGE("Warning: bad volumic element");
1125 const int nbFaces = aPolyedre->NbFaces();
1126 vector<const SMDS_MeshNode *> poly_nodes;
1127 vector<int> quantities (nbFaces);
1129 // reverse each face of the polyedre
1130 for (int iface = 1; iface <= nbFaces; iface++) {
1131 int inode, nbFaceNodes = aPolyedre->NbFaceNodes(iface);
1132 quantities[iface - 1] = nbFaceNodes;
1134 for (inode = nbFaceNodes; inode >= 1; inode--) {
1135 const SMDS_MeshNode* curNode = aPolyedre->GetFaceNode(iface, inode);
1136 poly_nodes.push_back(curNode);
1139 return GetMeshDS()->ChangePolyhedronNodes( theElem, poly_nodes, quantities );
1141 else // other elements
1143 vector<const SMDS_MeshNode*> nodes( theElem->begin_nodes(), theElem->end_nodes() );
1144 const std::vector<int>& interlace = SMDS_MeshCell::reverseSmdsOrder( geomType, nodes.size() );
1145 if ( interlace.empty() )
1147 std::reverse( nodes.begin(), nodes.end() ); // obsolete, just in case
1151 SMDS_MeshCell::applyInterlace( interlace, nodes );
1153 return GetMeshDS()->ChangeElementNodes( theElem, &nodes[0], nodes.size() );
1158 //================================================================================
1160 * \brief Reorient faces.
1161 * \param theFaces - the faces to reorient. If empty the whole mesh is meant
1162 * \param theDirection - desired direction of normal of \a theFace
1163 * \param theFace - one of \a theFaces that sould be oriented according to
1164 * \a theDirection and whose orientation defines orientation of other faces
1165 * \return number of reoriented faces.
1167 //================================================================================
1169 int SMESH_MeshEditor::Reorient2D (TIDSortedElemSet & theFaces,
1170 const gp_Dir& theDirection,
1171 const SMDS_MeshElement * theFace)
1174 if ( !theFace || theFace->GetType() != SMDSAbs_Face ) return nbReori;
1176 if ( theFaces.empty() )
1178 SMDS_FaceIteratorPtr fIt = GetMeshDS()->facesIterator(/*idInceasingOrder=*/true);
1179 while ( fIt->more() )
1180 theFaces.insert( theFaces.end(), fIt->next() );
1183 // orient theFace according to theDirection
1185 SMESH_MeshAlgos::FaceNormal( theFace, normal, /*normalized=*/false );
1186 if ( normal * theDirection.XYZ() < 0 )
1187 nbReori += Reorient( theFace );
1189 // Orient other faces
1191 set< const SMDS_MeshElement* > startFaces, visitedFaces;
1192 TIDSortedElemSet avoidSet;
1193 set< SMESH_TLink > checkedLinks;
1194 pair< set< SMESH_TLink >::iterator, bool > linkIt_isNew;
1196 if ( theFaces.size() > 1 )// leave 1 face to prevent finding not selected faces
1197 theFaces.erase( theFace );
1198 startFaces.insert( theFace );
1200 int nodeInd1, nodeInd2;
1201 const SMDS_MeshElement* otherFace;
1202 vector< const SMDS_MeshElement* > facesNearLink;
1203 vector< std::pair< int, int > > nodeIndsOfFace;
1205 set< const SMDS_MeshElement* >::iterator startFace = startFaces.begin();
1206 while ( !startFaces.empty() )
1208 startFace = startFaces.begin();
1209 theFace = *startFace;
1210 startFaces.erase( startFace );
1211 if ( !visitedFaces.insert( theFace ).second )
1215 avoidSet.insert(theFace);
1217 NLink link( theFace->GetNode( 0 ), (SMDS_MeshNode *) 0 );
1219 const int nbNodes = theFace->NbCornerNodes();
1220 for ( int i = 0; i < nbNodes; ++i ) // loop on links of theFace
1222 link.second = theFace->GetNode(( i+1 ) % nbNodes );
1223 linkIt_isNew = checkedLinks.insert( link );
1224 if ( !linkIt_isNew.second )
1226 // link has already been checked and won't be encountered more
1227 // if the group (theFaces) is manifold
1228 //checkedLinks.erase( linkIt_isNew.first );
1232 facesNearLink.clear();
1233 nodeIndsOfFace.clear();
1234 while (( otherFace = SMESH_MeshAlgos::FindFaceInSet( link.first, link.second,
1236 &nodeInd1, &nodeInd2 )))
1237 if ( otherFace != theFace)
1239 facesNearLink.push_back( otherFace );
1240 nodeIndsOfFace.push_back( make_pair( nodeInd1, nodeInd2 ));
1241 avoidSet.insert( otherFace );
1243 if ( facesNearLink.size() > 1 )
1245 // NON-MANIFOLD mesh shell !
1246 // select a face most co-directed with theFace,
1247 // other faces won't be visited this time
1249 SMESH_MeshAlgos::FaceNormal( theFace, NF, /*normalized=*/false );
1250 double proj, maxProj = -1;
1251 for ( size_t i = 0; i < facesNearLink.size(); ++i ) {
1252 SMESH_MeshAlgos::FaceNormal( facesNearLink[i], NOF, /*normalized=*/false );
1253 if (( proj = Abs( NF * NOF )) > maxProj ) {
1255 otherFace = facesNearLink[i];
1256 nodeInd1 = nodeIndsOfFace[i].first;
1257 nodeInd2 = nodeIndsOfFace[i].second;
1260 // not to visit rejected faces
1261 for ( size_t i = 0; i < facesNearLink.size(); ++i )
1262 if ( facesNearLink[i] != otherFace && theFaces.size() > 1 )
1263 visitedFaces.insert( facesNearLink[i] );
1265 else if ( facesNearLink.size() == 1 )
1267 otherFace = facesNearLink[0];
1268 nodeInd1 = nodeIndsOfFace.back().first;
1269 nodeInd2 = nodeIndsOfFace.back().second;
1271 if ( otherFace && otherFace != theFace)
1273 // link must be reverse in otherFace if orientation ot otherFace
1274 // is same as that of theFace
1275 if ( abs(nodeInd2-nodeInd1) == 1 ? nodeInd2 > nodeInd1 : nodeInd1 > nodeInd2 )
1277 nbReori += Reorient( otherFace );
1279 startFaces.insert( otherFace );
1282 std::swap( link.first, link.second ); // reverse the link
1288 //================================================================================
1290 * \brief Reorient faces basing on orientation of adjacent volumes.
1291 * \param theFaces - faces to reorient. If empty, all mesh faces are treated.
1292 * \param theVolumes - reference volumes.
1293 * \param theOutsideNormal - to orient faces to have their normal
1294 * pointing either \a outside or \a inside the adjacent volumes.
1295 * \return number of reoriented faces.
1297 //================================================================================
1299 int SMESH_MeshEditor::Reorient2DBy3D (TIDSortedElemSet & theFaces,
1300 TIDSortedElemSet & theVolumes,
1301 const bool theOutsideNormal)
1305 SMDS_ElemIteratorPtr faceIt;
1306 if ( theFaces.empty() )
1307 faceIt = GetMeshDS()->elementsIterator( SMDSAbs_Face );
1309 faceIt = elemSetIterator( theFaces );
1311 vector< const SMDS_MeshNode* > faceNodes;
1312 TIDSortedElemSet checkedVolumes;
1313 set< const SMDS_MeshNode* > faceNodesSet;
1314 SMDS_VolumeTool volumeTool;
1316 while ( faceIt->more() ) // loop on given faces
1318 const SMDS_MeshElement* face = faceIt->next();
1319 if ( face->GetType() != SMDSAbs_Face )
1322 const size_t nbCornersNodes = face->NbCornerNodes();
1323 faceNodes.assign( face->begin_nodes(), face->end_nodes() );
1325 checkedVolumes.clear();
1326 SMDS_ElemIteratorPtr vIt = faceNodes[ 0 ]->GetInverseElementIterator( SMDSAbs_Volume );
1327 while ( vIt->more() )
1329 const SMDS_MeshElement* volume = vIt->next();
1331 if ( !checkedVolumes.insert( volume ).second )
1333 if ( !theVolumes.empty() && !theVolumes.count( volume ))
1336 // is volume adjacent?
1337 bool allNodesCommon = true;
1338 for ( size_t iN = 1; iN < nbCornersNodes && allNodesCommon; ++iN )
1339 allNodesCommon = ( volume->GetNodeIndex( faceNodes[ iN ]) > -1 );
1340 if ( !allNodesCommon )
1343 // get nodes of a corresponding volume facet
1344 faceNodesSet.clear();
1345 faceNodesSet.insert( faceNodes.begin(), faceNodes.end() );
1346 volumeTool.Set( volume );
1347 int facetID = volumeTool.GetFaceIndex( faceNodesSet );
1348 if ( facetID < 0 ) continue;
1349 volumeTool.SetExternalNormal();
1350 const SMDS_MeshNode** facetNodes = volumeTool.GetFaceNodes( facetID );
1352 // compare order of faceNodes and facetNodes
1353 const int iQ = 1 + ( nbCornersNodes < faceNodes.size() );
1355 for ( int i = 0; i < 2; ++i )
1357 const SMDS_MeshNode* n = facetNodes[ i*iQ ];
1358 for ( size_t iN = 0; iN < nbCornersNodes; ++iN )
1359 if ( faceNodes[ iN ] == n )
1365 bool isOutside = Abs( iNN[0]-iNN[1] ) == 1 ? iNN[0] < iNN[1] : iNN[0] > iNN[1];
1366 if ( isOutside != theOutsideNormal )
1367 nbReori += Reorient( face );
1369 } // loop on given faces
1374 //=======================================================================
1375 //function : getBadRate
1377 //=======================================================================
1379 static double getBadRate (const SMDS_MeshElement* theElem,
1380 SMESH::Controls::NumericalFunctorPtr& theCrit)
1382 SMESH::Controls::TSequenceOfXYZ P;
1383 if ( !theElem || !theCrit->GetPoints( theElem, P ))
1385 return theCrit->GetBadRate( theCrit->GetValue( P ), theElem->NbNodes() );
1386 //return theCrit->GetBadRate( theCrit->GetValue( theElem->GetID() ), theElem->NbNodes() );
1389 //=======================================================================
1390 //function : QuadToTri
1391 //purpose : Cut quadrangles into triangles.
1392 // theCrit is used to select a diagonal to cut
1393 //=======================================================================
1395 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet & theElems,
1396 SMESH::Controls::NumericalFunctorPtr theCrit)
1398 myLastCreatedElems.Clear();
1399 myLastCreatedNodes.Clear();
1401 if ( !theCrit.get() )
1404 SMESHDS_Mesh * aMesh = GetMeshDS();
1406 Handle(Geom_Surface) surface;
1407 SMESH_MesherHelper helper( *GetMesh() );
1409 TIDSortedElemSet::iterator itElem;
1410 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
1412 const SMDS_MeshElement* elem = *itElem;
1413 if ( !elem || elem->GetType() != SMDSAbs_Face )
1415 if ( elem->NbCornerNodes() != 4 )
1418 // retrieve element nodes
1419 vector< const SMDS_MeshNode* > aNodes( elem->begin_nodes(), elem->end_nodes() );
1421 // compare two sets of possible triangles
1422 double aBadRate1, aBadRate2; // to what extent a set is bad
1423 SMDS_FaceOfNodes tr1 ( aNodes[0], aNodes[1], aNodes[2] );
1424 SMDS_FaceOfNodes tr2 ( aNodes[2], aNodes[3], aNodes[0] );
1425 aBadRate1 = getBadRate( &tr1, theCrit ) + getBadRate( &tr2, theCrit );
1427 SMDS_FaceOfNodes tr3 ( aNodes[1], aNodes[2], aNodes[3] );
1428 SMDS_FaceOfNodes tr4 ( aNodes[3], aNodes[0], aNodes[1] );
1429 aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit );
1431 const int aShapeId = FindShape( elem );
1432 const SMDS_MeshElement* newElem1 = 0;
1433 const SMDS_MeshElement* newElem2 = 0;
1435 if ( !elem->IsQuadratic() ) // split liner quadrangle
1437 // for MaxElementLength2D functor we return minimum diagonal for splitting,
1438 // because aBadRate1=2*len(diagonal 1-3); aBadRate2=2*len(diagonal 2-4)
1439 if ( aBadRate1 <= aBadRate2 ) {
1440 // tr1 + tr2 is better
1441 newElem1 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
1442 newElem2 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
1445 // tr3 + tr4 is better
1446 newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
1447 newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
1450 else // split quadratic quadrangle
1452 helper.SetIsQuadratic( true );
1453 helper.SetIsBiQuadratic( aNodes.size() == 9 );
1455 helper.AddTLinks( static_cast< const SMDS_MeshFace* >( elem ));
1456 if ( aNodes.size() == 9 )
1458 helper.SetIsBiQuadratic( true );
1459 if ( aBadRate1 <= aBadRate2 )
1460 helper.AddTLinkNode( aNodes[0], aNodes[2], aNodes[8] );
1462 helper.AddTLinkNode( aNodes[1], aNodes[3], aNodes[8] );
1464 // create a new element
1465 if ( aBadRate1 <= aBadRate2 ) {
1466 newElem1 = helper.AddFace( aNodes[2], aNodes[3], aNodes[0] );
1467 newElem2 = helper.AddFace( aNodes[2], aNodes[0], aNodes[1] );
1470 newElem1 = helper.AddFace( aNodes[3], aNodes[0], aNodes[1] );
1471 newElem2 = helper.AddFace( aNodes[3], aNodes[1], aNodes[2] );
1475 // care of a new element
1477 myLastCreatedElems.Append(newElem1);
1478 myLastCreatedElems.Append(newElem2);
1479 AddToSameGroups( newElem1, elem, aMesh );
1480 AddToSameGroups( newElem2, elem, aMesh );
1482 // put a new triangle on the same shape
1484 aMesh->SetMeshElementOnShape( newElem1, aShapeId );
1485 aMesh->SetMeshElementOnShape( newElem2, aShapeId );
1487 aMesh->RemoveElement( elem );
1492 //=======================================================================
1494 * \brief Split each of given quadrangles into 4 triangles.
1495 * \param theElems - The faces to be splitted. If empty all faces are split.
1497 //=======================================================================
1499 void SMESH_MeshEditor::QuadTo4Tri (TIDSortedElemSet & theElems)
1501 myLastCreatedElems.Clear();
1502 myLastCreatedNodes.Clear();
1504 SMESH_MesherHelper helper( *GetMesh() );
1505 helper.SetElementsOnShape( true );
1507 SMDS_ElemIteratorPtr faceIt;
1508 if ( theElems.empty() ) faceIt = GetMeshDS()->elementsIterator(SMDSAbs_Face);
1509 else faceIt = elemSetIterator( theElems );
1512 gp_XY uv [9]; uv[8] = gp_XY(0,0);
1514 vector< const SMDS_MeshNode* > nodes;
1515 SMESHDS_SubMesh* subMeshDS = 0;
1517 Handle(Geom_Surface) surface;
1518 TopLoc_Location loc;
1520 while ( faceIt->more() )
1522 const SMDS_MeshElement* quad = faceIt->next();
1523 if ( !quad || quad->NbCornerNodes() != 4 )
1526 // get a surface the quad is on
1528 if ( quad->getshapeId() < 1 )
1531 helper.SetSubShape( 0 );
1534 else if ( quad->getshapeId() != helper.GetSubShapeID() )
1536 helper.SetSubShape( quad->getshapeId() );
1537 if ( !helper.GetSubShape().IsNull() &&
1538 helper.GetSubShape().ShapeType() == TopAbs_FACE )
1540 F = TopoDS::Face( helper.GetSubShape() );
1541 surface = BRep_Tool::Surface( F, loc );
1542 subMeshDS = GetMeshDS()->MeshElements( quad->getshapeId() );
1546 helper.SetSubShape( 0 );
1551 // create a central node
1553 const SMDS_MeshNode* nCentral;
1554 nodes.assign( quad->begin_nodes(), quad->end_nodes() );
1556 if ( nodes.size() == 9 )
1558 nCentral = nodes.back();
1565 for ( ; iN < nodes.size(); ++iN )
1566 xyz[ iN ] = SMESH_TNodeXYZ( nodes[ iN ] );
1568 for ( ; iN < 8; ++iN ) // mid-side points of a linear qudrangle
1569 xyz[ iN ] = 0.5 * ( xyz[ iN - 4 ] + xyz[( iN - 3 )%4 ] );
1571 xyz[ 8 ] = helper.calcTFI( 0.5, 0.5,
1572 xyz[0], xyz[1], xyz[2], xyz[3],
1573 xyz[4], xyz[5], xyz[6], xyz[7] );
1577 for ( ; iN < nodes.size(); ++iN )
1578 uv[ iN ] = helper.GetNodeUV( F, nodes[iN], nodes[(iN+2)%4], &checkUV );
1580 for ( ; iN < 8; ++iN ) // UV of mid-side points of a linear qudrangle
1581 uv[ iN ] = helper.GetMiddleUV( surface, uv[ iN - 4 ], uv[( iN - 3 )%4 ] );
1583 uv[ 8 ] = helper.calcTFI( 0.5, 0.5,
1584 uv[0], uv[1], uv[2], uv[3],
1585 uv[4], uv[5], uv[6], uv[7] );
1587 gp_Pnt p = surface->Value( uv[8].X(), uv[8].Y() ).Transformed( loc );
1591 nCentral = helper.AddNode( xyz[8].X(), xyz[8].Y(), xyz[8].Z(), /*id=*/0,
1592 uv[8].X(), uv[8].Y() );
1593 myLastCreatedNodes.Append( nCentral );
1596 // create 4 triangles
1598 helper.SetIsQuadratic ( nodes.size() > 4 );
1599 helper.SetIsBiQuadratic( nodes.size() == 9 );
1600 if ( helper.GetIsQuadratic() )
1601 helper.AddTLinks( static_cast< const SMDS_MeshFace*>( quad ));
1603 GetMeshDS()->RemoveFreeElement( quad, subMeshDS, /*fromGroups=*/false );
1605 for ( int i = 0; i < 4; ++i )
1607 SMDS_MeshElement* tria = helper.AddFace( nodes[ i ],
1610 ReplaceElemInGroups( tria, quad, GetMeshDS() );
1611 myLastCreatedElems.Append( tria );
1616 //=======================================================================
1617 //function : BestSplit
1618 //purpose : Find better diagonal for cutting.
1619 //=======================================================================
1621 int SMESH_MeshEditor::BestSplit (const SMDS_MeshElement* theQuad,
1622 SMESH::Controls::NumericalFunctorPtr theCrit)
1624 myLastCreatedElems.Clear();
1625 myLastCreatedNodes.Clear();
1630 if (!theQuad || theQuad->GetType() != SMDSAbs_Face )
1633 if( theQuad->NbNodes()==4 ||
1634 (theQuad->NbNodes()==8 && theQuad->IsQuadratic()) ) {
1636 // retrieve element nodes
1637 const SMDS_MeshNode* aNodes [4];
1638 SMDS_ElemIteratorPtr itN = theQuad->nodesIterator();
1640 //while (itN->more())
1642 aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
1644 // compare two sets of possible triangles
1645 double aBadRate1, aBadRate2; // to what extent a set is bad
1646 SMDS_FaceOfNodes tr1 ( aNodes[0], aNodes[1], aNodes[2] );
1647 SMDS_FaceOfNodes tr2 ( aNodes[2], aNodes[3], aNodes[0] );
1648 aBadRate1 = getBadRate( &tr1, theCrit ) + getBadRate( &tr2, theCrit );
1650 SMDS_FaceOfNodes tr3 ( aNodes[1], aNodes[2], aNodes[3] );
1651 SMDS_FaceOfNodes tr4 ( aNodes[3], aNodes[0], aNodes[1] );
1652 aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit );
1653 // for MaxElementLength2D functor we return minimum diagonal for splitting,
1654 // because aBadRate1=2*len(diagonal 1-3); aBadRate2=2*len(diagonal 2-4)
1655 if (aBadRate1 <= aBadRate2) // tr1 + tr2 is better
1656 return 1; // diagonal 1-3
1658 return 2; // diagonal 2-4
1665 // Methods of splitting volumes into tetra
1667 const int theHexTo5_1[5*4+1] =
1669 0, 1, 2, 5, 0, 4, 5, 7, 0, 2, 3, 7, 2, 5, 6, 7, 0, 5, 2, 7, -1
1671 const int theHexTo5_2[5*4+1] =
1673 1, 2, 3, 6, 1, 4, 5, 6, 0, 1, 3, 4, 3, 4, 6, 7, 1, 3, 4, 6, -1
1675 const int* theHexTo5[2] = { theHexTo5_1, theHexTo5_2 };
1677 const int theHexTo6_1[6*4+1] =
1679 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
1681 const int theHexTo6_2[6*4+1] =
1683 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
1685 const int theHexTo6_3[6*4+1] =
1687 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
1689 const int theHexTo6_4[6*4+1] =
1691 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
1693 const int* theHexTo6[4] = { theHexTo6_1, theHexTo6_2, theHexTo6_3, theHexTo6_4 };
1695 const int thePyraTo2_1[2*4+1] =
1697 0, 1, 2, 4, 0, 2, 3, 4, -1
1699 const int thePyraTo2_2[2*4+1] =
1701 1, 2, 3, 4, 1, 3, 0, 4, -1
1703 const int* thePyraTo2[2] = { thePyraTo2_1, thePyraTo2_2 };
1705 const int thePentaTo3_1[3*4+1] =
1707 0, 1, 2, 3, 1, 3, 4, 2, 2, 3, 4, 5, -1
1709 const int thePentaTo3_2[3*4+1] =
1711 1, 2, 0, 4, 2, 4, 5, 0, 0, 4, 5, 3, -1
1713 const int thePentaTo3_3[3*4+1] =
1715 2, 0, 1, 5, 0, 5, 3, 1, 1, 5, 3, 4, -1
1717 const int thePentaTo3_4[3*4+1] =
1719 0, 1, 2, 3, 1, 3, 4, 5, 2, 3, 1, 5, -1
1721 const int thePentaTo3_5[3*4+1] =
1723 1, 2, 0, 4, 2, 4, 5, 3, 0, 4, 2, 3, -1
1725 const int thePentaTo3_6[3*4+1] =
1727 2, 0, 1, 5, 0, 5, 3, 4, 1, 5, 0, 4, -1
1729 const int* thePentaTo3[6] = { thePentaTo3_1, thePentaTo3_2, thePentaTo3_3,
1730 thePentaTo3_4, thePentaTo3_5, thePentaTo3_6 };
1732 // Methods of splitting hexahedron into prisms
1734 const int theHexTo4Prisms_BT[6*4+1] = // bottom-top
1736 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
1738 const int theHexTo4Prisms_LR[6*4+1] = // left-right
1740 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
1742 const int theHexTo4Prisms_FB[6*4+1] = // front-back
1744 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
1747 const int theHexTo2Prisms_BT_1[6*2+1] =
1749 0, 1, 3, 4, 5, 7, 1, 2, 3, 5, 6, 7, -1
1751 const int theHexTo2Prisms_BT_2[6*2+1] =
1753 0, 1, 2, 4, 5, 6, 0, 2, 3, 4, 6, 7, -1
1755 const int* theHexTo2Prisms_BT[2] = { theHexTo2Prisms_BT_1, theHexTo2Prisms_BT_2 };
1757 const int theHexTo2Prisms_LR_1[6*2+1] =
1759 1, 0, 4, 2, 3, 7, 1, 4, 5, 2, 7, 6, -1
1761 const int theHexTo2Prisms_LR_2[6*2+1] =
1763 1, 0, 4, 2, 3, 7, 1, 4, 5, 2, 7, 6, -1
1765 const int* theHexTo2Prisms_LR[2] = { theHexTo2Prisms_LR_1, theHexTo2Prisms_LR_2 };
1767 const int theHexTo2Prisms_FB_1[6*2+1] =
1769 0, 3, 4, 1, 2, 5, 3, 7, 4, 2, 6, 5, -1
1771 const int theHexTo2Prisms_FB_2[6*2+1] =
1773 0, 3, 7, 1, 2, 7, 0, 7, 4, 1, 6, 5, -1
1775 const int* theHexTo2Prisms_FB[2] = { theHexTo2Prisms_FB_1, theHexTo2Prisms_FB_2 };
1778 struct TTriangleFacet //!< stores indices of three nodes of tetra facet
1781 TTriangleFacet(int n1, int n2, int n3): _n1(n1), _n2(n2), _n3(n3) {}
1782 bool contains(int n) const { return ( n == _n1 || n == _n2 || n == _n3 ); }
1783 bool hasAdjacentVol( const SMDS_MeshElement* elem,
1784 const SMDSAbs_GeometryType geom = SMDSGeom_TETRA) const;
1790 const int* _connectivity; //!< foursomes of tetra connectivy finished by -1
1791 bool _baryNode; //!< additional node is to be created at cell barycenter
1792 bool _ownConn; //!< to delete _connectivity in destructor
1793 map<int, const SMDS_MeshNode*> _faceBaryNode; //!< map face index to node at BC of face
1795 TSplitMethod( int nbTet=0, const int* conn=0, bool addNode=false)
1796 : _nbSplits(nbTet), _nbCorners(4), _connectivity(conn), _baryNode(addNode), _ownConn(false) {}
1797 ~TSplitMethod() { if ( _ownConn ) delete [] _connectivity; _connectivity = 0; }
1798 bool hasFacet( const TTriangleFacet& facet ) const
1800 if ( _nbCorners == 4 )
1802 const int* tetConn = _connectivity;
1803 for ( ; tetConn[0] >= 0; tetConn += 4 )
1804 if (( facet.contains( tetConn[0] ) +
1805 facet.contains( tetConn[1] ) +
1806 facet.contains( tetConn[2] ) +
1807 facet.contains( tetConn[3] )) == 3 )
1810 else // prism, _nbCorners == 6
1812 const int* prismConn = _connectivity;
1813 for ( ; prismConn[0] >= 0; prismConn += 6 )
1815 if (( facet.contains( prismConn[0] ) &&
1816 facet.contains( prismConn[1] ) &&
1817 facet.contains( prismConn[2] ))
1819 ( facet.contains( prismConn[3] ) &&
1820 facet.contains( prismConn[4] ) &&
1821 facet.contains( prismConn[5] )))
1829 //=======================================================================
1831 * \brief return TSplitMethod for the given element to split into tetrahedra
1833 //=======================================================================
1835 TSplitMethod getTetraSplitMethod( SMDS_VolumeTool& vol, const int theMethodFlags)
1837 const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
1839 // at HEXA_TO_24 method, each face of volume is split into triangles each based on
1840 // an edge and a face barycenter; tertaherdons are based on triangles and
1841 // a volume barycenter
1842 const bool is24TetMode = ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_24 );
1844 // Find out how adjacent volumes are split
1846 vector < list< TTriangleFacet > > triaSplitsByFace( vol.NbFaces() ); // splits of each side
1847 int hasAdjacentSplits = 0, maxTetConnSize = 0;
1848 for ( int iF = 0; iF < vol.NbFaces(); ++iF )
1850 int nbNodes = vol.NbFaceNodes( iF ) / iQ;
1851 maxTetConnSize += 4 * ( nbNodes - (is24TetMode ? 0 : 2));
1852 if ( nbNodes < 4 ) continue;
1854 list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
1855 const int* nInd = vol.GetFaceNodesIndices( iF );
1858 TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
1859 TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
1860 if ( t012.hasAdjacentVol( vol.Element() )) triaSplits.push_back( t012 );
1861 else if ( t123.hasAdjacentVol( vol.Element() )) triaSplits.push_back( t123 );
1865 int iCom = 0; // common node of triangle faces to split into
1866 for ( int iVar = 0; iVar < nbNodes; ++iVar, ++iCom )
1868 TTriangleFacet t012( nInd[ iQ * ( iCom )],
1869 nInd[ iQ * ( (iCom+1)%nbNodes )],
1870 nInd[ iQ * ( (iCom+2)%nbNodes )]);
1871 TTriangleFacet t023( nInd[ iQ * ( iCom )],
1872 nInd[ iQ * ( (iCom+2)%nbNodes )],
1873 nInd[ iQ * ( (iCom+3)%nbNodes )]);
1874 if ( t012.hasAdjacentVol( vol.Element() ) && t023.hasAdjacentVol( vol.Element() ))
1876 triaSplits.push_back( t012 );
1877 triaSplits.push_back( t023 );
1882 if ( !triaSplits.empty() )
1883 hasAdjacentSplits = true;
1886 // Among variants of split method select one compliant with adjacent volumes
1888 TSplitMethod method;
1889 if ( !vol.Element()->IsPoly() && !is24TetMode )
1891 int nbVariants = 2, nbTet = 0;
1892 const int** connVariants = 0;
1893 switch ( vol.Element()->GetEntityType() )
1895 case SMDSEntity_Hexa:
1896 case SMDSEntity_Quad_Hexa:
1897 case SMDSEntity_TriQuad_Hexa:
1898 if ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_5 )
1899 connVariants = theHexTo5, nbTet = 5;
1901 connVariants = theHexTo6, nbTet = 6, nbVariants = 4;
1903 case SMDSEntity_Pyramid:
1904 case SMDSEntity_Quad_Pyramid:
1905 connVariants = thePyraTo2; nbTet = 2;
1907 case SMDSEntity_Penta:
1908 case SMDSEntity_Quad_Penta:
1909 connVariants = thePentaTo3; nbTet = 3; nbVariants = 6;
1914 for ( int variant = 0; variant < nbVariants && method._nbSplits == 0; ++variant )
1916 // check method compliancy with adjacent tetras,
1917 // all found splits must be among facets of tetras described by this method
1918 method = TSplitMethod( nbTet, connVariants[variant] );
1919 if ( hasAdjacentSplits && method._nbSplits > 0 )
1921 bool facetCreated = true;
1922 for ( size_t iF = 0; facetCreated && iF < triaSplitsByFace.size(); ++iF )
1924 list< TTriangleFacet >::const_iterator facet = triaSplitsByFace[iF].begin();
1925 for ( ; facetCreated && facet != triaSplitsByFace[iF].end(); ++facet )
1926 facetCreated = method.hasFacet( *facet );
1928 if ( !facetCreated )
1929 method = TSplitMethod(0); // incompatible method
1933 if ( method._nbSplits < 1 )
1935 // No standard method is applicable, use a generic solution:
1936 // each facet of a volume is split into triangles and
1937 // each of triangles and a volume barycenter form a tetrahedron.
1939 const bool isHex27 = ( vol.Element()->GetEntityType() == SMDSEntity_TriQuad_Hexa );
1941 int* connectivity = new int[ maxTetConnSize + 1 ];
1942 method._connectivity = connectivity;
1943 method._ownConn = true;
1944 method._baryNode = !isHex27; // to create central node or not
1947 int baryCenInd = vol.NbNodes() - int( isHex27 );
1948 for ( int iF = 0; iF < vol.NbFaces(); ++iF )
1950 const int nbNodes = vol.NbFaceNodes( iF ) / iQ;
1951 const int* nInd = vol.GetFaceNodesIndices( iF );
1952 // find common node of triangle facets of tetra to create
1953 int iCommon = 0; // index in linear numeration
1954 const list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
1955 if ( !triaSplits.empty() )
1958 const TTriangleFacet* facet = &triaSplits.front();
1959 for ( ; iCommon < nbNodes-1 ; ++iCommon )
1960 if ( facet->contains( nInd[ iQ * iCommon ]) &&
1961 facet->contains( nInd[ iQ * ((iCommon+2)%nbNodes) ]))
1964 else if ( nbNodes > 3 && !is24TetMode )
1966 // find the best method of splitting into triangles by aspect ratio
1967 SMESH::Controls::NumericalFunctorPtr aspectRatio( new SMESH::Controls::AspectRatio);
1968 map< double, int > badness2iCommon;
1969 const SMDS_MeshNode** nodes = vol.GetFaceNodes( iF );
1970 int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
1971 for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCommon )
1974 for ( int iLast = iCommon+2; iLast < iCommon+nbNodes; ++iLast )
1976 SMDS_FaceOfNodes tria ( nodes[ iQ*( iCommon )],
1977 nodes[ iQ*((iLast-1)%nbNodes)],
1978 nodes[ iQ*((iLast )%nbNodes)]);
1979 badness += getBadRate( &tria, aspectRatio );
1981 badness2iCommon.insert( make_pair( badness, iCommon ));
1983 // use iCommon with lowest badness
1984 iCommon = badness2iCommon.begin()->second;
1986 if ( iCommon >= nbNodes )
1987 iCommon = 0; // something wrong
1989 // fill connectivity of tetrahedra based on a current face
1990 int nbTet = nbNodes - 2;
1991 if ( is24TetMode && nbNodes > 3 && triaSplits.empty())
1996 faceBaryCenInd = vol.GetCenterNodeIndex( iF );
1997 method._faceBaryNode[ iF ] = vol.GetNodes()[ faceBaryCenInd ];
2001 method._faceBaryNode[ iF ] = 0;
2002 faceBaryCenInd = baryCenInd + method._faceBaryNode.size();
2005 for ( int i = 0; i < nbTet; ++i )
2007 int i1 = i, i2 = (i+1) % nbNodes;
2008 if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
2009 connectivity[ connSize++ ] = nInd[ iQ * i1 ];
2010 connectivity[ connSize++ ] = nInd[ iQ * i2 ];
2011 connectivity[ connSize++ ] = faceBaryCenInd;
2012 connectivity[ connSize++ ] = baryCenInd;
2017 for ( int i = 0; i < nbTet; ++i )
2019 int i1 = (iCommon+1+i) % nbNodes, i2 = (iCommon+2+i) % nbNodes;
2020 if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
2021 connectivity[ connSize++ ] = nInd[ iQ * iCommon ];
2022 connectivity[ connSize++ ] = nInd[ iQ * i1 ];
2023 connectivity[ connSize++ ] = nInd[ iQ * i2 ];
2024 connectivity[ connSize++ ] = baryCenInd;
2027 method._nbSplits += nbTet;
2029 } // loop on volume faces
2031 connectivity[ connSize++ ] = -1;
2033 } // end of generic solution
2037 //=======================================================================
2039 * \brief return TSplitMethod to split haxhedron into prisms
2041 //=======================================================================
2043 TSplitMethod getPrismSplitMethod( SMDS_VolumeTool& vol,
2044 const int methodFlags,
2045 const int facetToSplit)
2047 // order of facets in HEX according to SMDS_VolumeTool::Hexa_F :
2049 const int iF = ( facetToSplit < 2 ) ? 0 : 1 + ( facetToSplit-2 ) % 2; // [0,1,2]
2051 if ( methodFlags == SMESH_MeshEditor::HEXA_TO_4_PRISMS )
2053 static TSplitMethod to4methods[4]; // order BT, LR, FB
2054 if ( to4methods[iF]._nbSplits == 0 )
2058 to4methods[iF]._connectivity = theHexTo4Prisms_BT;
2059 to4methods[iF]._faceBaryNode[ 0 ] = 0;
2060 to4methods[iF]._faceBaryNode[ 1 ] = 0;
2063 to4methods[iF]._connectivity = theHexTo4Prisms_LR;
2064 to4methods[iF]._faceBaryNode[ 2 ] = 0;
2065 to4methods[iF]._faceBaryNode[ 4 ] = 0;
2068 to4methods[iF]._connectivity = theHexTo4Prisms_FB;
2069 to4methods[iF]._faceBaryNode[ 3 ] = 0;
2070 to4methods[iF]._faceBaryNode[ 5 ] = 0;
2072 default: return to4methods[3];
2074 to4methods[iF]._nbSplits = 4;
2075 to4methods[iF]._nbCorners = 6;
2077 return to4methods[iF];
2079 // else if ( methodFlags == HEXA_TO_2_PRISMS )
2081 TSplitMethod method;
2083 const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
2085 const int nbVariants = 2, nbSplits = 2;
2086 const int** connVariants = 0;
2088 case 0: connVariants = theHexTo2Prisms_BT; break;
2089 case 1: connVariants = theHexTo2Prisms_LR; break;
2090 case 2: connVariants = theHexTo2Prisms_FB; break;
2091 default: return method;
2094 // look for prisms adjacent via facetToSplit and an opposite one
2095 for ( int is2nd = 0; is2nd < 2; ++is2nd )
2097 int iFacet = is2nd ? vol.GetOppFaceIndexOfHex( facetToSplit ) : facetToSplit;
2098 int nbNodes = vol.NbFaceNodes( iFacet ) / iQ;
2099 if ( nbNodes != 4 ) return method;
2101 const int* nInd = vol.GetFaceNodesIndices( iFacet );
2102 TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
2103 TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
2105 if ( t012.hasAdjacentVol( vol.Element(), SMDSGeom_PENTA ))
2107 else if ( t123.hasAdjacentVol( vol.Element(), SMDSGeom_PENTA ))
2112 // there are adjacent prism
2113 for ( int variant = 0; variant < nbVariants; ++variant )
2115 // check method compliancy with adjacent prisms,
2116 // the found prism facets must be among facets of prisms described by current method
2117 method._nbSplits = nbSplits;
2118 method._nbCorners = 6;
2119 method._connectivity = connVariants[ variant ];
2120 if ( method.hasFacet( *t ))
2125 // No adjacent prisms. Select a variant with a best aspect ratio.
2127 double badness[2] = { 0, 0 };
2128 static SMESH::Controls::NumericalFunctorPtr aspectRatio( new SMESH::Controls::AspectRatio);
2129 const SMDS_MeshNode** nodes = vol.GetNodes();
2130 for ( int variant = 0; variant < nbVariants; ++variant )
2131 for ( int is2nd = 0; is2nd < 2; ++is2nd )
2133 int iFacet = is2nd ? vol.GetOppFaceIndexOfHex( facetToSplit ) : facetToSplit;
2134 const int* nInd = vol.GetFaceNodesIndices( iFacet );
2136 method._connectivity = connVariants[ variant ];
2137 TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
2138 TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
2139 TTriangleFacet* t = ( method.hasFacet( t012 )) ? & t012 : & t123;
2141 SMDS_FaceOfNodes tria ( nodes[ t->_n1 ],
2144 badness[ variant ] += getBadRate( &tria, aspectRatio );
2146 const int iBetter = ( badness[1] < badness[0] && badness[0]-badness[1] > 0.1 * badness[0] );
2148 method._nbSplits = nbSplits;
2149 method._nbCorners = 6;
2150 method._connectivity = connVariants[ iBetter ];
2155 //================================================================================
2157 * \brief Check if there is a tetraherdon adjacent to the given element via this facet
2159 //================================================================================
2161 bool TTriangleFacet::hasAdjacentVol( const SMDS_MeshElement* elem,
2162 const SMDSAbs_GeometryType geom ) const
2164 // find the tetrahedron including the three nodes of facet
2165 const SMDS_MeshNode* n1 = elem->GetNode(_n1);
2166 const SMDS_MeshNode* n2 = elem->GetNode(_n2);
2167 const SMDS_MeshNode* n3 = elem->GetNode(_n3);
2168 SMDS_ElemIteratorPtr volIt1 = n1->GetInverseElementIterator(SMDSAbs_Volume);
2169 while ( volIt1->more() )
2171 const SMDS_MeshElement* v = volIt1->next();
2172 if ( v->GetGeomType() != geom )
2174 const int lastCornerInd = v->NbCornerNodes() - 1;
2175 if ( v->IsQuadratic() && v->GetNodeIndex( n1 ) > lastCornerInd )
2176 continue; // medium node not allowed
2177 const int ind2 = v->GetNodeIndex( n2 );
2178 if ( ind2 < 0 || lastCornerInd < ind2 )
2180 const int ind3 = v->GetNodeIndex( n3 );
2181 if ( ind3 < 0 || lastCornerInd < ind3 )
2188 //=======================================================================
2190 * \brief A key of a face of volume
2192 //=======================================================================
2194 struct TVolumeFaceKey: pair< pair< int, int>, pair< int, int> >
2196 TVolumeFaceKey( SMDS_VolumeTool& vol, int iF )
2198 TIDSortedNodeSet sortedNodes;
2199 const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
2200 int nbNodes = vol.NbFaceNodes( iF );
2201 const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iF );
2202 for ( int i = 0; i < nbNodes; i += iQ )
2203 sortedNodes.insert( fNodes[i] );
2204 TIDSortedNodeSet::iterator n = sortedNodes.begin();
2205 first.first = (*(n++))->GetID();
2206 first.second = (*(n++))->GetID();
2207 second.first = (*(n++))->GetID();
2208 second.second = ( sortedNodes.size() > 3 ) ? (*(n++))->GetID() : 0;
2213 //=======================================================================
2214 //function : SplitVolumes
2215 //purpose : Split volume elements into tetrahedra or prisms.
2216 // If facet ID < 0, element is split into tetrahedra,
2217 // else a hexahedron is split into prisms so that the given facet is
2218 // split into triangles
2219 //=======================================================================
2221 void SMESH_MeshEditor::SplitVolumes (const TFacetOfElem & theElems,
2222 const int theMethodFlags)
2224 SMDS_VolumeTool volTool;
2225 SMESH_MesherHelper helper( *GetMesh()), fHelper(*GetMesh());
2226 fHelper.ToFixNodeParameters( true );
2228 SMESHDS_SubMesh* subMesh = 0;//GetMeshDS()->MeshElements(1);
2229 SMESHDS_SubMesh* fSubMesh = 0;//subMesh;
2231 SMESH_SequenceOfElemPtr newNodes, newElems;
2233 // map face of volume to it's baricenrtic node
2234 map< TVolumeFaceKey, const SMDS_MeshNode* > volFace2BaryNode;
2236 vector<const SMDS_MeshElement* > splitVols;
2238 TFacetOfElem::const_iterator elem2facet = theElems.begin();
2239 for ( ; elem2facet != theElems.end(); ++elem2facet )
2241 const SMDS_MeshElement* elem = elem2facet->first;
2242 const int facetToSplit = elem2facet->second;
2243 if ( elem->GetType() != SMDSAbs_Volume )
2245 const SMDSAbs_EntityType geomType = elem->GetEntityType();
2246 if ( geomType == SMDSEntity_Tetra || geomType == SMDSEntity_Quad_Tetra )
2249 if ( !volTool.Set( elem, /*ignoreCentralNodes=*/false )) continue; // strange...
2251 TSplitMethod splitMethod = ( facetToSplit < 0 ?
2252 getTetraSplitMethod( volTool, theMethodFlags ) :
2253 getPrismSplitMethod( volTool, theMethodFlags, facetToSplit ));
2254 if ( splitMethod._nbSplits < 1 ) continue;
2256 // find submesh to add new tetras to
2257 if ( !subMesh || !subMesh->Contains( elem ))
2259 int shapeID = FindShape( elem );
2260 helper.SetSubShape( shapeID ); // helper will add tetras to the found submesh
2261 subMesh = GetMeshDS()->MeshElements( shapeID );
2264 if ( elem->IsQuadratic() )
2267 // add quadratic links to the helper
2268 for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
2270 const SMDS_MeshNode** fNodes = volTool.GetFaceNodes( iF );
2271 int nbN = volTool.NbFaceNodes( iF ) - bool( volTool.GetCenterNodeIndex(iF) > 0 );
2272 for ( int iN = 0; iN < nbN; iN += iQ )
2273 helper.AddTLinkNode( fNodes[iN], fNodes[iN+2], fNodes[iN+1] );
2275 helper.SetIsQuadratic( true );
2280 helper.SetIsQuadratic( false );
2282 vector<const SMDS_MeshNode*> nodes( volTool.GetNodes(),
2283 volTool.GetNodes() + elem->NbNodes() );
2284 helper.SetElementsOnShape( true );
2285 if ( splitMethod._baryNode )
2287 // make a node at barycenter
2288 volTool.GetBaryCenter( bc[0], bc[1], bc[2] );
2289 SMDS_MeshNode* gcNode = helper.AddNode( bc[0], bc[1], bc[2] );
2290 nodes.push_back( gcNode );
2291 newNodes.Append( gcNode );
2293 if ( !splitMethod._faceBaryNode.empty() )
2295 // make or find baricentric nodes of faces
2296 map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.begin();
2297 for ( ; iF_n != splitMethod._faceBaryNode.end(); ++iF_n )
2299 map< TVolumeFaceKey, const SMDS_MeshNode* >::iterator f_n =
2300 volFace2BaryNode.insert
2301 ( make_pair( TVolumeFaceKey( volTool,iF_n->first ), iF_n->second )).first;
2304 volTool.GetFaceBaryCenter( iF_n->first, bc[0], bc[1], bc[2] );
2305 newNodes.Append( f_n->second = helper.AddNode( bc[0], bc[1], bc[2] ));
2307 nodes.push_back( iF_n->second = f_n->second );
2312 splitVols.resize( splitMethod._nbSplits ); // splits of a volume
2313 const int* volConn = splitMethod._connectivity;
2314 if ( splitMethod._nbCorners == 4 ) // tetra
2315 for ( int i = 0; i < splitMethod._nbSplits; ++i, volConn += splitMethod._nbCorners )
2316 newElems.Append( splitVols[ i ] = helper.AddVolume( nodes[ volConn[0] ],
2317 nodes[ volConn[1] ],
2318 nodes[ volConn[2] ],
2319 nodes[ volConn[3] ]));
2321 for ( int i = 0; i < splitMethod._nbSplits; ++i, volConn += splitMethod._nbCorners )
2322 newElems.Append( splitVols[ i ] = helper.AddVolume( nodes[ volConn[0] ],
2323 nodes[ volConn[1] ],
2324 nodes[ volConn[2] ],
2325 nodes[ volConn[3] ],
2326 nodes[ volConn[4] ],
2327 nodes[ volConn[5] ]));
2329 ReplaceElemInGroups( elem, splitVols, GetMeshDS() );
2331 // Split faces on sides of the split volume
2333 const SMDS_MeshNode** volNodes = volTool.GetNodes();
2334 for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
2336 const int nbNodes = volTool.NbFaceNodes( iF ) / iQ;
2337 if ( nbNodes < 4 ) continue;
2339 // find an existing face
2340 vector<const SMDS_MeshNode*> fNodes( volTool.GetFaceNodes( iF ),
2341 volTool.GetFaceNodes( iF ) + volTool.NbFaceNodes( iF ));
2342 while ( const SMDS_MeshElement* face = GetMeshDS()->FindElement( fNodes, SMDSAbs_Face,
2343 /*noMedium=*/false))
2346 helper.SetElementsOnShape( false );
2347 vector< const SMDS_MeshElement* > triangles;
2349 // find submesh to add new triangles in
2350 if ( !fSubMesh || !fSubMesh->Contains( face ))
2352 int shapeID = FindShape( face );
2353 fSubMesh = GetMeshDS()->MeshElements( shapeID );
2355 map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.find(iF);
2356 if ( iF_n != splitMethod._faceBaryNode.end() )
2358 const SMDS_MeshNode *baryNode = iF_n->second;
2359 for ( int iN = 0; iN < nbNodes*iQ; iN += iQ )
2361 const SMDS_MeshNode* n1 = fNodes[iN];
2362 const SMDS_MeshNode *n2 = fNodes[(iN+iQ)%(nbNodes*iQ)];
2363 const SMDS_MeshNode *n3 = baryNode;
2364 if ( !volTool.IsFaceExternal( iF ))
2366 triangles.push_back( helper.AddFace( n1,n2,n3 ));
2368 if ( fSubMesh ) // update position of the bary node on geometry
2371 subMesh->RemoveNode( baryNode, false );
2372 GetMeshDS()->SetNodeOnFace( baryNode, fSubMesh->GetID() );
2373 const TopoDS_Shape& s = GetMeshDS()->IndexToShape( fSubMesh->GetID() );
2374 if ( !s.IsNull() && s.ShapeType() == TopAbs_FACE )
2376 fHelper.SetSubShape( s );
2377 gp_XY uv( 1e100, 1e100 );
2379 if ( !fHelper.CheckNodeUV( TopoDS::Face( s ), baryNode,
2380 uv, /*tol=*/1e-7, /*force=*/true, distXYZ ) &&
2383 // node is too far from the surface
2384 GetMeshDS()->MoveNode( baryNode, distXYZ[1], distXYZ[2], distXYZ[3] );
2385 const_cast<SMDS_MeshNode*>( baryNode )->SetPosition
2386 ( SMDS_PositionPtr( new SMDS_FacePosition( uv.X(), uv.Y() )));
2393 // among possible triangles create ones discribed by split method
2394 const int* nInd = volTool.GetFaceNodesIndices( iF );
2395 int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
2396 int iCom = 0; // common node of triangle faces to split into
2397 list< TTriangleFacet > facets;
2398 for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCom )
2400 TTriangleFacet t012( nInd[ iQ * ( iCom )],
2401 nInd[ iQ * ( (iCom+1)%nbNodes )],
2402 nInd[ iQ * ( (iCom+2)%nbNodes )]);
2403 TTriangleFacet t023( nInd[ iQ * ( iCom )],
2404 nInd[ iQ * ( (iCom+2)%nbNodes )],
2405 nInd[ iQ * ( (iCom+3)%nbNodes )]);
2406 if ( splitMethod.hasFacet( t012 ) && splitMethod.hasFacet( t023 ))
2408 facets.push_back( t012 );
2409 facets.push_back( t023 );
2410 for ( int iLast = iCom+4; iLast < iCom+nbNodes; ++iLast )
2411 facets.push_back( TTriangleFacet( nInd[ iQ * ( iCom )],
2412 nInd[ iQ * ((iLast-1)%nbNodes )],
2413 nInd[ iQ * ((iLast )%nbNodes )]));
2417 list< TTriangleFacet >::iterator facet = facets.begin();
2418 if ( facet == facets.end() )
2420 for ( ; facet != facets.end(); ++facet )
2422 if ( !volTool.IsFaceExternal( iF ))
2423 swap( facet->_n2, facet->_n3 );
2424 triangles.push_back( helper.AddFace( volNodes[ facet->_n1 ],
2425 volNodes[ facet->_n2 ],
2426 volNodes[ facet->_n3 ]));
2429 for ( size_t i = 0; i < triangles.size(); ++i )
2431 if ( !triangles[ i ]) continue;
2433 fSubMesh->AddElement( triangles[ i ]);
2434 newElems.Append( triangles[ i ]);
2436 ReplaceElemInGroups( face, triangles, GetMeshDS() );
2437 GetMeshDS()->RemoveFreeElement( face, fSubMesh, /*fromGroups=*/false );
2439 } // while a face based on facet nodes exists
2440 } // loop on volume faces to split them into triangles
2442 GetMeshDS()->RemoveFreeElement( elem, subMesh, /*fromGroups=*/false );
2444 if ( geomType == SMDSEntity_TriQuad_Hexa )
2446 // remove medium nodes that could become free
2447 for ( int i = 20; i < volTool.NbNodes(); ++i )
2448 if ( volNodes[i]->NbInverseElements() == 0 )
2449 GetMeshDS()->RemoveNode( volNodes[i] );
2451 } // loop on volumes to split
2453 myLastCreatedNodes = newNodes;
2454 myLastCreatedElems = newElems;
2457 //=======================================================================
2458 //function : GetHexaFacetsToSplit
2459 //purpose : For hexahedra that will be split into prisms, finds facets to
2460 // split into triangles. Only hexahedra adjacent to the one closest
2461 // to theFacetNormal.Location() are returned.
2462 //param [in,out] theHexas - the hexahedra
2463 //param [in] theFacetNormal - facet normal
2464 //param [out] theFacets - the hexahedra and found facet IDs
2465 //=======================================================================
2467 void SMESH_MeshEditor::GetHexaFacetsToSplit( TIDSortedElemSet& theHexas,
2468 const gp_Ax1& theFacetNormal,
2469 TFacetOfElem & theFacets)
2471 #define THIS_METHOD "SMESH_MeshEditor::GetHexaFacetsToSplit(): "
2473 // Find a hexa closest to the location of theFacetNormal
2475 const SMDS_MeshElement* startHex;
2477 // get SMDS_ElemIteratorPtr on theHexas
2478 typedef const SMDS_MeshElement* TValue;
2479 typedef TIDSortedElemSet::iterator TSetIterator;
2480 typedef SMDS::SimpleAccessor<TValue,TSetIterator> TAccesor;
2481 typedef SMDS_MeshElement::GeomFilter TFilter;
2482 typedef SMDS_SetIterator < TValue, TSetIterator, TAccesor, TFilter > TElemSetIter;
2483 SMDS_ElemIteratorPtr elemIt = SMDS_ElemIteratorPtr
2484 ( new TElemSetIter( theHexas.begin(),
2486 SMDS_MeshElement::GeomFilter( SMDSGeom_HEXA )));
2488 SMESH_ElementSearcher* searcher =
2489 SMESH_MeshAlgos::GetElementSearcher( *myMesh->GetMeshDS(), elemIt );
2491 startHex = searcher->FindClosestTo( theFacetNormal.Location(), SMDSAbs_Volume );
2496 throw SALOME_Exception( THIS_METHOD "startHex not found");
2499 // Select a facet of startHex by theFacetNormal
2501 SMDS_VolumeTool vTool( startHex );
2502 double norm[3], dot, maxDot = 0;
2504 for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2505 if ( vTool.GetFaceNormal( iF, norm[0], norm[1], norm[2] ))
2507 dot = Abs( theFacetNormal.Direction().Dot( gp_Dir( norm[0], norm[1], norm[2] )));
2515 throw SALOME_Exception( THIS_METHOD "facet of startHex not found");
2517 // Fill theFacets starting from facetID of startHex
2519 // facets used for seach of volumes adjacent to already treated ones
2520 typedef pair< TFacetOfElem::iterator, int > TElemFacets;
2521 typedef map< TVolumeFaceKey, TElemFacets > TFacetMap;
2522 TFacetMap facetsToCheck;
2524 set<const SMDS_MeshNode*> facetNodes;
2525 const SMDS_MeshElement* curHex;
2527 const bool allHex = ((int) theHexas.size() == myMesh->NbHexas() );
2531 // move in two directions from startHex via facetID
2532 for ( int is2nd = 0; is2nd < 2; ++is2nd )
2535 int curFacet = facetID;
2536 if ( is2nd ) // do not treat startHex twice
2538 vTool.Set( curHex );
2539 if ( vTool.IsFreeFace( curFacet, &curHex ))
2545 vTool.GetFaceNodes( curFacet, facetNodes );
2546 vTool.Set( curHex );
2547 curFacet = vTool.GetFaceIndex( facetNodes );
2552 // store a facet to split
2553 if ( curHex->GetGeomType() != SMDSGeom_HEXA )
2555 theFacets.insert( make_pair( curHex, -1 ));
2558 if ( !allHex && !theHexas.count( curHex ))
2561 pair< TFacetOfElem::iterator, bool > facetIt2isNew =
2562 theFacets.insert( make_pair( curHex, curFacet ));
2563 if ( !facetIt2isNew.second )
2566 // remember not-to-split facets in facetsToCheck
2567 int oppFacet = vTool.GetOppFaceIndexOfHex( curFacet );
2568 for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2570 if ( iF == curFacet && iF == oppFacet )
2572 TVolumeFaceKey facetKey ( vTool, iF );
2573 TElemFacets elemFacet( facetIt2isNew.first, iF );
2574 pair< TFacetMap::iterator, bool > it2isnew =
2575 facetsToCheck.insert( make_pair( facetKey, elemFacet ));
2576 if ( !it2isnew.second )
2577 facetsToCheck.erase( it2isnew.first ); // adjacent hex already checked
2579 // pass to a volume adjacent via oppFacet
2580 if ( vTool.IsFreeFace( oppFacet, &curHex ))
2586 // get a new curFacet
2587 vTool.GetFaceNodes( oppFacet, facetNodes );
2588 vTool.Set( curHex );
2589 curFacet = vTool.GetFaceIndex( facetNodes, /*hint=*/curFacet );
2592 } // move in two directions from startHex via facetID
2594 // Find a new startHex by facetsToCheck
2598 TFacetMap::iterator fIt = facetsToCheck.begin();
2599 while ( !startHex && fIt != facetsToCheck.end() )
2601 const TElemFacets& elemFacets = fIt->second;
2602 const SMDS_MeshElement* hex = elemFacets.first->first;
2603 int splitFacet = elemFacets.first->second;
2604 int lateralFacet = elemFacets.second;
2605 facetsToCheck.erase( fIt );
2606 fIt = facetsToCheck.begin();
2609 if ( vTool.IsFreeFace( lateralFacet, &curHex ) ||
2610 curHex->GetGeomType() != SMDSGeom_HEXA )
2612 if ( !allHex && !theHexas.count( curHex ))
2617 // find a facet of startHex to split
2619 set<const SMDS_MeshNode*> lateralNodes;
2620 vTool.GetFaceNodes( lateralFacet, lateralNodes );
2621 vTool.GetFaceNodes( splitFacet, facetNodes );
2622 int oppLateralFacet = vTool.GetOppFaceIndexOfHex( lateralFacet );
2623 vTool.Set( startHex );
2624 lateralFacet = vTool.GetFaceIndex( lateralNodes, oppLateralFacet );
2626 // look for a facet of startHex having common nodes with facetNodes
2627 // but not lateralFacet
2628 for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2630 if ( iF == lateralFacet )
2632 int nbCommonNodes = 0;
2633 const SMDS_MeshNode** nn = vTool.GetFaceNodes( iF );
2634 for ( int iN = 0, nbN = vTool.NbFaceNodes( iF ); iN < nbN; ++iN )
2635 nbCommonNodes += facetNodes.count( nn[ iN ]);
2637 if ( nbCommonNodes >= 2 )
2644 throw SALOME_Exception( THIS_METHOD "facet of a new startHex not found");
2646 } // while ( startHex )
2653 //================================================================================
2655 * \brief Selects nodes of several elements according to a given interlace
2656 * \param [in] srcNodes - nodes to select from
2657 * \param [out] tgtNodesVec - array of nodes of several elements to fill in
2658 * \param [in] interlace - indices of nodes for all elements
2659 * \param [in] nbElems - nb of elements
2660 * \param [in] nbNodes - nb of nodes in each element
2661 * \param [in] mesh - the mesh
2662 * \param [out] elemQueue - a list to push elements found by the selected nodes
2663 * \param [in] type - type of elements to look for
2665 //================================================================================
2667 void selectNodes( const vector< const SMDS_MeshNode* >& srcNodes,
2668 vector< const SMDS_MeshNode* >* tgtNodesVec,
2669 const int* interlace,
2672 SMESHDS_Mesh* mesh = 0,
2673 list< const SMDS_MeshElement* >* elemQueue=0,
2674 SMDSAbs_ElementType type=SMDSAbs_All)
2676 for ( int iE = 0; iE < nbElems; ++iE )
2678 vector< const SMDS_MeshNode* >& elemNodes = tgtNodesVec[iE];
2679 const int* select = & interlace[iE*nbNodes];
2680 elemNodes.resize( nbNodes );
2681 for ( int iN = 0; iN < nbNodes; ++iN )
2682 elemNodes[iN] = srcNodes[ select[ iN ]];
2684 const SMDS_MeshElement* e;
2686 for ( int iE = 0; iE < nbElems; ++iE )
2687 if (( e = mesh->FindElement( tgtNodesVec[iE], type, /*noMedium=*/false)))
2688 elemQueue->push_back( e );
2692 //=======================================================================
2694 * Split bi-quadratic elements into linear ones without creation of additional nodes
2695 * - bi-quadratic triangle will be split into 3 linear quadrangles;
2696 * - bi-quadratic quadrangle will be split into 4 linear quadrangles;
2697 * - tri-quadratic hexahedron will be split into 8 linear hexahedra;
2698 * Quadratic elements of lower dimension adjacent to the split bi-quadratic element
2699 * will be split in order to keep the mesh conformal.
2700 * \param elems - elements to split
2702 //=======================================================================
2704 void SMESH_MeshEditor::SplitBiQuadraticIntoLinear(TIDSortedElemSet& theElems)
2706 vector< const SMDS_MeshNode* > elemNodes(27), subNodes[12], splitNodes[8];
2707 vector<const SMDS_MeshElement* > splitElems;
2708 list< const SMDS_MeshElement* > elemQueue;
2709 list< const SMDS_MeshElement* >::iterator elemIt;
2711 SMESHDS_Mesh * mesh = GetMeshDS();
2712 ElemFeatures *elemType, hexaType(SMDSAbs_Volume), quadType(SMDSAbs_Face), segType(SMDSAbs_Edge);
2713 int nbElems, nbNodes;
2715 TIDSortedElemSet::iterator elemSetIt = theElems.begin();
2716 for ( ; elemSetIt != theElems.end(); ++elemSetIt )
2719 elemQueue.push_back( *elemSetIt );
2720 for ( elemIt = elemQueue.begin(); elemIt != elemQueue.end(); ++elemIt )
2722 const SMDS_MeshElement* elem = *elemIt;
2723 switch( elem->GetEntityType() )
2725 case SMDSEntity_TriQuad_Hexa: // HEX27
2727 elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2728 nbElems = nbNodes = 8;
2729 elemType = & hexaType;
2731 // get nodes for new elements
2732 static int vInd[8][8] = {{ 0,8,20,11, 16,21,26,24 },
2733 { 1,9,20,8, 17,22,26,21 },
2734 { 2,10,20,9, 18,23,26,22 },
2735 { 3,11,20,10, 19,24,26,23 },
2736 { 16,21,26,24, 4,12,25,15 },
2737 { 17,22,26,21, 5,13,25,12 },
2738 { 18,23,26,22, 6,14,25,13 },
2739 { 19,24,26,23, 7,15,25,14 }};
2740 selectNodes( elemNodes, & splitNodes[0], &vInd[0][0], nbElems, nbNodes );
2742 // add boundary faces to elemQueue
2743 static int fInd[6][9] = {{ 0,1,2,3, 8,9,10,11, 20 },
2744 { 4,5,6,7, 12,13,14,15, 25 },
2745 { 0,1,5,4, 8,17,12,16, 21 },
2746 { 1,2,6,5, 9,18,13,17, 22 },
2747 { 2,3,7,6, 10,19,14,18, 23 },
2748 { 3,0,4,7, 11,16,15,19, 24 }};
2749 selectNodes( elemNodes, & subNodes[0], &fInd[0][0], 6,9, mesh, &elemQueue, SMDSAbs_Face );
2751 // add boundary segments to elemQueue
2752 static int eInd[12][3] = {{ 0,1,8 }, { 1,2,9 }, { 2,3,10 }, { 3,0,11 },
2753 { 4,5,12}, { 5,6,13}, { 6,7,14 }, { 7,4,15 },
2754 { 0,4,16}, { 1,5,17}, { 2,6,18 }, { 3,7,19 }};
2755 selectNodes( elemNodes, & subNodes[0], &eInd[0][0], 12,3, mesh, &elemQueue, SMDSAbs_Edge );
2758 case SMDSEntity_BiQuad_Triangle: // TRIA7
2760 elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2763 elemType = & quadType;
2765 // get nodes for new elements
2766 static int fInd[3][4] = {{ 0,3,6,5 }, { 1,4,6,3 }, { 2,5,6,4 }};
2767 selectNodes( elemNodes, & splitNodes[0], &fInd[0][0], nbElems, nbNodes );
2769 // add boundary segments to elemQueue
2770 static int eInd[3][3] = {{ 0,1,3 }, { 1,2,4 }, { 2,0,5 }};
2771 selectNodes( elemNodes, & subNodes[0], &eInd[0][0], 3,3, mesh, &elemQueue, SMDSAbs_Edge );
2774 case SMDSEntity_BiQuad_Quadrangle: // QUAD9
2776 elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2779 elemType = & quadType;
2781 // get nodes for new elements
2782 static int fInd[4][4] = {{ 0,4,8,7 }, { 1,5,8,4 }, { 2,6,8,5 }, { 3,7,8,6 }};
2783 selectNodes( elemNodes, & splitNodes[0], &fInd[0][0], nbElems, nbNodes );
2785 // add boundary segments to elemQueue
2786 static int eInd[4][3] = {{ 0,1,4 }, { 1,2,5 }, { 2,3,6 }, { 3,0,7 }};
2787 selectNodes( elemNodes, & subNodes[0], &eInd[0][0], 4,3, mesh, &elemQueue, SMDSAbs_Edge );
2790 case SMDSEntity_Quad_Edge:
2792 if ( elemIt == elemQueue.begin() )
2793 continue; // an elem is in theElems
2794 elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2797 elemType = & segType;
2799 // get nodes for new elements
2800 static int eInd[2][2] = {{ 0,2 }, { 2,1 }};
2801 selectNodes( elemNodes, & splitNodes[0], &eInd[0][0], nbElems, nbNodes );
2805 } // switch( elem->GetEntityType() )
2807 // Create new elements
2809 SMESHDS_SubMesh* subMesh = mesh->MeshElements( elem->getshapeId() );
2813 //elemType->SetID( elem->GetID() ); // create an elem with the same ID as a removed one
2814 mesh->RemoveFreeElement( elem, subMesh, /*fromGroups=*/false );
2815 //splitElems.push_back( AddElement( splitNodes[ 0 ], *elemType ));
2816 //elemType->SetID( -1 );
2818 for ( int iE = 0; iE < nbElems; ++iE )
2819 splitElems.push_back( AddElement( splitNodes[ iE ], *elemType ));
2822 ReplaceElemInGroups( elem, splitElems, mesh );
2825 for ( size_t i = 0; i < splitElems.size(); ++i )
2826 subMesh->AddElement( splitElems[i] );
2831 //=======================================================================
2832 //function : AddToSameGroups
2833 //purpose : add elemToAdd to the groups the elemInGroups belongs to
2834 //=======================================================================
2836 void SMESH_MeshEditor::AddToSameGroups (const SMDS_MeshElement* elemToAdd,
2837 const SMDS_MeshElement* elemInGroups,
2838 SMESHDS_Mesh * aMesh)
2840 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2841 if (!groups.empty()) {
2842 set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2843 for ( ; grIt != groups.end(); grIt++ ) {
2844 SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2845 if ( group && group->Contains( elemInGroups ))
2846 group->SMDSGroup().Add( elemToAdd );
2852 //=======================================================================
2853 //function : RemoveElemFromGroups
2854 //purpose : Remove removeelem to the groups the elemInGroups belongs to
2855 //=======================================================================
2856 void SMESH_MeshEditor::RemoveElemFromGroups (const SMDS_MeshElement* removeelem,
2857 SMESHDS_Mesh * aMesh)
2859 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2860 if (!groups.empty())
2862 set<SMESHDS_GroupBase*>::const_iterator GrIt = groups.begin();
2863 for (; GrIt != groups.end(); GrIt++)
2865 SMESHDS_Group* grp = dynamic_cast<SMESHDS_Group*>(*GrIt);
2866 if (!grp || grp->IsEmpty()) continue;
2867 grp->SMDSGroup().Remove(removeelem);
2872 //================================================================================
2874 * \brief Replace elemToRm by elemToAdd in the all groups
2876 //================================================================================
2878 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement* elemToRm,
2879 const SMDS_MeshElement* elemToAdd,
2880 SMESHDS_Mesh * aMesh)
2882 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2883 if (!groups.empty()) {
2884 set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2885 for ( ; grIt != groups.end(); grIt++ ) {
2886 SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2887 if ( group && group->SMDSGroup().Remove( elemToRm ) && elemToAdd )
2888 group->SMDSGroup().Add( elemToAdd );
2893 //================================================================================
2895 * \brief Replace elemToRm by elemToAdd in the all groups
2897 //================================================================================
2899 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement* elemToRm,
2900 const vector<const SMDS_MeshElement*>& elemToAdd,
2901 SMESHDS_Mesh * aMesh)
2903 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2904 if (!groups.empty())
2906 set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2907 for ( ; grIt != groups.end(); grIt++ ) {
2908 SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2909 if ( group && group->SMDSGroup().Remove( elemToRm ) )
2910 for ( size_t i = 0; i < elemToAdd.size(); ++i )
2911 group->SMDSGroup().Add( elemToAdd[ i ] );
2916 //=======================================================================
2917 //function : QuadToTri
2918 //purpose : Cut quadrangles into triangles.
2919 // theCrit is used to select a diagonal to cut
2920 //=======================================================================
2922 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet & theElems,
2923 const bool the13Diag)
2925 myLastCreatedElems.Clear();
2926 myLastCreatedNodes.Clear();
2928 SMESHDS_Mesh * aMesh = GetMeshDS();
2930 Handle(Geom_Surface) surface;
2931 SMESH_MesherHelper helper( *GetMesh() );
2933 TIDSortedElemSet::iterator itElem;
2934 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
2936 const SMDS_MeshElement* elem = *itElem;
2937 if ( !elem || elem->GetGeomType() != SMDSGeom_QUADRANGLE )
2940 if ( elem->NbNodes() == 4 ) {
2941 // retrieve element nodes
2942 const SMDS_MeshNode* aNodes [4];
2943 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
2945 while ( itN->more() )
2946 aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
2948 int aShapeId = FindShape( elem );
2949 const SMDS_MeshElement* newElem1 = 0;
2950 const SMDS_MeshElement* newElem2 = 0;
2952 newElem1 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
2953 newElem2 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
2956 newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
2957 newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
2959 myLastCreatedElems.Append(newElem1);
2960 myLastCreatedElems.Append(newElem2);
2961 // put a new triangle on the same shape and add to the same groups
2964 aMesh->SetMeshElementOnShape( newElem1, aShapeId );
2965 aMesh->SetMeshElementOnShape( newElem2, aShapeId );
2967 AddToSameGroups( newElem1, elem, aMesh );
2968 AddToSameGroups( newElem2, elem, aMesh );
2969 aMesh->RemoveElement( elem );
2972 // Quadratic quadrangle
2974 else if ( elem->NbNodes() >= 8 )
2976 // get surface elem is on
2977 int aShapeId = FindShape( elem );
2978 if ( aShapeId != helper.GetSubShapeID() ) {
2982 shape = aMesh->IndexToShape( aShapeId );
2983 if ( !shape.IsNull() && shape.ShapeType() == TopAbs_FACE ) {
2984 TopoDS_Face face = TopoDS::Face( shape );
2985 surface = BRep_Tool::Surface( face );
2986 if ( !surface.IsNull() )
2987 helper.SetSubShape( shape );
2991 const SMDS_MeshNode* aNodes [9]; aNodes[8] = 0;
2992 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
2993 for ( int i = 0; itN->more(); ++i )
2994 aNodes[ i ] = static_cast<const SMDS_MeshNode*>( itN->next() );
2996 const SMDS_MeshNode* centrNode = aNodes[8];
2997 if ( centrNode == 0 )
2999 centrNode = helper.GetCentralNode( aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3000 aNodes[4], aNodes[5], aNodes[6], aNodes[7],
3002 myLastCreatedNodes.Append(centrNode);
3005 // create a new element
3006 const SMDS_MeshElement* newElem1 = 0;
3007 const SMDS_MeshElement* newElem2 = 0;
3009 newElem1 = aMesh->AddFace(aNodes[2], aNodes[3], aNodes[0],
3010 aNodes[6], aNodes[7], centrNode );
3011 newElem2 = aMesh->AddFace(aNodes[2], aNodes[0], aNodes[1],
3012 centrNode, aNodes[4], aNodes[5] );
3015 newElem1 = aMesh->AddFace(aNodes[3], aNodes[0], aNodes[1],
3016 aNodes[7], aNodes[4], centrNode );
3017 newElem2 = aMesh->AddFace(aNodes[3], aNodes[1], aNodes[2],
3018 centrNode, aNodes[5], aNodes[6] );
3020 myLastCreatedElems.Append(newElem1);
3021 myLastCreatedElems.Append(newElem2);
3022 // put a new triangle on the same shape and add to the same groups
3025 aMesh->SetMeshElementOnShape( newElem1, aShapeId );
3026 aMesh->SetMeshElementOnShape( newElem2, aShapeId );
3028 AddToSameGroups( newElem1, elem, aMesh );
3029 AddToSameGroups( newElem2, elem, aMesh );
3030 aMesh->RemoveElement( elem );
3037 //=======================================================================
3038 //function : getAngle
3040 //=======================================================================
3042 double getAngle(const SMDS_MeshElement * tr1,
3043 const SMDS_MeshElement * tr2,
3044 const SMDS_MeshNode * n1,
3045 const SMDS_MeshNode * n2)
3047 double angle = 2. * M_PI; // bad angle
3050 SMESH::Controls::TSequenceOfXYZ P1, P2;
3051 if ( !SMESH::Controls::NumericalFunctor::GetPoints( tr1, P1 ) ||
3052 !SMESH::Controls::NumericalFunctor::GetPoints( tr2, P2 ))
3055 if(!tr1->IsQuadratic())
3056 N1 = gp_Vec( P1(2) - P1(1) ) ^ gp_Vec( P1(3) - P1(1) );
3058 N1 = gp_Vec( P1(3) - P1(1) ) ^ gp_Vec( P1(5) - P1(1) );
3059 if ( N1.SquareMagnitude() <= gp::Resolution() )
3061 if(!tr2->IsQuadratic())
3062 N2 = gp_Vec( P2(2) - P2(1) ) ^ gp_Vec( P2(3) - P2(1) );
3064 N2 = gp_Vec( P2(3) - P2(1) ) ^ gp_Vec( P2(5) - P2(1) );
3065 if ( N2.SquareMagnitude() <= gp::Resolution() )
3068 // find the first diagonal node n1 in the triangles:
3069 // take in account a diagonal link orientation
3070 const SMDS_MeshElement *nFirst[2], *tr[] = { tr1, tr2 };
3071 for ( int t = 0; t < 2; t++ ) {
3072 SMDS_ElemIteratorPtr it = tr[ t ]->nodesIterator();
3073 int i = 0, iDiag = -1;
3074 while ( it->more()) {
3075 const SMDS_MeshElement *n = it->next();
3076 if ( n == n1 || n == n2 ) {
3080 if ( i - iDiag == 1 )
3081 nFirst[ t ] = ( n == n1 ? n2 : n1 );
3090 if ( nFirst[ 0 ] == nFirst[ 1 ] )
3093 angle = N1.Angle( N2 );
3098 // =================================================
3099 // class generating a unique ID for a pair of nodes
3100 // and able to return nodes by that ID
3101 // =================================================
3105 LinkID_Gen( const SMESHDS_Mesh* theMesh )
3106 :myMesh( theMesh ), myMaxID( theMesh->MaxNodeID() + 1)
3109 long GetLinkID (const SMDS_MeshNode * n1,
3110 const SMDS_MeshNode * n2) const
3112 return ( Min(n1->GetID(),n2->GetID()) * myMaxID + Max(n1->GetID(),n2->GetID()));
3115 bool GetNodes (const long theLinkID,
3116 const SMDS_MeshNode* & theNode1,
3117 const SMDS_MeshNode* & theNode2) const
3119 theNode1 = myMesh->FindNode( theLinkID / myMaxID );
3120 if ( !theNode1 ) return false;
3121 theNode2 = myMesh->FindNode( theLinkID % myMaxID );
3122 if ( !theNode2 ) return false;
3128 const SMESHDS_Mesh* myMesh;
3133 //=======================================================================
3134 //function : TriToQuad
3135 //purpose : Fuse neighbour triangles into quadrangles.
3136 // theCrit is used to select a neighbour to fuse with.
3137 // theMaxAngle is a max angle between element normals at which
3138 // fusion is still performed.
3139 //=======================================================================
3141 bool SMESH_MeshEditor::TriToQuad (TIDSortedElemSet & theElems,
3142 SMESH::Controls::NumericalFunctorPtr theCrit,
3143 const double theMaxAngle)
3145 myLastCreatedElems.Clear();
3146 myLastCreatedNodes.Clear();
3148 if ( !theCrit.get() )
3151 SMESHDS_Mesh * aMesh = GetMeshDS();
3153 // Prepare data for algo: build
3154 // 1. map of elements with their linkIDs
3155 // 2. map of linkIDs with their elements
3157 map< SMESH_TLink, list< const SMDS_MeshElement* > > mapLi_listEl;
3158 map< SMESH_TLink, list< const SMDS_MeshElement* > >::iterator itLE;
3159 map< const SMDS_MeshElement*, set< SMESH_TLink > > mapEl_setLi;
3160 map< const SMDS_MeshElement*, set< SMESH_TLink > >::iterator itEL;
3162 TIDSortedElemSet::iterator itElem;
3163 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
3165 const SMDS_MeshElement* elem = *itElem;
3166 if(!elem || elem->GetType() != SMDSAbs_Face ) continue;
3167 bool IsTria = ( elem->NbCornerNodes()==3 );
3168 if (!IsTria) continue;
3170 // retrieve element nodes
3171 const SMDS_MeshNode* aNodes [4];
3172 SMDS_NodeIteratorPtr itN = elem->nodeIterator();
3175 aNodes[ i++ ] = itN->next();
3176 aNodes[ 3 ] = aNodes[ 0 ];
3179 for ( i = 0; i < 3; i++ ) {
3180 SMESH_TLink link( aNodes[i], aNodes[i+1] );
3181 // check if elements sharing a link can be fused
3182 itLE = mapLi_listEl.find( link );
3183 if ( itLE != mapLi_listEl.end() ) {
3184 if ((*itLE).second.size() > 1 ) // consider only 2 elems adjacent by a link
3186 const SMDS_MeshElement* elem2 = (*itLE).second.front();
3187 //if ( FindShape( elem ) != FindShape( elem2 ))
3188 // continue; // do not fuse triangles laying on different shapes
3189 if ( getAngle( elem, elem2, aNodes[i], aNodes[i+1] ) > theMaxAngle )
3190 continue; // avoid making badly shaped quads
3191 (*itLE).second.push_back( elem );
3194 mapLi_listEl[ link ].push_back( elem );
3196 mapEl_setLi [ elem ].insert( link );
3199 // Clean the maps from the links shared by a sole element, ie
3200 // links to which only one element is bound in mapLi_listEl
3202 for ( itLE = mapLi_listEl.begin(); itLE != mapLi_listEl.end(); itLE++ ) {
3203 int nbElems = (*itLE).second.size();
3204 if ( nbElems < 2 ) {
3205 const SMDS_MeshElement* elem = (*itLE).second.front();
3206 SMESH_TLink link = (*itLE).first;
3207 mapEl_setLi[ elem ].erase( link );
3208 if ( mapEl_setLi[ elem ].empty() )
3209 mapEl_setLi.erase( elem );
3213 // Algo: fuse triangles into quadrangles
3215 while ( ! mapEl_setLi.empty() ) {
3216 // Look for the start element:
3217 // the element having the least nb of shared links
3218 const SMDS_MeshElement* startElem = 0;
3220 for ( itEL = mapEl_setLi.begin(); itEL != mapEl_setLi.end(); itEL++ ) {
3221 int nbLinks = (*itEL).second.size();
3222 if ( nbLinks < minNbLinks ) {
3223 startElem = (*itEL).first;
3224 minNbLinks = nbLinks;
3225 if ( minNbLinks == 1 )
3230 // search elements to fuse starting from startElem or links of elements
3231 // fused earlyer - startLinks
3232 list< SMESH_TLink > startLinks;
3233 while ( startElem || !startLinks.empty() ) {
3234 while ( !startElem && !startLinks.empty() ) {
3235 // Get an element to start, by a link
3236 SMESH_TLink linkId = startLinks.front();
3237 startLinks.pop_front();
3238 itLE = mapLi_listEl.find( linkId );
3239 if ( itLE != mapLi_listEl.end() ) {
3240 list< const SMDS_MeshElement* > & listElem = (*itLE).second;
3241 list< const SMDS_MeshElement* >::iterator itE = listElem.begin();
3242 for ( ; itE != listElem.end() ; itE++ )
3243 if ( mapEl_setLi.find( (*itE) ) != mapEl_setLi.end() )
3245 mapLi_listEl.erase( itLE );
3250 // Get candidates to be fused
3251 const SMDS_MeshElement *tr1 = startElem, *tr2 = 0, *tr3 = 0;
3252 const SMESH_TLink *link12 = 0, *link13 = 0;
3254 ASSERT( mapEl_setLi.find( tr1 ) != mapEl_setLi.end() );
3255 set< SMESH_TLink >& setLi = mapEl_setLi[ tr1 ];
3256 ASSERT( !setLi.empty() );
3257 set< SMESH_TLink >::iterator itLi;
3258 for ( itLi = setLi.begin(); itLi != setLi.end(); itLi++ )
3260 const SMESH_TLink & link = (*itLi);
3261 itLE = mapLi_listEl.find( link );
3262 if ( itLE == mapLi_listEl.end() )
3265 const SMDS_MeshElement* elem = (*itLE).second.front();
3267 elem = (*itLE).second.back();
3268 mapLi_listEl.erase( itLE );
3269 if ( mapEl_setLi.find( elem ) == mapEl_setLi.end())
3280 // add other links of elem to list of links to re-start from
3281 set< SMESH_TLink >& links = mapEl_setLi[ elem ];
3282 set< SMESH_TLink >::iterator it;
3283 for ( it = links.begin(); it != links.end(); it++ ) {
3284 const SMESH_TLink& link2 = (*it);
3285 if ( link2 != link )
3286 startLinks.push_back( link2 );
3290 // Get nodes of possible quadrangles
3291 const SMDS_MeshNode *n12 [4], *n13 [4];
3292 bool Ok12 = false, Ok13 = false;
3293 const SMDS_MeshNode *linkNode1, *linkNode2;
3295 linkNode1 = link12->first;
3296 linkNode2 = link12->second;
3297 if ( tr2 && getQuadrangleNodes( n12, linkNode1, linkNode2, tr1, tr2 ))
3301 linkNode1 = link13->first;
3302 linkNode2 = link13->second;
3303 if ( tr3 && getQuadrangleNodes( n13, linkNode1, linkNode2, tr1, tr3 ))
3307 // Choose a pair to fuse
3308 if ( Ok12 && Ok13 ) {
3309 SMDS_FaceOfNodes quad12 ( n12[ 0 ], n12[ 1 ], n12[ 2 ], n12[ 3 ] );
3310 SMDS_FaceOfNodes quad13 ( n13[ 0 ], n13[ 1 ], n13[ 2 ], n13[ 3 ] );
3311 double aBadRate12 = getBadRate( &quad12, theCrit );
3312 double aBadRate13 = getBadRate( &quad13, theCrit );
3313 if ( aBadRate13 < aBadRate12 )
3320 // and remove fused elems and remove links from the maps
3321 mapEl_setLi.erase( tr1 );
3324 mapEl_setLi.erase( tr2 );
3325 mapLi_listEl.erase( *link12 );
3326 if ( tr1->NbNodes() == 3 )
3328 const SMDS_MeshElement* newElem = 0;
3329 newElem = aMesh->AddFace(n12[0], n12[1], n12[2], n12[3] );
3330 myLastCreatedElems.Append(newElem);
3331 AddToSameGroups( newElem, tr1, aMesh );
3332 int aShapeId = tr1->getshapeId();
3334 aMesh->SetMeshElementOnShape( newElem, aShapeId );
3335 aMesh->RemoveElement( tr1 );
3336 aMesh->RemoveElement( tr2 );
3339 vector< const SMDS_MeshNode* > N1;
3340 vector< const SMDS_MeshNode* > N2;
3341 getNodesFromTwoTria(tr1,tr2,N1,N2);
3342 // now we receive following N1 and N2 (using numeration as in image in InverseDiag())
3343 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
3344 // i.e. first nodes from both arrays form a new diagonal
3345 const SMDS_MeshNode* aNodes[8];
3354 const SMDS_MeshElement* newElem = 0;
3355 if ( N1.size() == 7 || N2.size() == 7 ) // biquadratic
3356 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3357 aNodes[4], aNodes[5], aNodes[6], aNodes[7], N1[4]);
3359 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3360 aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
3361 myLastCreatedElems.Append(newElem);
3362 AddToSameGroups( newElem, tr1, aMesh );
3363 int aShapeId = tr1->getshapeId();
3365 aMesh->SetMeshElementOnShape( newElem, aShapeId );
3366 aMesh->RemoveElement( tr1 );
3367 aMesh->RemoveElement( tr2 );
3368 // remove middle node (9)
3369 if ( N1[4]->NbInverseElements() == 0 )
3370 aMesh->RemoveNode( N1[4] );
3371 if ( N1.size() == 7 && N1[6]->NbInverseElements() == 0 )
3372 aMesh->RemoveNode( N1[6] );
3373 if ( N2.size() == 7 && N2[6]->NbInverseElements() == 0 )
3374 aMesh->RemoveNode( N2[6] );
3379 mapEl_setLi.erase( tr3 );
3380 mapLi_listEl.erase( *link13 );
3381 if ( tr1->NbNodes() == 3 ) {
3382 const SMDS_MeshElement* newElem = 0;
3383 newElem = aMesh->AddFace(n13[0], n13[1], n13[2], n13[3] );
3384 myLastCreatedElems.Append(newElem);
3385 AddToSameGroups( newElem, tr1, aMesh );
3386 int aShapeId = tr1->getshapeId();
3388 aMesh->SetMeshElementOnShape( newElem, aShapeId );
3389 aMesh->RemoveElement( tr1 );
3390 aMesh->RemoveElement( tr3 );
3393 vector< const SMDS_MeshNode* > N1;
3394 vector< const SMDS_MeshNode* > N2;
3395 getNodesFromTwoTria(tr1,tr3,N1,N2);
3396 // now we receive following N1 and N2 (using numeration as above image)
3397 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
3398 // i.e. first nodes from both arrays form a new diagonal
3399 const SMDS_MeshNode* aNodes[8];
3408 const SMDS_MeshElement* newElem = 0;
3409 if ( N1.size() == 7 || N2.size() == 7 ) // biquadratic
3410 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3411 aNodes[4], aNodes[5], aNodes[6], aNodes[7], N1[4]);
3413 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3414 aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
3415 myLastCreatedElems.Append(newElem);
3416 AddToSameGroups( newElem, tr1, aMesh );
3417 int aShapeId = tr1->getshapeId();
3419 aMesh->SetMeshElementOnShape( newElem, aShapeId );
3420 aMesh->RemoveElement( tr1 );
3421 aMesh->RemoveElement( tr3 );
3422 // remove middle node (9)
3423 if ( N1[4]->NbInverseElements() == 0 )
3424 aMesh->RemoveNode( N1[4] );
3425 if ( N1.size() == 7 && N1[6]->NbInverseElements() == 0 )
3426 aMesh->RemoveNode( N1[6] );
3427 if ( N2.size() == 7 && N2[6]->NbInverseElements() == 0 )
3428 aMesh->RemoveNode( N2[6] );
3432 // Next element to fuse: the rejected one
3434 startElem = Ok12 ? tr3 : tr2;
3436 } // if ( startElem )
3437 } // while ( startElem || !startLinks.empty() )
3438 } // while ( ! mapEl_setLi.empty() )
3444 /*#define DUMPSO(txt) \
3445 // cout << txt << endl;
3446 //=============================================================================
3450 //=============================================================================
3451 static void swap( int i1, int i2, int idNodes[], gp_Pnt P[] )
3455 int tmp = idNodes[ i1 ];
3456 idNodes[ i1 ] = idNodes[ i2 ];
3457 idNodes[ i2 ] = tmp;
3458 gp_Pnt Ptmp = P[ i1 ];
3461 DUMPSO( i1 << "(" << idNodes[ i2 ] << ") <-> " << i2 << "(" << idNodes[ i1 ] << ")");
3464 //=======================================================================
3465 //function : SortQuadNodes
3466 //purpose : Set 4 nodes of a quadrangle face in a good order.
3467 // Swap 1<->2 or 2<->3 nodes and correspondingly return
3469 //=======================================================================
3471 int SMESH_MeshEditor::SortQuadNodes (const SMDS_Mesh * theMesh,
3476 for ( i = 0; i < 4; i++ ) {
3477 const SMDS_MeshNode *n = theMesh->FindNode( idNodes[i] );
3479 P[ i ].SetCoord( n->X(), n->Y(), n->Z() );
3482 gp_Vec V1(P[0], P[1]);
3483 gp_Vec V2(P[0], P[2]);
3484 gp_Vec V3(P[0], P[3]);
3486 gp_Vec Cross1 = V1 ^ V2;
3487 gp_Vec Cross2 = V2 ^ V3;
3490 if (Cross1.Dot(Cross2) < 0)
3495 if (Cross1.Dot(Cross2) < 0)
3499 swap ( i, i + 1, idNodes, P );
3501 // for ( int ii = 0; ii < 4; ii++ ) {
3502 // const SMDS_MeshNode *n = theMesh->FindNode( idNodes[ii] );
3503 // DUMPSO( ii << "(" << idNodes[ii] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
3509 //=======================================================================
3510 //function : SortHexaNodes
3511 //purpose : Set 8 nodes of a hexahedron in a good order.
3512 // Return success status
3513 //=======================================================================
3515 bool SMESH_MeshEditor::SortHexaNodes (const SMDS_Mesh * theMesh,
3520 DUMPSO( "INPUT: ========================================");
3521 for ( i = 0; i < 8; i++ ) {
3522 const SMDS_MeshNode *n = theMesh->FindNode( idNodes[i] );
3523 if ( !n ) return false;
3524 P[ i ].SetCoord( n->X(), n->Y(), n->Z() );
3525 DUMPSO( i << "(" << idNodes[i] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
3527 DUMPSO( "========================================");
3530 set<int> faceNodes; // ids of bottom face nodes, to be found
3531 set<int> checkedId1; // ids of tried 2-nd nodes
3532 Standard_Real leastDist = DBL_MAX; // dist of the 4-th node from 123 plane
3533 const Standard_Real tol = 1.e-6; // tolerance to find nodes in plane
3534 int iMin, iLoop1 = 0;
3536 // Loop to try the 2-nd nodes
3538 while ( leastDist > DBL_MIN && ++iLoop1 < 8 )
3540 // Find not checked 2-nd node
3541 for ( i = 1; i < 8; i++ )
3542 if ( checkedId1.find( idNodes[i] ) == checkedId1.end() ) {
3543 int id1 = idNodes[i];
3544 swap ( 1, i, idNodes, P );
3545 checkedId1.insert ( id1 );
3549 // Find the 3-d node so that 1-2-3 triangle to be on a hexa face,
3550 // ie that all but meybe one (id3 which is on the same face) nodes
3551 // lay on the same side from the triangle plane.
3553 bool manyInPlane = false; // more than 4 nodes lay in plane
3555 while ( ++iLoop2 < 6 ) {
3557 // get 1-2-3 plane coeffs
3558 Standard_Real A, B, C, D;
3559 gp_Vec N = gp_Vec (P[0], P[1]).Crossed( gp_Vec (P[0], P[2]) );
3560 if ( N.SquareMagnitude() > gp::Resolution() )
3562 gp_Pln pln ( P[0], N );
3563 pln.Coefficients( A, B, C, D );
3565 // find the node (iMin) closest to pln
3566 Standard_Real dist[ 8 ], minDist = DBL_MAX;
3568 for ( i = 3; i < 8; i++ ) {
3569 dist[i] = A * P[i].X() + B * P[i].Y() + C * P[i].Z() + D;
3570 if ( fabs( dist[i] ) < minDist ) {
3571 minDist = fabs( dist[i] );
3574 if ( fabs( dist[i] ) <= tol )
3575 idInPln.insert( idNodes[i] );
3578 // there should not be more than 4 nodes in bottom plane
3579 if ( idInPln.size() > 1 )
3581 DUMPSO( "### idInPln.size() = " << idInPln.size());
3582 // idInPlane does not contain the first 3 nodes
3583 if ( manyInPlane || idInPln.size() == 5)
3584 return false; // all nodes in one plane
3587 // set the 1-st node to be not in plane
3588 for ( i = 3; i < 8; i++ ) {
3589 if ( idInPln.find( idNodes[ i ] ) == idInPln.end() ) {
3590 DUMPSO( "### Reset 0-th node");
3591 swap( 0, i, idNodes, P );
3596 // reset to re-check second nodes
3597 leastDist = DBL_MAX;
3601 break; // from iLoop2;
3604 // check that the other 4 nodes are on the same side
3605 bool sameSide = true;
3606 bool isNeg = dist[ iMin == 3 ? 4 : 3 ] <= 0.;
3607 for ( i = 3; sameSide && i < 8; i++ ) {
3609 sameSide = ( isNeg == dist[i] <= 0.);
3612 // keep best solution
3613 if ( sameSide && minDist < leastDist ) {
3614 leastDist = minDist;
3616 faceNodes.insert( idNodes[ 1 ] );
3617 faceNodes.insert( idNodes[ 2 ] );
3618 faceNodes.insert( idNodes[ iMin ] );
3619 DUMPSO( "loop " << iLoop2 << " id2 " << idNodes[ 1 ] << " id3 " << idNodes[ 2 ]
3620 << " leastDist = " << leastDist);
3621 if ( leastDist <= DBL_MIN )
3626 // set next 3-d node to check
3627 int iNext = 2 + iLoop2;
3629 DUMPSO( "Try 2-nd");
3630 swap ( 2, iNext, idNodes, P );
3632 } // while ( iLoop2 < 6 )
3635 if ( faceNodes.empty() ) return false;
3637 // Put the faceNodes in proper places
3638 for ( i = 4; i < 8; i++ ) {
3639 if ( faceNodes.find( idNodes[ i ] ) != faceNodes.end() ) {
3640 // find a place to put
3642 while ( faceNodes.find( idNodes[ iTo ] ) != faceNodes.end() )
3644 DUMPSO( "Set faceNodes");
3645 swap ( iTo, i, idNodes, P );
3650 // Set nodes of the found bottom face in good order
3651 DUMPSO( " Found bottom face: ");
3652 i = SortQuadNodes( theMesh, idNodes );
3654 gp_Pnt Ptmp = P[ i ];
3659 // for ( int ii = 0; ii < 4; ii++ ) {
3660 // const SMDS_MeshNode *n = theMesh->FindNode( idNodes[ii] );
3661 // DUMPSO( ii << "(" << idNodes[ii] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
3664 // Gravity center of the top and bottom faces
3665 gp_Pnt aGCb = ( P[0].XYZ() + P[1].XYZ() + P[2].XYZ() + P[3].XYZ() ) / 4.;
3666 gp_Pnt aGCt = ( P[4].XYZ() + P[5].XYZ() + P[6].XYZ() + P[7].XYZ() ) / 4.;
3668 // Get direction from the bottom to the top face
3669 gp_Vec upDir ( aGCb, aGCt );
3670 Standard_Real upDirSize = upDir.Magnitude();
3671 if ( upDirSize <= gp::Resolution() ) return false;
3674 // Assure that the bottom face normal points up
3675 gp_Vec Nb = gp_Vec (P[0], P[1]).Crossed( gp_Vec (P[0], P[2]) );
3676 Nb += gp_Vec (P[0], P[2]).Crossed( gp_Vec (P[0], P[3]) );
3677 if ( Nb.Dot( upDir ) < 0 ) {
3678 DUMPSO( "Reverse bottom face");
3679 swap( 1, 3, idNodes, P );
3682 // Find 5-th node - the one closest to the 1-st among the last 4 nodes.
3683 Standard_Real minDist = DBL_MAX;
3684 for ( i = 4; i < 8; i++ ) {
3685 // projection of P[i] to the plane defined by P[0] and upDir
3686 gp_Pnt Pp = P[i].Translated( upDir * ( upDir.Dot( gp_Vec( P[i], P[0] ))));
3687 Standard_Real sqDist = P[0].SquareDistance( Pp );
3688 if ( sqDist < minDist ) {
3693 DUMPSO( "Set 4-th");
3694 swap ( 4, iMin, idNodes, P );
3696 // Set nodes of the top face in good order
3697 DUMPSO( "Sort top face");
3698 i = SortQuadNodes( theMesh, &idNodes[4] );
3701 gp_Pnt Ptmp = P[ i ];
3706 // Assure that direction of the top face normal is from the bottom face
3707 gp_Vec Nt = gp_Vec (P[4], P[5]).Crossed( gp_Vec (P[4], P[6]) );
3708 Nt += gp_Vec (P[4], P[6]).Crossed( gp_Vec (P[4], P[7]) );
3709 if ( Nt.Dot( upDir ) < 0 ) {
3710 DUMPSO( "Reverse top face");
3711 swap( 5, 7, idNodes, P );
3714 // DUMPSO( "OUTPUT: ========================================");
3715 // for ( i = 0; i < 8; i++ ) {
3716 // float *p = ugrid->GetPoint(idNodes[i]);
3717 // DUMPSO( i << "(" << idNodes[i] << ") : " << p[0] << " " << p[1] << " " << p[2]);
3723 //================================================================================
3725 * \brief Return nodes linked to the given one
3726 * \param theNode - the node
3727 * \param linkedNodes - the found nodes
3728 * \param type - the type of elements to check
3730 * Medium nodes are ignored
3732 //================================================================================
3734 void SMESH_MeshEditor::GetLinkedNodes( const SMDS_MeshNode* theNode,
3735 TIDSortedElemSet & linkedNodes,
3736 SMDSAbs_ElementType type )
3738 SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(type);
3739 while ( elemIt->more() )
3741 const SMDS_MeshElement* elem = elemIt->next();
3742 if(elem->GetType() == SMDSAbs_0DElement)
3745 SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
3746 if ( elem->GetType() == SMDSAbs_Volume )
3748 SMDS_VolumeTool vol( elem );
3749 while ( nodeIt->more() ) {
3750 const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
3751 if ( theNode != n && vol.IsLinked( theNode, n ))
3752 linkedNodes.insert( n );
3757 for ( int i = 0; nodeIt->more(); ++i ) {
3758 const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
3759 if ( n == theNode ) {
3760 int iBefore = i - 1;
3762 if ( elem->IsQuadratic() ) {
3763 int nb = elem->NbNodes() / 2;
3764 iAfter = SMESH_MesherHelper::WrapIndex( iAfter, nb );
3765 iBefore = SMESH_MesherHelper::WrapIndex( iBefore, nb );
3767 linkedNodes.insert( elem->GetNodeWrap( iAfter ));
3768 linkedNodes.insert( elem->GetNodeWrap( iBefore ));
3775 //=======================================================================
3776 //function : laplacianSmooth
3777 //purpose : pulls theNode toward the center of surrounding nodes directly
3778 // connected to that node along an element edge
3779 //=======================================================================
3781 void laplacianSmooth(const SMDS_MeshNode* theNode,
3782 const Handle(Geom_Surface)& theSurface,
3783 map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
3785 // find surrounding nodes
3787 TIDSortedElemSet nodeSet;
3788 SMESH_MeshEditor::GetLinkedNodes( theNode, nodeSet, SMDSAbs_Face );
3790 // compute new coodrs
3792 double coord[] = { 0., 0., 0. };
3793 TIDSortedElemSet::iterator nodeSetIt = nodeSet.begin();
3794 for ( ; nodeSetIt != nodeSet.end(); nodeSetIt++ ) {
3795 const SMDS_MeshNode* node = cast2Node(*nodeSetIt);
3796 if ( theSurface.IsNull() ) { // smooth in 3D
3797 coord[0] += node->X();
3798 coord[1] += node->Y();
3799 coord[2] += node->Z();
3801 else { // smooth in 2D
3802 ASSERT( theUVMap.find( node ) != theUVMap.end() );
3803 gp_XY* uv = theUVMap[ node ];
3804 coord[0] += uv->X();
3805 coord[1] += uv->Y();
3808 int nbNodes = nodeSet.size();
3811 coord[0] /= nbNodes;
3812 coord[1] /= nbNodes;
3814 if ( !theSurface.IsNull() ) {
3815 ASSERT( theUVMap.find( theNode ) != theUVMap.end() );
3816 theUVMap[ theNode ]->SetCoord( coord[0], coord[1] );
3817 gp_Pnt p3d = theSurface->Value( coord[0], coord[1] );
3823 coord[2] /= nbNodes;
3827 const_cast< SMDS_MeshNode* >( theNode )->setXYZ(coord[0],coord[1],coord[2]);
3830 //=======================================================================
3831 //function : centroidalSmooth
3832 //purpose : pulls theNode toward the element-area-weighted centroid of the
3833 // surrounding elements
3834 //=======================================================================
3836 void centroidalSmooth(const SMDS_MeshNode* theNode,
3837 const Handle(Geom_Surface)& theSurface,
3838 map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
3840 gp_XYZ aNewXYZ(0.,0.,0.);
3841 SMESH::Controls::Area anAreaFunc;
3842 double totalArea = 0.;
3847 SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(SMDSAbs_Face);
3848 while ( elemIt->more() )
3850 const SMDS_MeshElement* elem = elemIt->next();
3853 gp_XYZ elemCenter(0.,0.,0.);
3854 SMESH::Controls::TSequenceOfXYZ aNodePoints;
3855 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3856 int nn = elem->NbNodes();
3857 if(elem->IsQuadratic()) nn = nn/2;
3859 //while ( itN->more() ) {
3861 const SMDS_MeshNode* aNode = static_cast<const SMDS_MeshNode*>( itN->next() );
3863 gp_XYZ aP( aNode->X(), aNode->Y(), aNode->Z() );
3864 aNodePoints.push_back( aP );
3865 if ( !theSurface.IsNull() ) { // smooth in 2D
3866 ASSERT( theUVMap.find( aNode ) != theUVMap.end() );
3867 gp_XY* uv = theUVMap[ aNode ];
3868 aP.SetCoord( uv->X(), uv->Y(), 0. );
3872 double elemArea = anAreaFunc.GetValue( aNodePoints );
3873 totalArea += elemArea;
3875 aNewXYZ += elemCenter * elemArea;
3877 aNewXYZ /= totalArea;
3878 if ( !theSurface.IsNull() ) {
3879 theUVMap[ theNode ]->SetCoord( aNewXYZ.X(), aNewXYZ.Y() );
3880 aNewXYZ = theSurface->Value( aNewXYZ.X(), aNewXYZ.Y() ).XYZ();
3885 const_cast< SMDS_MeshNode* >( theNode )->setXYZ(aNewXYZ.X(),aNewXYZ.Y(),aNewXYZ.Z());
3888 //=======================================================================
3889 //function : getClosestUV
3890 //purpose : return UV of closest projection
3891 //=======================================================================
3893 static bool getClosestUV (Extrema_GenExtPS& projector,
3894 const gp_Pnt& point,
3897 projector.Perform( point );
3898 if ( projector.IsDone() ) {
3899 double u, v, minVal = DBL_MAX;
3900 for ( int i = projector.NbExt(); i > 0; i-- )
3901 if ( projector.SquareDistance( i ) < minVal ) {
3902 minVal = projector.SquareDistance( i );
3903 projector.Point( i ).Parameter( u, v );
3905 result.SetCoord( u, v );
3911 //=======================================================================
3913 //purpose : Smooth theElements during theNbIterations or until a worst
3914 // element has aspect ratio <= theTgtAspectRatio.
3915 // Aspect Ratio varies in range [1.0, inf].
3916 // If theElements is empty, the whole mesh is smoothed.
3917 // theFixedNodes contains additionally fixed nodes. Nodes built
3918 // on edges and boundary nodes are always fixed.
3919 //=======================================================================
3921 void SMESH_MeshEditor::Smooth (TIDSortedElemSet & theElems,
3922 set<const SMDS_MeshNode*> & theFixedNodes,
3923 const SmoothMethod theSmoothMethod,
3924 const int theNbIterations,
3925 double theTgtAspectRatio,
3928 myLastCreatedElems.Clear();
3929 myLastCreatedNodes.Clear();
3931 if ( theTgtAspectRatio < 1.0 )
3932 theTgtAspectRatio = 1.0;
3934 const double disttol = 1.e-16;
3936 SMESH::Controls::AspectRatio aQualityFunc;
3938 SMESHDS_Mesh* aMesh = GetMeshDS();
3940 if ( theElems.empty() ) {
3941 // add all faces to theElems
3942 SMDS_FaceIteratorPtr fIt = aMesh->facesIterator();
3943 while ( fIt->more() ) {
3944 const SMDS_MeshElement* face = fIt->next();
3945 theElems.insert( theElems.end(), face );
3948 // get all face ids theElems are on
3949 set< int > faceIdSet;
3950 TIDSortedElemSet::iterator itElem;
3952 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
3953 int fId = FindShape( *itElem );
3954 // check that corresponding submesh exists and a shape is face
3956 faceIdSet.find( fId ) == faceIdSet.end() &&
3957 aMesh->MeshElements( fId )) {
3958 TopoDS_Shape F = aMesh->IndexToShape( fId );
3959 if ( !F.IsNull() && F.ShapeType() == TopAbs_FACE )
3960 faceIdSet.insert( fId );
3963 faceIdSet.insert( 0 ); // to smooth elements that are not on any TopoDS_Face
3965 // ===============================================
3966 // smooth elements on each TopoDS_Face separately
3967 // ===============================================
3969 SMESH_MesherHelper helper( *GetMesh() );
3971 set< int >::reverse_iterator fId = faceIdSet.rbegin(); // treat 0 fId at the end
3972 for ( ; fId != faceIdSet.rend(); ++fId )
3974 // get face surface and submesh
3975 Handle(Geom_Surface) surface;
3976 SMESHDS_SubMesh* faceSubMesh = 0;
3979 double u1 = 0, u2 = 0, v1 = 0, v2 = 0;
3980 bool isUPeriodic = false, isVPeriodic = false;
3983 face = TopoDS::Face( aMesh->IndexToShape( *fId ));
3984 surface = BRep_Tool::Surface( face );
3985 faceSubMesh = aMesh->MeshElements( *fId );
3986 fToler2 = BRep_Tool::Tolerance( face );
3987 fToler2 *= fToler2 * 10.;
3988 isUPeriodic = surface->IsUPeriodic();
3991 isVPeriodic = surface->IsVPeriodic();
3994 surface->Bounds( u1, u2, v1, v2 );
3995 helper.SetSubShape( face );
3997 // ---------------------------------------------------------
3998 // for elements on a face, find movable and fixed nodes and
3999 // compute UV for them
4000 // ---------------------------------------------------------
4001 bool checkBoundaryNodes = false;
4002 bool isQuadratic = false;
4003 set<const SMDS_MeshNode*> setMovableNodes;
4004 map< const SMDS_MeshNode*, gp_XY* > uvMap, uvMap2;
4005 list< gp_XY > listUV; // uvs the 2 uvMaps refer to
4006 list< const SMDS_MeshElement* > elemsOnFace;
4008 Extrema_GenExtPS projector;
4009 GeomAdaptor_Surface surfAdaptor;
4010 if ( !surface.IsNull() ) {
4011 surfAdaptor.Load( surface );
4012 projector.Initialize( surfAdaptor, 20,20, 1e-5,1e-5 );
4014 int nbElemOnFace = 0;
4015 itElem = theElems.begin();
4016 // loop on not yet smoothed elements: look for elems on a face
4017 while ( itElem != theElems.end() )
4019 if ( faceSubMesh && nbElemOnFace == faceSubMesh->NbElements() )
4020 break; // all elements found
4022 const SMDS_MeshElement* elem = *itElem;
4023 if ( !elem || elem->GetType() != SMDSAbs_Face || elem->NbNodes() < 3 ||
4024 ( faceSubMesh && !faceSubMesh->Contains( elem ))) {
4028 elemsOnFace.push_back( elem );
4029 theElems.erase( itElem++ );
4033 isQuadratic = elem->IsQuadratic();
4035 // get movable nodes of elem
4036 const SMDS_MeshNode* node;
4037 SMDS_TypeOfPosition posType;
4038 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
4039 int nn = 0, nbn = elem->NbNodes();
4040 if(elem->IsQuadratic())
4042 while ( nn++ < nbn ) {
4043 node = static_cast<const SMDS_MeshNode*>( itN->next() );
4044 const SMDS_PositionPtr& pos = node->GetPosition();
4045 posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
4046 if (posType != SMDS_TOP_EDGE &&
4047 posType != SMDS_TOP_VERTEX &&
4048 theFixedNodes.find( node ) == theFixedNodes.end())
4050 // check if all faces around the node are on faceSubMesh
4051 // because a node on edge may be bound to face
4052 SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
4054 if ( faceSubMesh ) {
4055 while ( eIt->more() && all ) {
4056 const SMDS_MeshElement* e = eIt->next();
4057 all = faceSubMesh->Contains( e );
4061 setMovableNodes.insert( node );
4063 checkBoundaryNodes = true;
4065 if ( posType == SMDS_TOP_3DSPACE )
4066 checkBoundaryNodes = true;
4069 if ( surface.IsNull() )
4072 // get nodes to check UV
4073 list< const SMDS_MeshNode* > uvCheckNodes;
4074 const SMDS_MeshNode* nodeInFace = 0;
4075 itN = elem->nodesIterator();
4076 nn = 0; nbn = elem->NbNodes();
4077 if(elem->IsQuadratic())
4079 while ( nn++ < nbn ) {
4080 node = static_cast<const SMDS_MeshNode*>( itN->next() );
4081 if ( node->GetPosition()->GetDim() == 2 )
4083 if ( uvMap.find( node ) == uvMap.end() )
4084 uvCheckNodes.push_back( node );
4085 // add nodes of elems sharing node
4086 // SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
4087 // while ( eIt->more() ) {
4088 // const SMDS_MeshElement* e = eIt->next();
4089 // if ( e != elem ) {
4090 // SMDS_ElemIteratorPtr nIt = e->nodesIterator();
4091 // while ( nIt->more() ) {
4092 // const SMDS_MeshNode* n =
4093 // static_cast<const SMDS_MeshNode*>( nIt->next() );
4094 // if ( uvMap.find( n ) == uvMap.end() )
4095 // uvCheckNodes.push_back( n );
4101 list< const SMDS_MeshNode* >::iterator n = uvCheckNodes.begin();
4102 for ( ; n != uvCheckNodes.end(); ++n ) {
4105 const SMDS_PositionPtr& pos = node->GetPosition();
4106 posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
4110 bool toCheck = true;
4111 uv = helper.GetNodeUV( face, node, nodeInFace, &toCheck );
4113 // compute not existing UV
4114 bool project = ( posType == SMDS_TOP_3DSPACE );
4115 // double dist1 = DBL_MAX, dist2 = 0;
4116 // if ( posType != SMDS_TOP_3DSPACE ) {
4117 // dist1 = pNode.SquareDistance( surface->Value( uv.X(), uv.Y() ));
4118 // project = dist1 > fToler2;
4120 if ( project ) { // compute new UV
4122 gp_Pnt pNode = SMESH_TNodeXYZ( node );
4123 if ( !getClosestUV( projector, pNode, newUV )) {
4124 MESSAGE("Node Projection Failed " << node);
4128 newUV.SetX( ElCLib::InPeriod( newUV.X(), u1, u2 ));
4130 newUV.SetY( ElCLib::InPeriod( newUV.Y(), v1, v2 ));
4132 // if ( posType != SMDS_TOP_3DSPACE )
4133 // dist2 = pNode.SquareDistance( surface->Value( newUV.X(), newUV.Y() ));
4134 // if ( dist2 < dist1 )
4138 // store UV in the map
4139 listUV.push_back( uv );
4140 uvMap.insert( make_pair( node, &listUV.back() ));
4142 } // loop on not yet smoothed elements
4144 if ( !faceSubMesh || nbElemOnFace != faceSubMesh->NbElements() )
4145 checkBoundaryNodes = true;
4147 // fix nodes on mesh boundary
4149 if ( checkBoundaryNodes ) {
4150 map< SMESH_TLink, int > linkNbMap; // how many times a link encounters in elemsOnFace
4151 map< SMESH_TLink, int >::iterator link_nb;
4152 // put all elements links to linkNbMap
4153 list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4154 for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
4155 const SMDS_MeshElement* elem = (*elemIt);
4156 int nbn = elem->NbCornerNodes();
4157 // loop on elem links: insert them in linkNbMap
4158 for ( int iN = 0; iN < nbn; ++iN ) {
4159 const SMDS_MeshNode* n1 = elem->GetNode( iN );
4160 const SMDS_MeshNode* n2 = elem->GetNode(( iN+1 ) % nbn);
4161 SMESH_TLink link( n1, n2 );
4162 link_nb = linkNbMap.insert( make_pair( link, 0 )).first;
4166 // remove nodes that are in links encountered only once from setMovableNodes
4167 for ( link_nb = linkNbMap.begin(); link_nb != linkNbMap.end(); ++link_nb ) {
4168 if ( link_nb->second == 1 ) {
4169 setMovableNodes.erase( link_nb->first.node1() );
4170 setMovableNodes.erase( link_nb->first.node2() );
4175 // -----------------------------------------------------
4176 // for nodes on seam edge, compute one more UV ( uvMap2 );
4177 // find movable nodes linked to nodes on seam and which
4178 // are to be smoothed using the second UV ( uvMap2 )
4179 // -----------------------------------------------------
4181 set<const SMDS_MeshNode*> nodesNearSeam; // to smooth using uvMap2
4182 if ( !surface.IsNull() ) {
4183 TopExp_Explorer eExp( face, TopAbs_EDGE );
4184 for ( ; eExp.More(); eExp.Next() ) {
4185 TopoDS_Edge edge = TopoDS::Edge( eExp.Current() );
4186 if ( !BRep_Tool::IsClosed( edge, face ))
4188 SMESHDS_SubMesh* sm = aMesh->MeshElements( edge );
4189 if ( !sm ) continue;
4190 // find out which parameter varies for a node on seam
4193 Handle(Geom2d_Curve) pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
4194 if ( pcurve.IsNull() ) continue;
4195 uv1 = pcurve->Value( f );
4197 pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
4198 if ( pcurve.IsNull() ) continue;
4199 uv2 = pcurve->Value( f );
4200 int iPar = Abs( uv1.X() - uv2.X() ) > Abs( uv1.Y() - uv2.Y() ) ? 1 : 2;
4202 if ( uv1.Coord( iPar ) > uv2.Coord( iPar ))
4203 std::swap( uv1, uv2 );
4204 // get nodes on seam and its vertices
4205 list< const SMDS_MeshNode* > seamNodes;
4206 SMDS_NodeIteratorPtr nSeamIt = sm->GetNodes();
4207 while ( nSeamIt->more() ) {
4208 const SMDS_MeshNode* node = nSeamIt->next();
4209 if ( !isQuadratic || !IsMedium( node ))
4210 seamNodes.push_back( node );
4212 TopExp_Explorer vExp( edge, TopAbs_VERTEX );
4213 for ( ; vExp.More(); vExp.Next() ) {
4214 sm = aMesh->MeshElements( vExp.Current() );
4216 nSeamIt = sm->GetNodes();
4217 while ( nSeamIt->more() )
4218 seamNodes.push_back( nSeamIt->next() );
4221 // loop on nodes on seam
4222 list< const SMDS_MeshNode* >::iterator noSeIt = seamNodes.begin();
4223 for ( ; noSeIt != seamNodes.end(); ++noSeIt ) {
4224 const SMDS_MeshNode* nSeam = *noSeIt;
4225 map< const SMDS_MeshNode*, gp_XY* >::iterator n_uv = uvMap.find( nSeam );
4226 if ( n_uv == uvMap.end() )
4229 n_uv->second->SetCoord( iPar, uv1.Coord( iPar ));
4230 // set the second UV
4231 listUV.push_back( *n_uv->second );
4232 listUV.back().SetCoord( iPar, uv2.Coord( iPar ));
4233 if ( uvMap2.empty() )
4234 uvMap2 = uvMap; // copy the uvMap contents
4235 uvMap2[ nSeam ] = &listUV.back();
4237 // collect movable nodes linked to ones on seam in nodesNearSeam
4238 SMDS_ElemIteratorPtr eIt = nSeam->GetInverseElementIterator(SMDSAbs_Face);
4239 while ( eIt->more() ) {
4240 const SMDS_MeshElement* e = eIt->next();
4241 int nbUseMap1 = 0, nbUseMap2 = 0;
4242 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
4243 int nn = 0, nbn = e->NbNodes();
4244 if(e->IsQuadratic()) nbn = nbn/2;
4245 while ( nn++ < nbn )
4247 const SMDS_MeshNode* n =
4248 static_cast<const SMDS_MeshNode*>( nIt->next() );
4250 setMovableNodes.find( n ) == setMovableNodes.end() )
4252 // add only nodes being closer to uv2 than to uv1
4253 // gp_Pnt pMid (0.5 * ( n->X() + nSeam->X() ),
4254 // 0.5 * ( n->Y() + nSeam->Y() ),
4255 // 0.5 * ( n->Z() + nSeam->Z() ));
4257 // getClosestUV( projector, pMid, uv );
4258 double x = uvMap[ n ]->Coord( iPar );
4259 if ( Abs( uv1.Coord( iPar ) - x ) >
4260 Abs( uv2.Coord( iPar ) - x )) {
4261 nodesNearSeam.insert( n );
4267 // for centroidalSmooth all element nodes must
4268 // be on one side of a seam
4269 if ( theSmoothMethod == CENTROIDAL && nbUseMap1 && nbUseMap2 ) {
4270 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
4272 while ( nn++ < nbn ) {
4273 const SMDS_MeshNode* n =
4274 static_cast<const SMDS_MeshNode*>( nIt->next() );
4275 setMovableNodes.erase( n );
4279 } // loop on nodes on seam
4280 } // loop on edge of a face
4281 } // if ( !face.IsNull() )
4283 if ( setMovableNodes.empty() ) {
4284 MESSAGE( "Face id : " << *fId << " - NO SMOOTHING: no nodes to move!!!");
4285 continue; // goto next face
4293 double maxRatio = -1., maxDisplacement = -1.;
4294 set<const SMDS_MeshNode*>::iterator nodeToMove;
4295 for ( it = 0; it < theNbIterations; it++ ) {
4296 maxDisplacement = 0.;
4297 nodeToMove = setMovableNodes.begin();
4298 for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
4299 const SMDS_MeshNode* node = (*nodeToMove);
4300 gp_XYZ aPrevPos ( node->X(), node->Y(), node->Z() );
4303 bool map2 = ( nodesNearSeam.find( node ) != nodesNearSeam.end() );
4304 if ( theSmoothMethod == LAPLACIAN )
4305 laplacianSmooth( node, surface, map2 ? uvMap2 : uvMap );
4307 centroidalSmooth( node, surface, map2 ? uvMap2 : uvMap );
4309 // node displacement
4310 gp_XYZ aNewPos ( node->X(), node->Y(), node->Z() );
4311 Standard_Real aDispl = (aPrevPos - aNewPos).SquareModulus();
4312 if ( aDispl > maxDisplacement )
4313 maxDisplacement = aDispl;
4315 // no node movement => exit
4316 //if ( maxDisplacement < 1.e-16 ) {
4317 if ( maxDisplacement < disttol ) {
4318 MESSAGE("-- no node movement --");
4322 // check elements quality
4324 list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4325 for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
4326 const SMDS_MeshElement* elem = (*elemIt);
4327 if ( !elem || elem->GetType() != SMDSAbs_Face )
4329 SMESH::Controls::TSequenceOfXYZ aPoints;
4330 if ( aQualityFunc.GetPoints( elem, aPoints )) {
4331 double aValue = aQualityFunc.GetValue( aPoints );
4332 if ( aValue > maxRatio )
4336 if ( maxRatio <= theTgtAspectRatio ) {
4337 MESSAGE("-- quality achived --");
4340 if (it+1 == theNbIterations) {
4341 MESSAGE("-- Iteration limit exceeded --");
4343 } // smoothing iterations
4345 MESSAGE(" Face id: " << *fId <<
4346 " Nb iterstions: " << it <<
4347 " Displacement: " << maxDisplacement <<
4348 " Aspect Ratio " << maxRatio);
4350 // ---------------------------------------
4351 // new nodes positions are computed,
4352 // record movement in DS and set new UV
4353 // ---------------------------------------
4354 nodeToMove = setMovableNodes.begin();
4355 for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
4356 SMDS_MeshNode* node = const_cast< SMDS_MeshNode* > (*nodeToMove);
4357 aMesh->MoveNode( node, node->X(), node->Y(), node->Z() );
4358 map< const SMDS_MeshNode*, gp_XY* >::iterator node_uv = uvMap.find( node );
4359 if ( node_uv != uvMap.end() ) {
4360 gp_XY* uv = node_uv->second;
4362 ( SMDS_PositionPtr( new SMDS_FacePosition( uv->X(), uv->Y() )));
4366 // move medium nodes of quadratic elements
4369 vector<const SMDS_MeshNode*> nodes;
4371 list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4372 for ( ; elemIt != elemsOnFace.end(); ++elemIt )
4374 const SMDS_MeshElement* QF = *elemIt;
4375 if ( QF->IsQuadratic() )
4377 nodes.assign( SMDS_MeshElement::iterator( QF->interlacedNodesElemIterator() ),
4378 SMDS_MeshElement::iterator() );
4379 nodes.push_back( nodes[0] );
4381 for (size_t i = 1; i < nodes.size(); i += 2 ) // i points to a medium node
4383 if ( !surface.IsNull() )
4385 gp_XY uv1 = helper.GetNodeUV( face, nodes[i-1], nodes[i+1], &checkUV );
4386 gp_XY uv2 = helper.GetNodeUV( face, nodes[i+1], nodes[i-1], &checkUV );
4387 gp_XY uv = helper.GetMiddleUV( surface, uv1, uv2 );
4388 xyz = surface->Value( uv.X(), uv.Y() );
4391 xyz = 0.5 * ( SMESH_TNodeXYZ( nodes[i-1] ) + SMESH_TNodeXYZ( nodes[i+1] ));
4393 if (( SMESH_TNodeXYZ( nodes[i] ) - xyz.XYZ() ).Modulus() > disttol )
4394 // we have to move a medium node
4395 aMesh->MoveNode( nodes[i], xyz.X(), xyz.Y(), xyz.Z() );
4401 } // loop on face ids
4407 //=======================================================================
4408 //function : isReverse
4409 //purpose : Return true if normal of prevNodes is not co-directied with
4410 // gp_Vec(prevNodes[iNotSame],nextNodes[iNotSame]).
4411 // iNotSame is where prevNodes and nextNodes are different.
4412 // If result is true then future volume orientation is OK
4413 //=======================================================================
4415 bool isReverse(const SMDS_MeshElement* face,
4416 const vector<const SMDS_MeshNode*>& prevNodes,
4417 const vector<const SMDS_MeshNode*>& nextNodes,
4421 SMESH_TNodeXYZ pP = prevNodes[ iNotSame ];
4422 SMESH_TNodeXYZ pN = nextNodes[ iNotSame ];
4423 gp_XYZ extrDir( pN - pP ), faceNorm;
4424 SMESH_MeshAlgos::FaceNormal( face, faceNorm, /*normalized=*/false );
4426 return faceNorm * extrDir < 0.0;
4429 //================================================================================
4431 * \brief Assure that theElemSets[0] holds elements, not nodes
4433 //================================================================================
4435 void setElemsFirst( TIDSortedElemSet theElemSets[2] )
4437 if ( !theElemSets[0].empty() &&
4438 (*theElemSets[0].begin())->GetType() == SMDSAbs_Node )
4440 std::swap( theElemSets[0], theElemSets[1] );
4442 else if ( !theElemSets[1].empty() &&
4443 (*theElemSets[1].begin())->GetType() != SMDSAbs_Node )
4445 std::swap( theElemSets[0], theElemSets[1] );
4450 //=======================================================================
4452 * \brief Create elements by sweeping an element
4453 * \param elem - element to sweep
4454 * \param newNodesItVec - nodes generated from each node of the element
4455 * \param newElems - generated elements
4456 * \param nbSteps - number of sweeping steps
4457 * \param srcElements - to append elem for each generated element
4459 //=======================================================================
4461 void SMESH_MeshEditor::sweepElement(const SMDS_MeshElement* elem,
4462 const vector<TNodeOfNodeListMapItr> & newNodesItVec,
4463 list<const SMDS_MeshElement*>& newElems,
4464 const size_t nbSteps,
4465 SMESH_SequenceOfElemPtr& srcElements)
4467 SMESHDS_Mesh* aMesh = GetMeshDS();
4469 const int nbNodes = elem->NbNodes();
4470 const int nbCorners = elem->NbCornerNodes();
4471 SMDSAbs_EntityType baseType = elem->GetEntityType(); /* it can change in case of
4472 polyhedron creation !!! */
4473 // Loop on elem nodes:
4474 // find new nodes and detect same nodes indices
4475 vector < list< const SMDS_MeshNode* >::const_iterator > itNN( nbNodes );
4476 vector<const SMDS_MeshNode*> prevNod( nbNodes );
4477 vector<const SMDS_MeshNode*> nextNod( nbNodes );
4478 vector<const SMDS_MeshNode*> midlNod( nbNodes );
4480 int iNode, nbSame = 0, nbDouble = 0, iNotSameNode = 0;
4481 vector<int> sames(nbNodes);
4482 vector<bool> isSingleNode(nbNodes);
4484 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4485 TNodeOfNodeListMapItr nnIt = newNodesItVec[ iNode ];
4486 const SMDS_MeshNode* node = nnIt->first;
4487 const list< const SMDS_MeshNode* > & listNewNodes = nnIt->second;
4488 if ( listNewNodes.empty() )
4491 itNN [ iNode ] = listNewNodes.begin();
4492 prevNod[ iNode ] = node;
4493 nextNod[ iNode ] = listNewNodes.front();
4495 isSingleNode[iNode] = (listNewNodes.size()==nbSteps); /* medium node of quadratic or
4496 corner node of linear */
4497 if ( prevNod[ iNode ] != nextNod [ iNode ])
4498 nbDouble += !isSingleNode[iNode];
4500 if( iNode < nbCorners ) { // check corners only
4501 if ( prevNod[ iNode ] == nextNod [ iNode ])
4502 sames[nbSame++] = iNode;
4504 iNotSameNode = iNode;
4508 if ( nbSame == nbNodes || nbSame > 2) {
4509 MESSAGE( " Too many same nodes of element " << elem->GetID() );
4513 if ( elem->GetType() == SMDSAbs_Face && !isReverse( elem, prevNod, nextNod, iNotSameNode ))
4515 // fix nodes order to have bottom normal external
4516 if ( baseType == SMDSEntity_Polygon )
4518 std::reverse( itNN.begin(), itNN.end() );
4519 std::reverse( prevNod.begin(), prevNod.end() );
4520 std::reverse( midlNod.begin(), midlNod.end() );
4521 std::reverse( nextNod.begin(), nextNod.end() );
4522 std::reverse( isSingleNode.begin(), isSingleNode.end() );
4526 const vector<int>& ind = SMDS_MeshCell::reverseSmdsOrder( baseType, nbNodes );
4527 SMDS_MeshCell::applyInterlace( ind, itNN );
4528 SMDS_MeshCell::applyInterlace( ind, prevNod );
4529 SMDS_MeshCell::applyInterlace( ind, nextNod );
4530 SMDS_MeshCell::applyInterlace( ind, midlNod );
4531 SMDS_MeshCell::applyInterlace( ind, isSingleNode );
4534 sames[nbSame] = iNotSameNode;
4535 for ( int j = 0; j <= nbSame; ++j )
4536 for ( size_t i = 0; i < ind.size(); ++i )
4537 if ( ind[i] == sames[j] )
4542 iNotSameNode = sames[nbSame];
4546 else if ( elem->GetType() == SMDSAbs_Edge )
4548 // orient a new face same as adjacent one
4550 const SMDS_MeshElement* e;
4551 TIDSortedElemSet dummy;
4552 if (( e = SMESH_MeshAlgos::FindFaceInSet( nextNod[0], prevNod[0], dummy,dummy, &i1, &i2 )) ||
4553 ( e = SMESH_MeshAlgos::FindFaceInSet( prevNod[1], nextNod[1], dummy,dummy, &i1, &i2 )) ||
4554 ( e = SMESH_MeshAlgos::FindFaceInSet( prevNod[0], prevNod[1], dummy,dummy, &i1, &i2 )))
4556 // there is an adjacent face, check order of nodes in it
4557 bool sameOrder = ( Abs( i2 - i1 ) == 1 ) ? ( i2 > i1 ) : ( i2 < i1 );
4560 std::swap( itNN[0], itNN[1] );
4561 std::swap( prevNod[0], prevNod[1] );
4562 std::swap( nextNod[0], nextNod[1] );
4563 isSingleNode.swap( isSingleNode[0], isSingleNode[1] );
4565 sames[0] = 1 - sames[0];
4566 iNotSameNode = 1 - iNotSameNode;
4571 int iSameNode = 0, iBeforeSame = 0, iAfterSame = 0, iOpposSame = 0;
4573 iSameNode = sames[ nbSame-1 ];
4574 iBeforeSame = ( iSameNode + nbCorners - 1 ) % nbCorners;
4575 iAfterSame = ( iSameNode + 1 ) % nbCorners;
4576 iOpposSame = ( iSameNode - 2 < 0 ? iSameNode + 2 : iSameNode - 2 );
4579 if ( baseType == SMDSEntity_Polygon )
4581 if ( nbNodes == 3 ) baseType = SMDSEntity_Triangle;
4582 else if ( nbNodes == 4 ) baseType = SMDSEntity_Quadrangle;
4584 else if ( baseType == SMDSEntity_Quad_Polygon )
4586 if ( nbNodes == 6 ) baseType = SMDSEntity_Quad_Triangle;
4587 else if ( nbNodes == 8 ) baseType = SMDSEntity_Quad_Quadrangle;
4590 // make new elements
4591 for ( size_t iStep = 0; iStep < nbSteps; iStep++ )
4594 for ( iNode = 0; iNode < nbNodes; iNode++ )
4596 midlNod[ iNode ] = isSingleNode[iNode] ? 0 : *itNN[ iNode ]++;
4597 nextNod[ iNode ] = *itNN[ iNode ]++;
4600 SMDS_MeshElement* aNewElem = 0;
4601 /*if(!elem->IsPoly())*/ {
4602 switch ( baseType ) {
4604 case SMDSEntity_Node: { // sweep NODE
4605 if ( nbSame == 0 ) {
4606 if ( isSingleNode[0] )
4607 aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ] );
4609 aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ], midlNod[ 0 ] );
4615 case SMDSEntity_Edge: { // sweep EDGE
4616 if ( nbDouble == 0 )
4618 if ( nbSame == 0 ) // ---> quadrangle
4619 aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
4620 nextNod[ 1 ], nextNod[ 0 ] );
4621 else // ---> triangle
4622 aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
4623 nextNod[ iNotSameNode ] );
4625 else // ---> polygon
4627 vector<const SMDS_MeshNode*> poly_nodes;
4628 poly_nodes.push_back( prevNod[0] );
4629 poly_nodes.push_back( prevNod[1] );
4630 if ( prevNod[1] != nextNod[1] )
4632 if ( midlNod[1]) poly_nodes.push_back( midlNod[1]);
4633 poly_nodes.push_back( nextNod[1] );
4635 if ( prevNod[0] != nextNod[0] )
4637 poly_nodes.push_back( nextNod[0] );
4638 if ( midlNod[0]) poly_nodes.push_back( midlNod[0]);
4640 switch ( poly_nodes.size() ) {
4642 aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ], poly_nodes[ 2 ]);
4645 aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ],
4646 poly_nodes[ 2 ], poly_nodes[ 3 ]);
4649 aNewElem = aMesh->AddPolygonalFace (poly_nodes);
4654 case SMDSEntity_Triangle: // TRIANGLE --->
4656 if ( nbDouble > 0 ) break;
4657 if ( nbSame == 0 ) // ---> pentahedron
4658 aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
4659 nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ] );
4661 else if ( nbSame == 1 ) // ---> pyramid
4662 aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
4663 nextNod[ iAfterSame ], nextNod[ iBeforeSame ],
4664 nextNod[ iSameNode ]);
4666 else // 2 same nodes: ---> tetrahedron
4667 aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
4668 nextNod[ iNotSameNode ]);
4671 case SMDSEntity_Quad_Edge: // sweep quadratic EDGE --->
4675 if ( nbDouble+nbSame == 2 )
4677 if(nbSame==0) { // ---> quadratic quadrangle
4678 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
4679 prevNod[2], midlNod[1], nextNod[2], midlNod[0]);
4681 else { //(nbSame==1) // ---> quadratic triangle
4683 return; // medium node on axis
4685 else if(sames[0]==0)
4686 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1],
4687 prevNod[2], midlNod[1], nextNod[2] );
4689 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[0],
4690 prevNod[2], nextNod[2], midlNod[0]);
4693 else if ( nbDouble == 3 )
4695 if ( nbSame == 0 ) { // ---> bi-quadratic quadrangle
4696 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
4697 prevNod[2], midlNod[1], nextNod[2], midlNod[0], midlNod[2]);
4704 case SMDSEntity_Quadrangle: { // sweep QUADRANGLE --->
4705 if ( nbDouble > 0 ) break;
4707 if ( nbSame == 0 ) // ---> hexahedron
4708 aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ], prevNod[ 3 ],
4709 nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ], nextNod[ 3 ]);
4711 else if ( nbSame == 1 ) { // ---> pyramid + pentahedron
4712 aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
4713 nextNod[ iAfterSame ], nextNod[ iBeforeSame ],
4714 nextNod[ iSameNode ]);
4715 newElems.push_back( aNewElem );
4716 aNewElem = aMesh->AddVolume (prevNod[ iAfterSame ], prevNod[ iOpposSame ],
4717 prevNod[ iBeforeSame ], nextNod[ iAfterSame ],
4718 nextNod[ iOpposSame ], nextNod[ iBeforeSame ] );
4720 else if ( nbSame == 2 ) { // ---> pentahedron
4721 if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] )
4722 // iBeforeSame is same too
4723 aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iOpposSame ],
4724 nextNod[ iOpposSame ], prevNod[ iSameNode ],
4725 prevNod[ iAfterSame ], nextNod[ iAfterSame ]);
4727 // iAfterSame is same too
4728 aNewElem = aMesh->AddVolume (prevNod[ iSameNode ], prevNod[ iBeforeSame ],
4729 nextNod[ iBeforeSame ], prevNod[ iAfterSame ],
4730 prevNod[ iOpposSame ], nextNod[ iOpposSame ]);
4734 case SMDSEntity_Quad_Triangle: // sweep (Bi)Quadratic TRIANGLE --->
4735 case SMDSEntity_BiQuad_Triangle: /* ??? */ {
4736 if ( nbDouble+nbSame != 3 ) break;
4738 // ---> pentahedron with 15 nodes
4739 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
4740 nextNod[0], nextNod[1], nextNod[2],
4741 prevNod[3], prevNod[4], prevNod[5],
4742 nextNod[3], nextNod[4], nextNod[5],
4743 midlNod[0], midlNod[1], midlNod[2]);
4745 else if(nbSame==1) {
4746 // ---> 2d order pyramid of 13 nodes
4747 int apex = iSameNode;
4748 int i0 = ( apex + 1 ) % nbCorners;
4749 int i1 = ( apex - 1 + nbCorners ) % nbCorners;
4753 aNewElem = aMesh->AddVolume(prevNod[i1], prevNod[i0],
4754 nextNod[i0], nextNod[i1], prevNod[apex],
4755 prevNod[i01], midlNod[i0],
4756 nextNod[i01], midlNod[i1],
4757 prevNod[i1a], prevNod[i0a],
4758 nextNod[i0a], nextNod[i1a]);
4760 else if(nbSame==2) {
4761 // ---> 2d order tetrahedron of 10 nodes
4762 int n1 = iNotSameNode;
4763 int n2 = ( n1 + 1 ) % nbCorners;
4764 int n3 = ( n1 + nbCorners - 1 ) % nbCorners;
4768 aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], prevNod[n3], nextNod[n1],
4769 prevNod[n12], prevNod[n23], prevNod[n31],
4770 midlNod[n1], nextNod[n12], nextNod[n31]);
4774 case SMDSEntity_Quad_Quadrangle: { // sweep Quadratic QUADRANGLE --->
4776 if ( nbDouble != 4 ) break;
4777 // ---> hexahedron with 20 nodes
4778 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
4779 nextNod[0], nextNod[1], nextNod[2], nextNod[3],
4780 prevNod[4], prevNod[5], prevNod[6], prevNod[7],
4781 nextNod[4], nextNod[5], nextNod[6], nextNod[7],
4782 midlNod[0], midlNod[1], midlNod[2], midlNod[3]);
4784 else if(nbSame==1) {
4785 // ---> pyramid + pentahedron - can not be created since it is needed
4786 // additional middle node at the center of face
4787 //INFOS( " Sweep for face " << elem->GetID() << " can not be created" );
4790 else if( nbSame == 2 ) {
4791 if ( nbDouble != 2 ) break;
4792 // ---> 2d order Pentahedron with 15 nodes
4794 if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] ) {
4795 // iBeforeSame is same too
4802 // iAfterSame is same too
4812 aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], nextNod[n2],
4813 prevNod[n4], prevNod[n5], nextNod[n5],
4814 prevNod[n12], midlNod[n2], nextNod[n12],
4815 prevNod[n45], midlNod[n5], nextNod[n45],
4816 prevNod[n14], prevNod[n25], nextNod[n25]);
4820 case SMDSEntity_BiQuad_Quadrangle: { // sweep BiQuadratic QUADRANGLE --->
4822 if( nbSame == 0 && nbDouble == 9 ) {
4823 // ---> tri-quadratic hexahedron with 27 nodes
4824 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
4825 nextNod[0], nextNod[1], nextNod[2], nextNod[3],
4826 prevNod[4], prevNod[5], prevNod[6], prevNod[7],
4827 nextNod[4], nextNod[5], nextNod[6], nextNod[7],
4828 midlNod[0], midlNod[1], midlNod[2], midlNod[3],
4829 prevNod[8], // bottom center
4830 midlNod[4], midlNod[5], midlNod[6], midlNod[7],
4831 nextNod[8], // top center
4832 midlNod[8]);// elem center
4840 case SMDSEntity_Polygon: { // sweep POLYGON
4842 if ( nbNodes == 6 && nbSame == 0 && nbDouble == 0 ) {
4843 // ---> hexagonal prism
4844 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
4845 prevNod[3], prevNod[4], prevNod[5],
4846 nextNod[0], nextNod[1], nextNod[2],
4847 nextNod[3], nextNod[4], nextNod[5]);
4851 case SMDSEntity_Ball:
4856 } // switch ( baseType )
4859 if ( !aNewElem && elem->GetType() == SMDSAbs_Face ) // try to create a polyherdal prism
4861 if ( baseType != SMDSEntity_Polygon )
4863 const std::vector<int>& ind = SMDS_MeshCell::interlacedSmdsOrder(baseType,nbNodes);
4864 SMDS_MeshCell::applyInterlace( ind, prevNod );
4865 SMDS_MeshCell::applyInterlace( ind, nextNod );
4866 SMDS_MeshCell::applyInterlace( ind, midlNod );
4867 SMDS_MeshCell::applyInterlace( ind, itNN );
4868 SMDS_MeshCell::applyInterlace( ind, isSingleNode );
4869 baseType = SMDSEntity_Polygon; // WARNING: change baseType !!!!
4871 vector<const SMDS_MeshNode*> polyedre_nodes (nbNodes*2 + 4*nbNodes);
4872 vector<int> quantities (nbNodes + 2);
4873 polyedre_nodes.clear();
4877 for (int inode = 0; inode < nbNodes; inode++)
4878 polyedre_nodes.push_back( prevNod[inode] );
4879 quantities.push_back( nbNodes );
4882 polyedre_nodes.push_back( nextNod[0] );
4883 for (int inode = nbNodes; inode-1; --inode )
4884 polyedre_nodes.push_back( nextNod[inode-1] );
4885 quantities.push_back( nbNodes );
4893 const int iQuad = elem->IsQuadratic();
4894 for (int iface = 0; iface < nbNodes; iface += 1+iQuad )
4896 const int prevNbNodes = polyedre_nodes.size(); // to detect degenerated face
4897 int inextface = (iface+1+iQuad) % nbNodes;
4898 int imid = (iface+1) % nbNodes;
4899 polyedre_nodes.push_back( prevNod[inextface] ); // 0
4900 if ( iQuad ) polyedre_nodes.push_back( prevNod[imid] ); // 4
4901 polyedre_nodes.push_back( prevNod[iface] ); // 1
4902 if ( prevNod[iface] != nextNod[iface] ) // 1 != 2
4904 if ( midlNod[ iface ]) polyedre_nodes.push_back( midlNod[ iface ]); // 5
4905 polyedre_nodes.push_back( nextNod[iface] ); // 2
4907 if ( iQuad ) polyedre_nodes.push_back( nextNod[imid] ); // 6
4908 if ( prevNod[inextface] != nextNod[inextface] ) // 0 != 3
4910 polyedre_nodes.push_back( nextNod[inextface] ); // 3
4911 if ( midlNod[ inextface ]) polyedre_nodes.push_back( midlNod[ inextface ]);// 7
4913 const int nbFaceNodes = polyedre_nodes.size() - prevNbNodes;
4914 if ( nbFaceNodes > 2 )
4915 quantities.push_back( nbFaceNodes );
4916 else // degenerated face
4917 polyedre_nodes.resize( prevNbNodes );
4919 aNewElem = aMesh->AddPolyhedralVolume (polyedre_nodes, quantities);
4921 } // try to create a polyherdal prism
4924 newElems.push_back( aNewElem );
4925 myLastCreatedElems.Append(aNewElem);
4926 srcElements.Append( elem );
4929 // set new prev nodes
4930 for ( iNode = 0; iNode < nbNodes; iNode++ )
4931 prevNod[ iNode ] = nextNod[ iNode ];
4936 //=======================================================================
4938 * \brief Create 1D and 2D elements around swept elements
4939 * \param mapNewNodes - source nodes and ones generated from them
4940 * \param newElemsMap - source elements and ones generated from them
4941 * \param elemNewNodesMap - nodes generated from each node of each element
4942 * \param elemSet - all swept elements
4943 * \param nbSteps - number of sweeping steps
4944 * \param srcElements - to append elem for each generated element
4946 //=======================================================================
4948 void SMESH_MeshEditor::makeWalls (TNodeOfNodeListMap & mapNewNodes,
4949 TTElemOfElemListMap & newElemsMap,
4950 TElemOfVecOfNnlmiMap & elemNewNodesMap,
4951 TIDSortedElemSet& elemSet,
4953 SMESH_SequenceOfElemPtr& srcElements)
4955 ASSERT( newElemsMap.size() == elemNewNodesMap.size() );
4956 SMESHDS_Mesh* aMesh = GetMeshDS();
4958 // Find nodes belonging to only one initial element - sweep them into edges.
4960 TNodeOfNodeListMapItr nList = mapNewNodes.begin();
4961 for ( ; nList != mapNewNodes.end(); nList++ )
4963 const SMDS_MeshNode* node =
4964 static_cast<const SMDS_MeshNode*>( nList->first );
4965 if ( newElemsMap.count( node ))
4966 continue; // node was extruded into edge
4967 SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator();
4968 int nbInitElems = 0;
4969 const SMDS_MeshElement* el = 0;
4970 SMDSAbs_ElementType highType = SMDSAbs_Edge; // count most complex elements only
4971 while ( eIt->more() && nbInitElems < 2 ) {
4972 const SMDS_MeshElement* e = eIt->next();
4973 SMDSAbs_ElementType type = e->GetType();
4974 if ( type == SMDSAbs_Volume ||
4978 if ( type > highType ) {
4985 if ( nbInitElems == 1 ) {
4986 bool NotCreateEdge = el && el->IsMediumNode(node);
4987 if(!NotCreateEdge) {
4988 vector<TNodeOfNodeListMapItr> newNodesItVec( 1, nList );
4989 list<const SMDS_MeshElement*> newEdges;
4990 sweepElement( node, newNodesItVec, newEdges, nbSteps, srcElements );
4995 // Make a ceiling for each element ie an equal element of last new nodes.
4996 // Find free links of faces - make edges and sweep them into faces.
4998 ElemFeatures polyFace( SMDSAbs_Face, /*isPoly=*/true ), anyFace;
5000 TTElemOfElemListMap::iterator itElem = newElemsMap.begin();
5001 TElemOfVecOfNnlmiMap::iterator itElemNodes = elemNewNodesMap.begin();
5002 for ( ; itElem != newElemsMap.end(); itElem++, itElemNodes++ )
5004 const SMDS_MeshElement* elem = itElem->first;
5005 vector<TNodeOfNodeListMapItr>& vecNewNodes = itElemNodes->second;
5007 if(itElem->second.size()==0) continue;
5009 const bool isQuadratic = elem->IsQuadratic();
5011 if ( elem->GetType() == SMDSAbs_Edge ) {
5012 // create a ceiling edge
5013 if ( !isQuadratic ) {
5014 if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
5015 vecNewNodes[ 1 ]->second.back())) {
5016 myLastCreatedElems.Append(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
5017 vecNewNodes[ 1 ]->second.back()));
5018 srcElements.Append( elem );
5022 if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
5023 vecNewNodes[ 1 ]->second.back(),
5024 vecNewNodes[ 2 ]->second.back())) {
5025 myLastCreatedElems.Append(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
5026 vecNewNodes[ 1 ]->second.back(),
5027 vecNewNodes[ 2 ]->second.back()));
5028 srcElements.Append( elem );
5032 if ( elem->GetType() != SMDSAbs_Face )
5035 bool hasFreeLinks = false;
5037 TIDSortedElemSet avoidSet;
5038 avoidSet.insert( elem );
5040 set<const SMDS_MeshNode*> aFaceLastNodes;
5041 int iNode, nbNodes = vecNewNodes.size();
5042 if ( !isQuadratic ) {
5043 // loop on the face nodes
5044 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
5045 aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
5046 // look for free links of the face
5047 int iNext = ( iNode + 1 == nbNodes ) ? 0 : iNode + 1;
5048 const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
5049 const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
5050 // check if a link n1-n2 is free
5051 if ( ! SMESH_MeshAlgos::FindFaceInSet ( n1, n2, elemSet, avoidSet )) {
5052 hasFreeLinks = true;
5053 // make a new edge and a ceiling for a new edge
5054 const SMDS_MeshElement* edge;
5055 if ( ! ( edge = aMesh->FindEdge( n1, n2 ))) {
5056 myLastCreatedElems.Append( edge = aMesh->AddEdge( n1, n2 )); // free link edge
5057 srcElements.Append( myLastCreatedElems.Last() );
5059 n1 = vecNewNodes[ iNode ]->second.back();
5060 n2 = vecNewNodes[ iNext ]->second.back();
5061 if ( !aMesh->FindEdge( n1, n2 )) {
5062 myLastCreatedElems.Append(aMesh->AddEdge( n1, n2 )); // new edge ceiling
5063 srcElements.Append( edge );
5068 else { // elem is quadratic face
5069 int nbn = nbNodes/2;
5070 for ( iNode = 0; iNode < nbn; iNode++ ) {
5071 aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
5072 int iNext = ( iNode + 1 == nbn ) ? 0 : iNode + 1;
5073 const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
5074 const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
5075 const SMDS_MeshNode* n3 = vecNewNodes[ iNode+nbn ]->first;
5076 // check if a link is free
5077 if ( ! SMESH_MeshAlgos::FindFaceInSet ( n1, n2, elemSet, avoidSet ) &&
5078 ! SMESH_MeshAlgos::FindFaceInSet ( n1, n3, elemSet, avoidSet ) &&
5079 ! SMESH_MeshAlgos::FindFaceInSet ( n3, n2, elemSet, avoidSet ) ) {
5080 hasFreeLinks = true;
5081 // make an edge and a ceiling for a new edge
5083 if ( !aMesh->FindEdge( n1, n2, n3 )) {
5084 myLastCreatedElems.Append(aMesh->AddEdge( n1, n2, n3 )); // free link edge
5085 srcElements.Append( elem );
5087 n1 = vecNewNodes[ iNode ]->second.back();
5088 n2 = vecNewNodes[ iNext ]->second.back();
5089 n3 = vecNewNodes[ iNode+nbn ]->second.back();
5090 if ( !aMesh->FindEdge( n1, n2, n3 )) {
5091 myLastCreatedElems.Append(aMesh->AddEdge( n1, n2, n3 )); // ceiling edge
5092 srcElements.Append( elem );
5096 for ( iNode = nbn; iNode < nbNodes; iNode++ ) {
5097 aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
5101 // sweep free links into faces
5103 if ( hasFreeLinks ) {
5104 list<const SMDS_MeshElement*> & newVolumes = itElem->second;
5105 int iVol, volNb, nbVolumesByStep = newVolumes.size() / nbSteps;
5107 set<const SMDS_MeshNode*> initNodeSet, topNodeSet, faceNodeSet;
5108 set<const SMDS_MeshNode*> initNodeSetNoCenter/*, topNodeSetNoCenter*/;
5109 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
5110 initNodeSet.insert( vecNewNodes[ iNode ]->first );
5111 topNodeSet .insert( vecNewNodes[ iNode ]->second.back() );
5113 if ( isQuadratic && nbNodes % 2 ) { // node set for the case of a biquadratic
5114 initNodeSetNoCenter = initNodeSet; // swept face and a not biquadratic volume
5115 initNodeSetNoCenter.erase( vecNewNodes.back()->first );
5117 for ( volNb = 0; volNb < nbVolumesByStep; volNb++ ) {
5118 list<const SMDS_MeshElement*>::iterator v = newVolumes.begin();
5119 std::advance( v, volNb );
5120 // find indices of free faces of a volume and their source edges
5121 list< int > freeInd;
5122 list< const SMDS_MeshElement* > srcEdges; // source edges of free faces
5123 SMDS_VolumeTool vTool( *v, /*ignoreCentralNodes=*/false );
5124 int iF, nbF = vTool.NbFaces();
5125 for ( iF = 0; iF < nbF; iF ++ ) {
5126 if (vTool.IsFreeFace( iF ) &&
5127 vTool.GetFaceNodes( iF, faceNodeSet ) &&
5128 initNodeSet != faceNodeSet) // except an initial face
5130 if ( nbSteps == 1 && faceNodeSet == topNodeSet )
5132 if ( faceNodeSet == initNodeSetNoCenter )
5134 freeInd.push_back( iF );
5135 // find source edge of a free face iF
5136 vector<const SMDS_MeshNode*> commonNodes; // shared by the initial and free faces
5137 vector<const SMDS_MeshNode*>::iterator lastCommom;
5138 commonNodes.resize( nbNodes, 0 );
5139 lastCommom = std::set_intersection( faceNodeSet.begin(), faceNodeSet.end(),
5140 initNodeSet.begin(), initNodeSet.end(),
5141 commonNodes.begin());
5142 if ( std::distance( commonNodes.begin(), lastCommom ) == 3 )
5143 srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1],commonNodes[2]));
5145 srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1]));
5147 if ( !srcEdges.back() )
5149 cout << "SMESH_MeshEditor::makeWalls(), no source edge found for a free face #"
5150 << iF << " of volume #" << vTool.ID() << endl;
5155 if ( freeInd.empty() )
5158 // create wall faces for all steps;
5159 // if such a face has been already created by sweep of edge,
5160 // assure that its orientation is OK
5161 for ( int iStep = 0; iStep < nbSteps; iStep++ )
5163 vTool.Set( *v, /*ignoreCentralNodes=*/false );
5164 vTool.SetExternalNormal();
5165 const int nextShift = vTool.IsForward() ? +1 : -1;
5166 list< int >::iterator ind = freeInd.begin();
5167 list< const SMDS_MeshElement* >::iterator srcEdge = srcEdges.begin();
5168 for ( ; ind != freeInd.end(); ++ind, ++srcEdge ) // loop on free faces
5170 const SMDS_MeshNode** nodes = vTool.GetFaceNodes( *ind );
5171 int nbn = vTool.NbFaceNodes( *ind );
5172 const SMDS_MeshElement * f = 0;
5173 if ( nbn == 3 ) ///// triangle
5175 f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ]);
5177 nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
5179 const SMDS_MeshNode* newOrder[3] = { nodes[ 1 - nextShift ],
5181 nodes[ 1 + nextShift ] };
5183 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5185 myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
5189 else if ( nbn == 4 ) ///// quadrangle
5191 f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ], nodes[ 3 ]);
5193 nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
5195 const SMDS_MeshNode* newOrder[4] = { nodes[ 0 ], nodes[ 2-nextShift ],
5196 nodes[ 2 ], nodes[ 2+nextShift ] };
5198 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5200 myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
5201 newOrder[ 2 ], newOrder[ 3 ]));
5204 else if ( nbn == 6 && isQuadratic ) /////// quadratic triangle
5206 f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[1], nodes[3], nodes[5] );
5208 nodes[2] != f->GetNodeWrap( f->GetNodeIndex( nodes[0] ) + 2*nextShift ))
5210 const SMDS_MeshNode* newOrder[6] = { nodes[2 - 2*nextShift],
5212 nodes[2 + 2*nextShift],
5213 nodes[3 - 2*nextShift],
5215 nodes[3 + 2*nextShift]};
5217 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5219 myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ],
5227 else if ( nbn == 8 && isQuadratic ) /////// quadratic quadrangle
5229 f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[6],
5230 nodes[1], nodes[3], nodes[5], nodes[7] );
5232 nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
5234 const SMDS_MeshNode* newOrder[8] = { nodes[0],
5235 nodes[4 - 2*nextShift],
5237 nodes[4 + 2*nextShift],
5239 nodes[5 - 2*nextShift],
5241 nodes[5 + 2*nextShift] };
5243 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5245 myLastCreatedElems.Append(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
5246 newOrder[ 2 ], newOrder[ 3 ],
5247 newOrder[ 4 ], newOrder[ 5 ],
5248 newOrder[ 6 ], newOrder[ 7 ]));
5251 else if ( nbn == 9 && isQuadratic ) /////// bi-quadratic quadrangle
5253 f = aMesh->FindElement( vector<const SMDS_MeshNode*>( nodes, nodes+nbn ),
5254 SMDSAbs_Face, /*noMedium=*/false);
5256 nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
5258 const SMDS_MeshNode* newOrder[9] = { nodes[0],
5259 nodes[4 - 2*nextShift],
5261 nodes[4 + 2*nextShift],
5263 nodes[5 - 2*nextShift],
5265 nodes[5 + 2*nextShift],
5268 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5270 myLastCreatedElems.Append(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
5271 newOrder[ 2 ], newOrder[ 3 ],
5272 newOrder[ 4 ], newOrder[ 5 ],
5273 newOrder[ 6 ], newOrder[ 7 ],
5277 else //////// polygon
5279 vector<const SMDS_MeshNode*> polygon_nodes ( nodes, nodes+nbn );
5280 const SMDS_MeshFace * f = aMesh->FindFace( polygon_nodes );
5282 nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + nextShift ))
5284 if ( !vTool.IsForward() )
5285 std::reverse( polygon_nodes.begin(), polygon_nodes.end());
5287 aMesh->ChangeElementNodes( f, &polygon_nodes[0], nbn );
5289 AddElement( polygon_nodes, polyFace.SetQuad( (*v)->IsQuadratic() ));
5293 while ( srcElements.Length() < myLastCreatedElems.Length() )
5294 srcElements.Append( *srcEdge );
5296 } // loop on free faces
5298 // go to the next volume
5300 while ( iVol++ < nbVolumesByStep ) v++;
5303 } // loop on volumes of one step
5304 } // sweep free links into faces
5306 // Make a ceiling face with a normal external to a volume
5308 // use SMDS_VolumeTool to get a correctly ordered nodes of a ceiling face
5309 SMDS_VolumeTool lastVol( itElem->second.back(), /*ignoreCentralNodes=*/false );
5310 int iF = lastVol.GetFaceIndex( aFaceLastNodes );
5312 if ( iF < 0 && isQuadratic && nbNodes % 2 ) { // remove a central node of biquadratic
5313 aFaceLastNodes.erase( vecNewNodes.back()->second.back() );
5314 iF = lastVol.GetFaceIndex( aFaceLastNodes );
5318 lastVol.SetExternalNormal();
5319 const SMDS_MeshNode** nodes = lastVol.GetFaceNodes( iF );
5320 const int nbn = lastVol.NbFaceNodes( iF );
5321 vector<const SMDS_MeshNode*> nodeVec( nodes, nodes+nbn );
5322 if ( !hasFreeLinks ||
5323 !aMesh->FindElement( nodeVec, SMDSAbs_Face, /*noMedium=*/false) )
5325 const vector<int>& interlace =
5326 SMDS_MeshCell::interlacedSmdsOrder( elem->GetEntityType(), nbn );
5327 SMDS_MeshCell::applyInterlaceRev( interlace, nodeVec );
5329 AddElement( nodeVec, anyFace.Init( elem ));
5331 while ( srcElements.Length() < myLastCreatedElems.Length() )
5332 srcElements.Append( elem );
5335 } // loop on swept elements
5338 //=======================================================================
5339 //function : RotationSweep
5341 //=======================================================================
5343 SMESH_MeshEditor::PGroupIDs
5344 SMESH_MeshEditor::RotationSweep(TIDSortedElemSet theElemSets[2],
5345 const gp_Ax1& theAxis,
5346 const double theAngle,
5347 const int theNbSteps,
5348 const double theTol,
5349 const bool theMakeGroups,
5350 const bool theMakeWalls)
5352 myLastCreatedElems.Clear();
5353 myLastCreatedNodes.Clear();
5355 // source elements for each generated one
5356 SMESH_SequenceOfElemPtr srcElems, srcNodes;
5359 aTrsf.SetRotation( theAxis, theAngle );
5361 aTrsf2.SetRotation( theAxis, theAngle/2. );
5363 gp_Lin aLine( theAxis );
5364 double aSqTol = theTol * theTol;
5366 SMESHDS_Mesh* aMesh = GetMeshDS();
5368 TNodeOfNodeListMap mapNewNodes;
5369 TElemOfVecOfNnlmiMap mapElemNewNodes;
5370 TTElemOfElemListMap newElemsMap;
5372 const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
5373 myMesh->NbFaces(ORDER_QUADRATIC) +
5374 myMesh->NbVolumes(ORDER_QUADRATIC) );
5375 // loop on theElemSets
5376 setElemsFirst( theElemSets );
5377 TIDSortedElemSet::iterator itElem;
5378 for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
5380 TIDSortedElemSet& theElems = theElemSets[ is2ndSet ];
5381 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
5382 const SMDS_MeshElement* elem = *itElem;
5383 if ( !elem || elem->GetType() == SMDSAbs_Volume )
5385 vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5386 newNodesItVec.reserve( elem->NbNodes() );
5388 // loop on elem nodes
5389 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5390 while ( itN->more() )
5392 const SMDS_MeshNode* node = cast2Node( itN->next() );
5394 gp_XYZ aXYZ( node->X(), node->Y(), node->Z() );
5396 aXYZ.Coord( coord[0], coord[1], coord[2] );
5397 bool isOnAxis = ( aLine.SquareDistance( aXYZ ) <= aSqTol );
5399 // check if a node has been already sweeped
5400 TNodeOfNodeListMapItr nIt =
5401 mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5402 list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5403 if ( listNewNodes.empty() )
5405 // check if we are to create medium nodes between corner ones
5406 bool needMediumNodes = false;
5407 if ( isQuadraticMesh )
5409 SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
5410 while (it->more() && !needMediumNodes )
5412 const SMDS_MeshElement* invElem = it->next();
5413 if ( invElem != elem && !theElems.count( invElem )) continue;
5414 needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
5415 if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
5416 needMediumNodes = true;
5421 const SMDS_MeshNode * newNode = node;
5422 for ( int i = 0; i < theNbSteps; i++ ) {
5424 if ( needMediumNodes ) // create a medium node
5426 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
5427 newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5428 myLastCreatedNodes.Append(newNode);
5429 srcNodes.Append( node );
5430 listNewNodes.push_back( newNode );
5431 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
5434 aTrsf.Transforms( coord[0], coord[1], coord[2] );
5436 // create a corner node
5437 newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5438 myLastCreatedNodes.Append(newNode);
5439 srcNodes.Append( node );
5440 listNewNodes.push_back( newNode );
5443 listNewNodes.push_back( newNode );
5444 // if ( needMediumNodes )
5445 // listNewNodes.push_back( newNode );
5449 newNodesItVec.push_back( nIt );
5451 // make new elements
5452 sweepElement( elem, newNodesItVec, newElemsMap[elem], theNbSteps, srcElems );
5457 makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElemSets[0], theNbSteps, srcElems );
5459 PGroupIDs newGroupIDs;
5460 if ( theMakeGroups )
5461 newGroupIDs = generateGroups( srcNodes, srcElems, "rotated");
5466 //=======================================================================
5467 //function : ExtrusParam
5468 //purpose : standard construction
5469 //=======================================================================
5471 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const gp_Vec& theStep,
5472 const int theNbSteps,
5474 const double theTolerance):
5476 myFlags( theFlags ),
5477 myTolerance( theTolerance ),
5478 myElemsToUse( NULL )
5480 mySteps = new TColStd_HSequenceOfReal;
5481 const double stepSize = theStep.Magnitude();
5482 for (int i=1; i<=theNbSteps; i++ )
5483 mySteps->Append( stepSize );
5485 if (( theFlags & EXTRUSION_FLAG_SEW ) &&
5486 ( theTolerance > 0 ))
5488 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDirAndSew;
5492 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDir;
5496 //=======================================================================
5497 //function : ExtrusParam
5498 //purpose : steps are given explicitly
5499 //=======================================================================
5501 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const gp_Dir& theDir,
5502 Handle(TColStd_HSequenceOfReal) theSteps,
5504 const double theTolerance):
5506 mySteps( theSteps ),
5507 myFlags( theFlags ),
5508 myTolerance( theTolerance ),
5509 myElemsToUse( NULL )
5511 if (( theFlags & EXTRUSION_FLAG_SEW ) &&
5512 ( theTolerance > 0 ))
5514 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDirAndSew;
5518 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDir;
5522 //=======================================================================
5523 //function : ExtrusParam
5524 //purpose : for extrusion by normal
5525 //=======================================================================
5527 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const double theStepSize,
5528 const int theNbSteps,
5532 mySteps( new TColStd_HSequenceOfReal ),
5533 myFlags( theFlags ),
5535 myElemsToUse( NULL )
5537 for (int i = 0; i < theNbSteps; i++ )
5538 mySteps->Append( theStepSize );
5542 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByNormal1D;
5546 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByNormal2D;
5550 //=======================================================================
5551 //function : ExtrusParam::SetElementsToUse
5552 //purpose : stores elements to use for extrusion by normal, depending on
5553 // state of EXTRUSION_FLAG_USE_INPUT_ELEMS_ONLY flag
5554 //=======================================================================
5556 void SMESH_MeshEditor::ExtrusParam::SetElementsToUse( const TIDSortedElemSet& elems )
5558 myElemsToUse = ToUseInpElemsOnly() ? & elems : 0;
5561 //=======================================================================
5562 //function : ExtrusParam::beginStepIter
5563 //purpose : prepare iteration on steps
5564 //=======================================================================
5566 void SMESH_MeshEditor::ExtrusParam::beginStepIter( bool withMediumNodes )
5568 myWithMediumNodes = withMediumNodes;
5572 //=======================================================================
5573 //function : ExtrusParam::moreSteps
5574 //purpose : are there more steps?
5575 //=======================================================================
5577 bool SMESH_MeshEditor::ExtrusParam::moreSteps()
5579 return myNextStep <= mySteps->Length() || !myCurSteps.empty();
5581 //=======================================================================
5582 //function : ExtrusParam::nextStep
5583 //purpose : returns the next step
5584 //=======================================================================
5586 double SMESH_MeshEditor::ExtrusParam::nextStep()
5589 if ( !myCurSteps.empty() )
5591 res = myCurSteps.back();
5592 myCurSteps.pop_back();
5594 else if ( myNextStep <= mySteps->Length() )
5596 myCurSteps.push_back( mySteps->Value( myNextStep ));
5598 if ( myWithMediumNodes )
5600 myCurSteps.back() /= 2.;
5601 myCurSteps.push_back( myCurSteps.back() );
5608 //=======================================================================
5609 //function : ExtrusParam::makeNodesByDir
5610 //purpose : create nodes for standard extrusion
5611 //=======================================================================
5613 int SMESH_MeshEditor::ExtrusParam::
5614 makeNodesByDir( SMESHDS_Mesh* mesh,
5615 const SMDS_MeshNode* srcNode,
5616 std::list<const SMDS_MeshNode*> & newNodes,
5617 const bool makeMediumNodes)
5619 gp_XYZ p = SMESH_TNodeXYZ( srcNode );
5622 for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5624 p += myDir.XYZ() * nextStep();
5625 const SMDS_MeshNode * newNode = mesh->AddNode( p.X(), p.Y(), p.Z() );
5626 newNodes.push_back( newNode );
5631 //=======================================================================
5632 //function : ExtrusParam::makeNodesByDirAndSew
5633 //purpose : create nodes for standard extrusion with sewing
5634 //=======================================================================
5636 int SMESH_MeshEditor::ExtrusParam::
5637 makeNodesByDirAndSew( SMESHDS_Mesh* mesh,
5638 const SMDS_MeshNode* srcNode,
5639 std::list<const SMDS_MeshNode*> & newNodes,
5640 const bool makeMediumNodes)
5642 gp_XYZ P1 = SMESH_TNodeXYZ( srcNode );
5645 for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5647 P1 += myDir.XYZ() * nextStep();
5649 // try to search in sequence of existing nodes
5650 // if myNodes.Length()>0 we 'nave to use given sequence
5651 // else - use all nodes of mesh
5652 const SMDS_MeshNode * node = 0;
5653 if ( myNodes.Length() > 0 ) {
5655 for(i=1; i<=myNodes.Length(); i++) {
5656 gp_XYZ P2 = SMESH_TNodeXYZ( myNodes.Value(i) );
5657 if (( P1 - P2 ).SquareModulus() < myTolerance * myTolerance )
5659 node = myNodes.Value(i);
5665 SMDS_NodeIteratorPtr itn = mesh->nodesIterator();
5666 while(itn->more()) {
5667 SMESH_TNodeXYZ P2( itn->next() );
5668 if (( P1 - P2 ).SquareModulus() < myTolerance * myTolerance )
5677 node = mesh->AddNode( P1.X(), P1.Y(), P1.Z() );
5679 newNodes.push_back( node );
5686 //=======================================================================
5687 //function : ExtrusParam::makeNodesByNormal2D
5688 //purpose : create nodes for extrusion using normals of faces
5689 //=======================================================================
5691 int SMESH_MeshEditor::ExtrusParam::
5692 makeNodesByNormal2D( SMESHDS_Mesh* mesh,
5693 const SMDS_MeshNode* srcNode,
5694 std::list<const SMDS_MeshNode*> & newNodes,
5695 const bool makeMediumNodes)
5697 const bool alongAvgNorm = ( myFlags & EXTRUSION_FLAG_BY_AVG_NORMAL );
5699 gp_XYZ p = SMESH_TNodeXYZ( srcNode );
5701 // get normals to faces sharing srcNode
5702 vector< gp_XYZ > norms, baryCenters;
5703 gp_XYZ norm, avgNorm( 0,0,0 );
5704 SMDS_ElemIteratorPtr faceIt = srcNode->GetInverseElementIterator( SMDSAbs_Face );
5705 while ( faceIt->more() )
5707 const SMDS_MeshElement* face = faceIt->next();
5708 if ( myElemsToUse && !myElemsToUse->count( face ))
5710 if ( SMESH_MeshAlgos::FaceNormal( face, norm, /*normalized=*/true ))
5712 norms.push_back( norm );
5714 if ( !alongAvgNorm )
5718 for ( SMDS_ElemIteratorPtr nIt = face->nodesIterator(); nIt->more(); ++nbN )
5719 bc += SMESH_TNodeXYZ( nIt->next() );
5720 baryCenters.push_back( bc / nbN );
5725 if ( norms.empty() ) return 0;
5727 double normSize = avgNorm.Modulus();
5728 if ( normSize < std::numeric_limits<double>::min() )
5731 if ( myFlags & EXTRUSION_FLAG_BY_AVG_NORMAL ) // extrude along avgNorm
5734 return makeNodesByDir( mesh, srcNode, newNodes, makeMediumNodes );
5737 avgNorm /= normSize;
5740 for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5743 double stepSize = nextStep();
5745 if ( norms.size() > 1 )
5747 for ( size_t iF = 0; iF < norms.size(); ++iF ) // loop on faces
5749 // translate plane of a face
5750 baryCenters[ iF ] += norms[ iF ] * stepSize;
5752 // find point of intersection of the face plane located at baryCenters[ iF ]
5753 // and avgNorm located at pNew
5754 double d = -( norms[ iF ] * baryCenters[ iF ]); // d of plane equation ax+by+cz+d=0
5755 double dot = ( norms[ iF ] * avgNorm );
5756 if ( dot < std::numeric_limits<double>::min() )
5757 dot = stepSize * 1e-3;
5758 double step = -( norms[ iF ] * pNew + d ) / dot;
5759 pNew += step * avgNorm;
5764 pNew += stepSize * avgNorm;
5768 const SMDS_MeshNode * newNode = mesh->AddNode( p.X(), p.Y(), p.Z() );
5769 newNodes.push_back( newNode );
5774 //=======================================================================
5775 //function : ExtrusParam::makeNodesByNormal1D
5776 //purpose : create nodes for extrusion using normals of edges
5777 //=======================================================================
5779 int SMESH_MeshEditor::ExtrusParam::
5780 makeNodesByNormal1D( SMESHDS_Mesh* mesh,
5781 const SMDS_MeshNode* srcNode,
5782 std::list<const SMDS_MeshNode*> & newNodes,
5783 const bool makeMediumNodes)
5785 throw SALOME_Exception("Extrusion 1D by Normal not implemented");
5789 //=======================================================================
5790 //function : ExtrusionSweep
5792 //=======================================================================
5794 SMESH_MeshEditor::PGroupIDs
5795 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet theElems[2],
5796 const gp_Vec& theStep,
5797 const int theNbSteps,
5798 TTElemOfElemListMap& newElemsMap,
5800 const double theTolerance)
5802 ExtrusParam aParams( theStep, theNbSteps, theFlags, theTolerance );
5803 return ExtrusionSweep( theElems, aParams, newElemsMap );
5807 //=======================================================================
5808 //function : ExtrusionSweep
5810 //=======================================================================
5812 SMESH_MeshEditor::PGroupIDs
5813 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet theElemSets[2],
5814 ExtrusParam& theParams,
5815 TTElemOfElemListMap& newElemsMap)
5817 myLastCreatedElems.Clear();
5818 myLastCreatedNodes.Clear();
5820 // source elements for each generated one
5821 SMESH_SequenceOfElemPtr srcElems, srcNodes;
5823 //SMESHDS_Mesh* aMesh = GetMeshDS();
5825 setElemsFirst( theElemSets );
5826 const int nbSteps = theParams.NbSteps();
5827 theParams.SetElementsToUse( theElemSets[0] );
5829 TNodeOfNodeListMap mapNewNodes;
5830 //TNodeOfNodeVecMap mapNewNodes;
5831 TElemOfVecOfNnlmiMap mapElemNewNodes;
5832 //TElemOfVecOfMapNodesMap mapElemNewNodes;
5834 const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
5835 myMesh->NbFaces(ORDER_QUADRATIC) +
5836 myMesh->NbVolumes(ORDER_QUADRATIC) );
5838 TIDSortedElemSet::iterator itElem;
5839 for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
5841 TIDSortedElemSet& theElems = theElemSets[ is2ndSet ];
5842 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
5844 // check element type
5845 const SMDS_MeshElement* elem = *itElem;
5846 if ( !elem || elem->GetType() == SMDSAbs_Volume )
5849 const size_t nbNodes = elem->NbNodes();
5850 vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5851 newNodesItVec.reserve( nbNodes );
5853 // loop on elem nodes
5854 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5855 while ( itN->more() )
5857 // check if a node has been already sweeped
5858 const SMDS_MeshNode* node = cast2Node( itN->next() );
5859 TNodeOfNodeListMap::iterator nIt =
5860 mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5861 list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5862 if ( listNewNodes.empty() )
5866 // check if we are to create medium nodes between corner ones
5867 bool needMediumNodes = false;
5868 if ( isQuadraticMesh )
5870 SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
5871 while (it->more() && !needMediumNodes )
5873 const SMDS_MeshElement* invElem = it->next();
5874 if ( invElem != elem && !theElems.count( invElem )) continue;
5875 needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
5876 if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
5877 needMediumNodes = true;
5880 // create nodes for all steps
5881 if ( theParams.MakeNodes( GetMeshDS(), node, listNewNodes, needMediumNodes ))
5883 list<const SMDS_MeshNode*>::iterator newNodesIt = listNewNodes.begin();
5884 for ( ; newNodesIt != listNewNodes.end(); ++newNodesIt )
5886 myLastCreatedNodes.Append( *newNodesIt );
5887 srcNodes.Append( node );
5892 break; // newNodesItVec will be shorter than nbNodes
5895 newNodesItVec.push_back( nIt );
5897 // make new elements
5898 if ( newNodesItVec.size() == nbNodes )
5899 sweepElement( elem, newNodesItVec, newElemsMap[elem], nbSteps, srcElems );
5903 if ( theParams.ToMakeBoundary() ) {
5904 makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElemSets[0], nbSteps, srcElems );
5906 PGroupIDs newGroupIDs;
5907 if ( theParams.ToMakeGroups() )
5908 newGroupIDs = generateGroups( srcNodes, srcElems, "extruded");
5913 //=======================================================================
5914 //function : ExtrusionAlongTrack
5916 //=======================================================================
5917 SMESH_MeshEditor::Extrusion_Error
5918 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet theElements[2],
5919 SMESH_subMesh* theTrack,
5920 const SMDS_MeshNode* theN1,
5921 const bool theHasAngles,
5922 list<double>& theAngles,
5923 const bool theLinearVariation,
5924 const bool theHasRefPoint,
5925 const gp_Pnt& theRefPoint,
5926 const bool theMakeGroups)
5928 myLastCreatedElems.Clear();
5929 myLastCreatedNodes.Clear();
5932 std::list<double> aPrms;
5933 TIDSortedElemSet::iterator itElem;
5936 TopoDS_Edge aTrackEdge;
5937 TopoDS_Vertex aV1, aV2;
5939 SMDS_ElemIteratorPtr aItE;
5940 SMDS_NodeIteratorPtr aItN;
5941 SMDSAbs_ElementType aTypeE;
5943 TNodeOfNodeListMap mapNewNodes;
5946 aNbE = theElements[0].size() + theElements[1].size();
5949 return EXTR_NO_ELEMENTS;
5951 // 1.1 Track Pattern
5954 SMESHDS_SubMesh* pSubMeshDS = theTrack->GetSubMeshDS();
5956 return ExtrusionAlongTrack( theElements, theTrack->GetFather(), theN1,
5957 theHasAngles, theAngles, theLinearVariation,
5958 theHasRefPoint, theRefPoint, theMakeGroups );
5960 aItE = pSubMeshDS->GetElements();
5961 while ( aItE->more() ) {
5962 const SMDS_MeshElement* pE = aItE->next();
5963 aTypeE = pE->GetType();
5964 // Pattern must contain links only
5965 if ( aTypeE != SMDSAbs_Edge )
5966 return EXTR_PATH_NOT_EDGE;
5969 list<SMESH_MeshEditor_PathPoint> fullList;
5971 const TopoDS_Shape& aS = theTrack->GetSubShape();
5972 // Sub-shape for the Pattern must be an Edge or Wire
5973 if( aS.ShapeType() == TopAbs_EDGE ) {
5974 aTrackEdge = TopoDS::Edge( aS );
5975 // the Edge must not be degenerated
5976 if ( SMESH_Algo::isDegenerated( aTrackEdge ) )
5977 return EXTR_BAD_PATH_SHAPE;
5978 TopExp::Vertices( aTrackEdge, aV1, aV2 );
5979 aItN = theTrack->GetFather()->GetSubMesh( aV1 )->GetSubMeshDS()->GetNodes();
5980 const SMDS_MeshNode* aN1 = aItN->next();
5981 aItN = theTrack->GetFather()->GetSubMesh( aV2 )->GetSubMeshDS()->GetNodes();
5982 const SMDS_MeshNode* aN2 = aItN->next();
5983 // starting node must be aN1 or aN2
5984 if ( !( aN1 == theN1 || aN2 == theN1 ) )
5985 return EXTR_BAD_STARTING_NODE;
5986 aItN = pSubMeshDS->GetNodes();
5987 while ( aItN->more() ) {
5988 const SMDS_MeshNode* pNode = aItN->next();
5989 const SMDS_EdgePosition* pEPos =
5990 static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
5991 double aT = pEPos->GetUParameter();
5992 aPrms.push_back( aT );
5994 //Extrusion_Error err =
5995 MakeEdgePathPoints(aPrms, aTrackEdge, (aN1==theN1), fullList);
5996 } else if( aS.ShapeType() == TopAbs_WIRE ) {
5997 list< SMESH_subMesh* > LSM;
5998 TopTools_SequenceOfShape Edges;
5999 SMESH_subMeshIteratorPtr itSM = theTrack->getDependsOnIterator(false,true);
6000 while(itSM->more()) {
6001 SMESH_subMesh* SM = itSM->next();
6003 const TopoDS_Shape& aS = SM->GetSubShape();
6006 list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
6007 int startNid = theN1->GetID();
6008 TColStd_MapOfInteger UsedNums;
6010 int NbEdges = Edges.Length();
6012 for(; i<=NbEdges; i++) {
6014 list< SMESH_subMesh* >::iterator itLSM = LSM.begin();
6015 for(; itLSM!=LSM.end(); itLSM++) {
6017 if(UsedNums.Contains(k)) continue;
6018 aTrackEdge = TopoDS::Edge( Edges.Value(k) );
6019 SMESH_subMesh* locTrack = *itLSM;
6020 SMESHDS_SubMesh* locMeshDS = locTrack->GetSubMeshDS();
6021 TopExp::Vertices( aTrackEdge, aV1, aV2 );
6022 aItN = locTrack->GetFather()->GetSubMesh(aV1)->GetSubMeshDS()->GetNodes();
6023 const SMDS_MeshNode* aN1 = aItN->next();
6024 aItN = locTrack->GetFather()->GetSubMesh(aV2)->GetSubMeshDS()->GetNodes();
6025 const SMDS_MeshNode* aN2 = aItN->next();
6026 // starting node must be aN1 or aN2
6027 if ( !( aN1->GetID() == startNid || aN2->GetID() == startNid ) ) continue;
6028 // 2. Collect parameters on the track edge
6030 aItN = locMeshDS->GetNodes();
6031 while ( aItN->more() ) {
6032 const SMDS_MeshNode* pNode = aItN->next();
6033 const SMDS_EdgePosition* pEPos =
6034 static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
6035 double aT = pEPos->GetUParameter();
6036 aPrms.push_back( aT );
6038 list<SMESH_MeshEditor_PathPoint> LPP;
6039 //Extrusion_Error err =
6040 MakeEdgePathPoints(aPrms, aTrackEdge,(aN1->GetID()==startNid), LPP);
6041 LLPPs.push_back(LPP);
6043 // update startN for search following egde
6044 if( aN1->GetID() == startNid ) startNid = aN2->GetID();
6045 else startNid = aN1->GetID();
6049 list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
6050 list<SMESH_MeshEditor_PathPoint> firstList = *itLLPP;
6051 list<SMESH_MeshEditor_PathPoint>::iterator itPP = firstList.begin();
6052 for(; itPP!=firstList.end(); itPP++) {
6053 fullList.push_back( *itPP );
6055 SMESH_MeshEditor_PathPoint PP1 = fullList.back();
6056 fullList.pop_back();
6058 for(; itLLPP!=LLPPs.end(); itLLPP++) {
6059 list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
6060 itPP = currList.begin();
6061 SMESH_MeshEditor_PathPoint PP2 = currList.front();
6062 gp_Dir D1 = PP1.Tangent();
6063 gp_Dir D2 = PP2.Tangent();
6064 gp_Dir Dnew( gp_Vec( (D1.X()+D2.X())/2, (D1.Y()+D2.Y())/2,
6065 (D1.Z()+D2.Z())/2 ) );
6066 PP1.SetTangent(Dnew);
6067 fullList.push_back(PP1);
6069 for(; itPP!=firstList.end(); itPP++) {
6070 fullList.push_back( *itPP );
6072 PP1 = fullList.back();
6073 fullList.pop_back();
6075 // if wire not closed
6076 fullList.push_back(PP1);
6080 return EXTR_BAD_PATH_SHAPE;
6083 return MakeExtrElements(theElements, fullList, theHasAngles, theAngles, theLinearVariation,
6084 theHasRefPoint, theRefPoint, theMakeGroups);
6088 //=======================================================================
6089 //function : ExtrusionAlongTrack
6091 //=======================================================================
6092 SMESH_MeshEditor::Extrusion_Error
6093 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet theElements[2],
6094 SMESH_Mesh* theTrack,
6095 const SMDS_MeshNode* theN1,
6096 const bool theHasAngles,
6097 list<double>& theAngles,
6098 const bool theLinearVariation,
6099 const bool theHasRefPoint,
6100 const gp_Pnt& theRefPoint,
6101 const bool theMakeGroups)
6103 myLastCreatedElems.Clear();
6104 myLastCreatedNodes.Clear();
6107 std::list<double> aPrms;
6108 TIDSortedElemSet::iterator itElem;
6111 TopoDS_Edge aTrackEdge;
6112 TopoDS_Vertex aV1, aV2;
6114 SMDS_ElemIteratorPtr aItE;
6115 SMDS_NodeIteratorPtr aItN;
6116 SMDSAbs_ElementType aTypeE;
6118 TNodeOfNodeListMap mapNewNodes;
6121 aNbE = theElements[0].size() + theElements[1].size();
6124 return EXTR_NO_ELEMENTS;
6126 // 1.1 Track Pattern
6129 SMESHDS_Mesh* pMeshDS = theTrack->GetMeshDS();
6131 aItE = pMeshDS->elementsIterator();
6132 while ( aItE->more() ) {
6133 const SMDS_MeshElement* pE = aItE->next();
6134 aTypeE = pE->GetType();
6135 // Pattern must contain links only
6136 if ( aTypeE != SMDSAbs_Edge )
6137 return EXTR_PATH_NOT_EDGE;
6140 list<SMESH_MeshEditor_PathPoint> fullList;
6142 const TopoDS_Shape& aS = theTrack->GetShapeToMesh();
6144 if ( !theTrack->HasShapeToMesh() ) {
6145 //Mesh without shape
6146 const SMDS_MeshNode* currentNode = NULL;
6147 const SMDS_MeshNode* prevNode = theN1;
6148 std::vector<const SMDS_MeshNode*> aNodesList;
6149 aNodesList.push_back(theN1);
6150 int nbEdges = 0, conn=0;
6151 const SMDS_MeshElement* prevElem = NULL;
6152 const SMDS_MeshElement* currentElem = NULL;
6153 int totalNbEdges = theTrack->NbEdges();
6154 SMDS_ElemIteratorPtr nIt;
6157 if( !theTrack->GetMeshDS()->Contains(theN1) ) {
6158 return EXTR_BAD_STARTING_NODE;
6161 conn = nbEdgeConnectivity(theN1);
6163 return EXTR_PATH_NOT_EDGE;
6165 aItE = theN1->GetInverseElementIterator();
6166 prevElem = aItE->next();
6167 currentElem = prevElem;
6169 if(totalNbEdges == 1 ) {
6170 nIt = currentElem->nodesIterator();
6171 currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
6172 if(currentNode == prevNode)
6173 currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
6174 aNodesList.push_back(currentNode);
6176 nIt = currentElem->nodesIterator();
6177 while( nIt->more() ) {
6178 currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
6179 if(currentNode == prevNode)
6180 currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
6181 aNodesList.push_back(currentNode);
6183 //case of the closed mesh
6184 if(currentNode == theN1) {
6189 conn = nbEdgeConnectivity(currentNode);
6191 return EXTR_PATH_NOT_EDGE;
6192 }else if( conn == 1 && nbEdges > 0 ) {
6197 prevNode = currentNode;
6198 aItE = currentNode->GetInverseElementIterator();
6199 currentElem = aItE->next();
6200 if( currentElem == prevElem)
6201 currentElem = aItE->next();
6202 nIt = currentElem->nodesIterator();
6203 prevElem = currentElem;
6209 if(nbEdges != totalNbEdges)
6210 return EXTR_PATH_NOT_EDGE;
6212 TopTools_SequenceOfShape Edges;
6213 list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
6214 int startNid = theN1->GetID();
6215 for ( size_t i = 1; i < aNodesList.size(); i++ )
6217 gp_Pnt p1 = SMESH_TNodeXYZ( aNodesList[i-1] );
6218 gp_Pnt p2 = SMESH_TNodeXYZ( aNodesList[i] );
6219 TopoDS_Edge e = BRepBuilderAPI_MakeEdge( p1, p2 );
6220 list<SMESH_MeshEditor_PathPoint> LPP;
6222 MakeEdgePathPoints(aPrms, e, (aNodesList[i-1]->GetID()==startNid), LPP);
6223 LLPPs.push_back(LPP);
6224 if ( aNodesList[i-1]->GetID() == startNid ) startNid = aNodesList[i ]->GetID();
6225 else startNid = aNodesList[i-1]->GetID();
6228 list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
6229 list<SMESH_MeshEditor_PathPoint> firstList = *itLLPP;
6230 list<SMESH_MeshEditor_PathPoint>::iterator itPP = firstList.begin();
6231 for(; itPP!=firstList.end(); itPP++) {
6232 fullList.push_back( *itPP );
6235 SMESH_MeshEditor_PathPoint PP1 = fullList.back();
6236 SMESH_MeshEditor_PathPoint PP2;
6237 fullList.pop_back();
6239 for(; itLLPP!=LLPPs.end(); itLLPP++) {
6240 list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
6241 itPP = currList.begin();
6242 PP2 = currList.front();
6243 gp_Dir D1 = PP1.Tangent();
6244 gp_Dir D2 = PP2.Tangent();
6245 gp_Dir Dnew( 0.5 * ( D1.XYZ() + D2.XYZ() ));
6246 PP1.SetTangent(Dnew);
6247 fullList.push_back(PP1);
6249 for(; itPP!=currList.end(); itPP++) {
6250 fullList.push_back( *itPP );
6252 PP1 = fullList.back();
6253 fullList.pop_back();
6255 fullList.push_back(PP1);
6257 } // Sub-shape for the Pattern must be an Edge or Wire
6258 else if ( aS.ShapeType() == TopAbs_EDGE )
6260 aTrackEdge = TopoDS::Edge( aS );
6261 // the Edge must not be degenerated
6262 if ( SMESH_Algo::isDegenerated( aTrackEdge ) )
6263 return EXTR_BAD_PATH_SHAPE;
6264 TopExp::Vertices( aTrackEdge, aV1, aV2 );
6265 const SMDS_MeshNode* aN1 = SMESH_Algo::VertexNode( aV1, pMeshDS );
6266 const SMDS_MeshNode* aN2 = SMESH_Algo::VertexNode( aV2, pMeshDS );
6267 // starting node must be aN1 or aN2
6268 if ( !( aN1 == theN1 || aN2 == theN1 ) )
6269 return EXTR_BAD_STARTING_NODE;
6270 aItN = pMeshDS->nodesIterator();
6271 while ( aItN->more() ) {
6272 const SMDS_MeshNode* pNode = aItN->next();
6273 if( pNode==aN1 || pNode==aN2 ) continue;
6274 const SMDS_EdgePosition* pEPos =
6275 static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
6276 double aT = pEPos->GetUParameter();
6277 aPrms.push_back( aT );
6279 //Extrusion_Error err =
6280 MakeEdgePathPoints(aPrms, aTrackEdge, (aN1==theN1), fullList);
6282 else if( aS.ShapeType() == TopAbs_WIRE ) {
6283 list< SMESH_subMesh* > LSM;
6284 TopTools_SequenceOfShape Edges;
6285 TopExp_Explorer eExp(aS, TopAbs_EDGE);
6286 for(; eExp.More(); eExp.Next()) {
6287 TopoDS_Edge E = TopoDS::Edge( eExp.Current() );
6288 if( SMESH_Algo::isDegenerated(E) ) continue;
6289 SMESH_subMesh* SM = theTrack->GetSubMesh(E);
6295 list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
6296 TopoDS_Vertex aVprev;
6297 TColStd_MapOfInteger UsedNums;
6298 int NbEdges = Edges.Length();
6300 for(; i<=NbEdges; i++) {
6302 list< SMESH_subMesh* >::iterator itLSM = LSM.begin();
6303 for(; itLSM!=LSM.end(); itLSM++) {
6305 if(UsedNums.Contains(k)) continue;
6306 aTrackEdge = TopoDS::Edge( Edges.Value(k) );
6307 SMESH_subMesh* locTrack = *itLSM;
6308 SMESHDS_SubMesh* locMeshDS = locTrack->GetSubMeshDS();
6309 TopExp::Vertices( aTrackEdge, aV1, aV2 );
6310 bool aN1isOK = false, aN2isOK = false;
6311 if ( aVprev.IsNull() ) {
6312 // if previous vertex is not yet defined, it means that we in the beginning of wire
6313 // and we have to find initial vertex corresponding to starting node theN1
6314 const SMDS_MeshNode* aN1 = SMESH_Algo::VertexNode( aV1, pMeshDS );
6315 const SMDS_MeshNode* aN2 = SMESH_Algo::VertexNode( aV2, pMeshDS );
6316 // starting node must be aN1 or aN2
6317 aN1isOK = ( aN1 && aN1 == theN1 );
6318 aN2isOK = ( aN2 && aN2 == theN1 );
6321 // we have specified ending vertex of the previous edge on the previous iteration
6322 // and we have just to check that it corresponds to any vertex in current segment
6323 aN1isOK = aVprev.IsSame( aV1 );
6324 aN2isOK = aVprev.IsSame( aV2 );
6326 if ( !aN1isOK && !aN2isOK ) continue;
6327 // 2. Collect parameters on the track edge
6329 aItN = locMeshDS->GetNodes();
6330 while ( aItN->more() ) {
6331 const SMDS_MeshNode* pNode = aItN->next();
6332 const SMDS_EdgePosition* pEPos =
6333 static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
6334 double aT = pEPos->GetUParameter();
6335 aPrms.push_back( aT );
6337 list<SMESH_MeshEditor_PathPoint> LPP;
6338 //Extrusion_Error err =
6339 MakeEdgePathPoints(aPrms, aTrackEdge, aN1isOK, LPP);
6340 LLPPs.push_back(LPP);
6342 // update startN for search following egde
6343 if ( aN1isOK ) aVprev = aV2;
6348 list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
6349 list<SMESH_MeshEditor_PathPoint>& firstList = *itLLPP;
6350 fullList.splice( fullList.end(), firstList );
6352 SMESH_MeshEditor_PathPoint PP1 = fullList.back();
6353 fullList.pop_back();
6355 for(; itLLPP!=LLPPs.end(); itLLPP++) {
6356 list<SMESH_MeshEditor_PathPoint>& currList = *itLLPP;
6357 SMESH_MeshEditor_PathPoint PP2 = currList.front();
6358 gp_Dir D1 = PP1.Tangent();
6359 gp_Dir D2 = PP2.Tangent();
6360 gp_Dir Dnew( D1.XYZ() + D2.XYZ() );
6361 PP1.SetTangent(Dnew);
6362 fullList.push_back(PP1);
6363 fullList.splice( fullList.end(), currList, ++currList.begin(), currList.end() );
6364 PP1 = fullList.back();
6365 fullList.pop_back();
6367 // if wire not closed
6368 fullList.push_back(PP1);
6372 return EXTR_BAD_PATH_SHAPE;
6375 return MakeExtrElements(theElements, fullList, theHasAngles, theAngles, theLinearVariation,
6376 theHasRefPoint, theRefPoint, theMakeGroups);
6380 //=======================================================================
6381 //function : MakeEdgePathPoints
6382 //purpose : auxilary for ExtrusionAlongTrack
6383 //=======================================================================
6384 SMESH_MeshEditor::Extrusion_Error
6385 SMESH_MeshEditor::MakeEdgePathPoints(std::list<double>& aPrms,
6386 const TopoDS_Edge& aTrackEdge,
6388 list<SMESH_MeshEditor_PathPoint>& LPP)
6390 Standard_Real aTx1, aTx2, aL2, aTolVec, aTolVec2;
6392 aTolVec2=aTolVec*aTolVec;
6394 TopoDS_Vertex aV1, aV2;
6395 TopExp::Vertices( aTrackEdge, aV1, aV2 );
6396 aT1=BRep_Tool::Parameter( aV1, aTrackEdge );
6397 aT2=BRep_Tool::Parameter( aV2, aTrackEdge );
6398 // 2. Collect parameters on the track edge
6399 aPrms.push_front( aT1 );
6400 aPrms.push_back( aT2 );
6403 if( FirstIsStart ) {
6414 SMESH_MeshEditor_PathPoint aPP;
6415 Handle(Geom_Curve) aC3D = BRep_Tool::Curve( aTrackEdge, aTx1, aTx2 );
6416 std::list<double>::iterator aItD = aPrms.begin();
6417 for(; aItD != aPrms.end(); ++aItD) {
6421 aC3D->D1( aT, aP3D, aVec );
6422 aL2 = aVec.SquareMagnitude();
6423 if ( aL2 < aTolVec2 )
6424 return EXTR_CANT_GET_TANGENT;
6425 gp_Dir aTgt( FirstIsStart ? aVec : -aVec );
6427 aPP.SetTangent( aTgt );
6428 aPP.SetParameter( aT );
6435 //=======================================================================
6436 //function : MakeExtrElements
6437 //purpose : auxilary for ExtrusionAlongTrack
6438 //=======================================================================
6439 SMESH_MeshEditor::Extrusion_Error
6440 SMESH_MeshEditor::MakeExtrElements(TIDSortedElemSet theElemSets[2],
6441 list<SMESH_MeshEditor_PathPoint>& fullList,
6442 const bool theHasAngles,
6443 list<double>& theAngles,
6444 const bool theLinearVariation,
6445 const bool theHasRefPoint,
6446 const gp_Pnt& theRefPoint,
6447 const bool theMakeGroups)
6449 const int aNbTP = fullList.size();
6452 if( theHasAngles && !theAngles.empty() && theLinearVariation )
6453 LinearAngleVariation(aNbTP-1, theAngles);
6455 // fill vector of path points with angles
6456 vector<SMESH_MeshEditor_PathPoint> aPPs;
6457 list<SMESH_MeshEditor_PathPoint>::iterator itPP = fullList.begin();
6458 list<double>::iterator itAngles = theAngles.begin();
6459 aPPs.push_back( *itPP++ );
6460 for( ; itPP != fullList.end(); itPP++) {
6461 aPPs.push_back( *itPP );
6462 if ( theHasAngles && itAngles != theAngles.end() )
6463 aPPs.back().SetAngle( *itAngles++ );
6466 TNodeOfNodeListMap mapNewNodes;
6467 TElemOfVecOfNnlmiMap mapElemNewNodes;
6468 TTElemOfElemListMap newElemsMap;
6469 TIDSortedElemSet::iterator itElem;
6470 // source elements for each generated one
6471 SMESH_SequenceOfElemPtr srcElems, srcNodes;
6473 // 3. Center of rotation aV0
6474 gp_Pnt aV0 = theRefPoint;
6475 if ( !theHasRefPoint )
6477 gp_XYZ aGC( 0.,0.,0. );
6478 TIDSortedElemSet newNodes;
6480 for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
6482 TIDSortedElemSet& theElements = theElemSets[ is2ndSet ];
6483 itElem = theElements.begin();
6484 for ( ; itElem != theElements.end(); itElem++ )
6486 const SMDS_MeshElement* elem = *itElem;
6487 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6488 while ( itN->more() ) {
6489 const SMDS_MeshElement* node = itN->next();
6490 if ( newNodes.insert( node ).second )
6491 aGC += SMESH_TNodeXYZ( node );
6495 aGC /= newNodes.size();
6497 } // if (!theHasRefPoint) {
6499 // 4. Processing the elements
6500 SMESHDS_Mesh* aMesh = GetMeshDS();
6501 list<const SMDS_MeshNode*> emptyList;
6503 setElemsFirst( theElemSets );
6504 for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
6506 TIDSortedElemSet& theElements = theElemSets[ is2ndSet ];
6507 for ( itElem = theElements.begin(); itElem != theElements.end(); itElem++ )
6509 const SMDS_MeshElement* elem = *itElem;
6511 vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
6512 newNodesItVec.reserve( elem->NbNodes() );
6514 // loop on elem nodes
6516 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6517 while ( itN->more() )
6520 // check if a node has been already processed
6521 const SMDS_MeshNode* node = cast2Node( itN->next() );
6522 TNodeOfNodeListMap::iterator nIt = mapNewNodes.insert( make_pair( node, emptyList )).first;
6523 list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
6524 if ( listNewNodes.empty() )
6527 Standard_Real aAngle1x, aAngleT1T0, aTolAng;
6528 gp_Pnt aP0x, aP1x, aPN0, aPN1, aV0x, aV1x;
6529 gp_Ax1 anAx1, anAxT1T0;
6530 gp_Dir aDT1x, aDT0x, aDT1T0;
6535 aPN0 = SMESH_TNodeXYZ( node );
6537 const SMESH_MeshEditor_PathPoint& aPP0 = aPPs[0];
6539 aDT0x= aPP0.Tangent();
6541 for ( int j = 1; j < aNbTP; ++j ) {
6542 const SMESH_MeshEditor_PathPoint& aPP1 = aPPs[j];
6544 aDT1x = aPP1.Tangent();
6545 aAngle1x = aPP1.Angle();
6547 gp_Trsf aTrsf, aTrsfRot, aTrsfRotT1T0;
6549 gp_Vec aV01x( aP0x, aP1x );
6550 aTrsf.SetTranslation( aV01x );
6553 aV1x = aV0x.Transformed( aTrsf );
6554 aPN1 = aPN0.Transformed( aTrsf );
6556 // rotation 1 [ T1,T0 ]
6557 aAngleT1T0=-aDT1x.Angle( aDT0x );
6558 if (fabs(aAngleT1T0) > aTolAng)
6561 anAxT1T0.SetLocation( aV1x );
6562 anAxT1T0.SetDirection( aDT1T0 );
6563 aTrsfRotT1T0.SetRotation( anAxT1T0, aAngleT1T0 );
6565 aPN1 = aPN1.Transformed( aTrsfRotT1T0 );
6569 if ( theHasAngles ) {
6570 anAx1.SetLocation( aV1x );
6571 anAx1.SetDirection( aDT1x );
6572 aTrsfRot.SetRotation( anAx1, aAngle1x );
6574 aPN1 = aPN1.Transformed( aTrsfRot );
6578 if ( elem->IsQuadratic() && !elem->IsMediumNode(node) )
6580 // create additional node
6581 gp_XYZ midP = 0.5 * ( aPN1.XYZ() + aPN0.XYZ() );
6582 const SMDS_MeshNode* newNode = aMesh->AddNode( midP.X(), midP.Y(), midP.Z() );
6583 myLastCreatedNodes.Append(newNode);
6584 srcNodes.Append( node );
6585 listNewNodes.push_back( newNode );
6587 const SMDS_MeshNode* newNode = aMesh->AddNode( aPN1.X(), aPN1.Y(), aPN1.Z() );
6588 myLastCreatedNodes.Append(newNode);
6589 srcNodes.Append( node );
6590 listNewNodes.push_back( newNode );
6598 else if( elem->IsQuadratic() && !elem->IsMediumNode(node) )
6600 // if current elem is quadratic and current node is not medium
6601 // we have to check - may be it is needed to insert additional nodes
6602 list< const SMDS_MeshNode* > & listNewNodes = nIt->second;
6603 if ((int) listNewNodes.size() == aNbTP-1 )
6605 vector<const SMDS_MeshNode*> aNodes(2*(aNbTP-1));
6606 gp_XYZ P(node->X(), node->Y(), node->Z());
6607 list< const SMDS_MeshNode* >::iterator it = listNewNodes.begin();
6609 for(i=0; i<aNbTP-1; i++) {
6610 const SMDS_MeshNode* N = *it;
6611 double x = ( N->X() + P.X() )/2.;
6612 double y = ( N->Y() + P.Y() )/2.;
6613 double z = ( N->Z() + P.Z() )/2.;
6614 const SMDS_MeshNode* newN = aMesh->AddNode(x,y,z);
6615 srcNodes.Append( node );
6616 myLastCreatedNodes.Append(newN);
6619 P = gp_XYZ(N->X(),N->Y(),N->Z());
6621 listNewNodes.clear();
6622 for(i=0; i<2*(aNbTP-1); i++) {
6623 listNewNodes.push_back(aNodes[i]);
6628 newNodesItVec.push_back( nIt );
6631 // make new elements
6632 sweepElement( elem, newNodesItVec, newElemsMap[elem], aNbTP-1, srcElems );
6636 makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElemSets[0], aNbTP-1, srcElems );
6638 if ( theMakeGroups )
6639 generateGroups( srcNodes, srcElems, "extruded");
6645 //=======================================================================
6646 //function : LinearAngleVariation
6647 //purpose : auxilary for ExtrusionAlongTrack
6648 //=======================================================================
6649 void SMESH_MeshEditor::LinearAngleVariation(const int nbSteps,
6650 list<double>& Angles)
6652 int nbAngles = Angles.size();
6653 if( nbSteps > nbAngles ) {
6654 vector<double> theAngles(nbAngles);
6655 list<double>::iterator it = Angles.begin();
6657 for(; it!=Angles.end(); it++) {
6659 theAngles[i] = (*it);
6662 double rAn2St = double( nbAngles ) / double( nbSteps );
6663 double angPrev = 0, angle;
6664 for ( int iSt = 0; iSt < nbSteps; ++iSt ) {
6665 double angCur = rAn2St * ( iSt+1 );
6666 double angCurFloor = floor( angCur );
6667 double angPrevFloor = floor( angPrev );
6668 if ( angPrevFloor == angCurFloor )
6669 angle = rAn2St * theAngles[ int( angCurFloor ) ];
6671 int iP = int( angPrevFloor );
6672 double angPrevCeil = ceil(angPrev);
6673 angle = ( angPrevCeil - angPrev ) * theAngles[ iP ];
6675 int iC = int( angCurFloor );
6676 if ( iC < nbAngles )
6677 angle += ( angCur - angCurFloor ) * theAngles[ iC ];
6679 iP = int( angPrevCeil );
6681 angle += theAngles[ iC ];
6683 res.push_back(angle);
6688 for(; it!=res.end(); it++)
6689 Angles.push_back( *it );
6694 //================================================================================
6696 * \brief Move or copy theElements applying theTrsf to their nodes
6697 * \param theElems - elements to transform, if theElems is empty then apply to all mesh nodes
6698 * \param theTrsf - transformation to apply
6699 * \param theCopy - if true, create translated copies of theElems
6700 * \param theMakeGroups - if true and theCopy, create translated groups
6701 * \param theTargetMesh - mesh to copy translated elements into
6702 * \return SMESH_MeshEditor::PGroupIDs - list of ids of created groups
6704 //================================================================================
6706 SMESH_MeshEditor::PGroupIDs
6707 SMESH_MeshEditor::Transform (TIDSortedElemSet & theElems,
6708 const gp_Trsf& theTrsf,
6710 const bool theMakeGroups,
6711 SMESH_Mesh* theTargetMesh)
6713 myLastCreatedElems.Clear();
6714 myLastCreatedNodes.Clear();
6716 bool needReverse = false;
6717 string groupPostfix;
6718 switch ( theTrsf.Form() ) {
6721 groupPostfix = "mirrored";
6724 groupPostfix = "mirrored";
6728 groupPostfix = "mirrored";
6731 groupPostfix = "rotated";
6733 case gp_Translation:
6734 groupPostfix = "translated";
6737 groupPostfix = "scaled";
6739 case gp_CompoundTrsf: // different scale by axis
6740 groupPostfix = "scaled";
6743 needReverse = false;
6744 groupPostfix = "transformed";
6747 SMESHDS_Mesh* aTgtMesh = theTargetMesh ? theTargetMesh->GetMeshDS() : 0;
6748 SMESHDS_Mesh* aMesh = GetMeshDS();
6750 SMESH_MeshEditor targetMeshEditor( theTargetMesh );
6751 SMESH_MeshEditor* editor = theTargetMesh ? & targetMeshEditor : theCopy ? this : 0;
6752 SMESH_MeshEditor::ElemFeatures elemType;
6754 // map old node to new one
6755 TNodeNodeMap nodeMap;
6757 // elements sharing moved nodes; those of them which have all
6758 // nodes mirrored but are not in theElems are to be reversed
6759 TIDSortedElemSet inverseElemSet;
6761 // source elements for each generated one
6762 SMESH_SequenceOfElemPtr srcElems, srcNodes;
6764 // issue 021015: EDF 1578 SMESH: Free nodes are removed when translating a mesh
6765 TIDSortedElemSet orphanNode;
6767 if ( theElems.empty() ) // transform the whole mesh
6770 SMDS_ElemIteratorPtr eIt = aMesh->elementsIterator();
6771 while ( eIt->more() ) theElems.insert( eIt->next() );
6773 SMDS_NodeIteratorPtr nIt = aMesh->nodesIterator();
6774 while ( nIt->more() )
6776 const SMDS_MeshNode* node = nIt->next();
6777 if ( node->NbInverseElements() == 0)
6778 orphanNode.insert( node );
6782 // loop on elements to transform nodes : first orphan nodes then elems
6783 TIDSortedElemSet::iterator itElem;
6784 TIDSortedElemSet *elements[] = { &orphanNode, &theElems };
6785 for (int i=0; i<2; i++)
6786 for ( itElem = elements[i]->begin(); itElem != elements[i]->end(); itElem++ )
6788 const SMDS_MeshElement* elem = *itElem;
6792 // loop on elem nodes
6794 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6795 while ( itN->more() )
6797 const SMDS_MeshNode* node = cast2Node( itN->next() );
6798 // check if a node has been already transformed
6799 pair<TNodeNodeMap::iterator,bool> n2n_isnew =
6800 nodeMap.insert( make_pair ( node, node ));
6801 if ( !n2n_isnew.second )
6804 node->GetXYZ( coord );
6805 theTrsf.Transforms( coord[0], coord[1], coord[2] );
6806 if ( theTargetMesh ) {
6807 const SMDS_MeshNode * newNode = aTgtMesh->AddNode( coord[0], coord[1], coord[2] );
6808 n2n_isnew.first->second = newNode;
6809 myLastCreatedNodes.Append(newNode);
6810 srcNodes.Append( node );
6812 else if ( theCopy ) {
6813 const SMDS_MeshNode * newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
6814 n2n_isnew.first->second = newNode;
6815 myLastCreatedNodes.Append(newNode);
6816 srcNodes.Append( node );
6819 aMesh->MoveNode( node, coord[0], coord[1], coord[2] );
6820 // node position on shape becomes invalid
6821 const_cast< SMDS_MeshNode* > ( node )->SetPosition
6822 ( SMDS_SpacePosition::originSpacePosition() );
6825 // keep inverse elements
6826 if ( !theCopy && !theTargetMesh && needReverse ) {
6827 SMDS_ElemIteratorPtr invElemIt = node->GetInverseElementIterator();
6828 while ( invElemIt->more() ) {
6829 const SMDS_MeshElement* iel = invElemIt->next();
6830 inverseElemSet.insert( iel );
6834 } // loop on elems in { &orphanNode, &theElems };
6836 // either create new elements or reverse mirrored ones
6837 if ( !theCopy && !needReverse && !theTargetMesh )
6840 theElems.insert( inverseElemSet.begin(),inverseElemSet.end() );
6842 // Replicate or reverse elements
6844 std::vector<int> iForw;
6845 vector<const SMDS_MeshNode*> nodes;
6846 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
6848 const SMDS_MeshElement* elem = *itElem;
6849 if ( !elem ) continue;
6851 SMDSAbs_GeometryType geomType = elem->GetGeomType();
6852 size_t nbNodes = elem->NbNodes();
6853 if ( geomType == SMDSGeom_NONE ) continue; // node
6855 nodes.resize( nbNodes );
6857 if ( geomType == SMDSGeom_POLYHEDRA ) // ------------------ polyhedral volume
6859 const SMDS_VtkVolume* aPolyedre = dynamic_cast<const SMDS_VtkVolume*>( elem );
6863 bool allTransformed = true;
6864 int nbFaces = aPolyedre->NbFaces();
6865 for (int iface = 1; iface <= nbFaces && allTransformed; iface++)
6867 int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
6868 for (int inode = 1; inode <= nbFaceNodes && allTransformed; inode++)
6870 const SMDS_MeshNode* node = aPolyedre->GetFaceNode(iface, inode);
6871 TNodeNodeMap::iterator nodeMapIt = nodeMap.find(node);
6872 if ( nodeMapIt == nodeMap.end() )
6873 allTransformed = false; // not all nodes transformed
6875 nodes.push_back((*nodeMapIt).second);
6877 if ( needReverse && allTransformed )
6878 std::reverse( nodes.end() - nbFaceNodes, nodes.end() );
6880 if ( !allTransformed )
6881 continue; // not all nodes transformed
6883 else // ----------------------- the rest element types
6885 while ( iForw.size() < nbNodes ) iForw.push_back( iForw.size() );
6886 const vector<int>& iRev = SMDS_MeshCell::reverseSmdsOrder( elem->GetEntityType(), nbNodes );
6887 const vector<int>& i = needReverse ? iRev : iForw;
6889 // find transformed nodes
6891 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6892 while ( itN->more() ) {
6893 const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( itN->next() );
6894 TNodeNodeMap::iterator nodeMapIt = nodeMap.find( node );
6895 if ( nodeMapIt == nodeMap.end() )
6896 break; // not all nodes transformed
6897 nodes[ i [ iNode++ ]] = (*nodeMapIt).second;
6899 if ( iNode != nbNodes )
6900 continue; // not all nodes transformed
6904 // copy in this or a new mesh
6905 if ( editor->AddElement( nodes, elemType.Init( elem, /*basicOnly=*/false )))
6906 srcElems.Append( elem );
6909 // reverse element as it was reversed by transformation
6911 aMesh->ChangeElementNodes( elem, &nodes[0], nbNodes );
6914 } // loop on elements
6916 if ( editor && editor != this )
6917 myLastCreatedElems = editor->myLastCreatedElems;
6919 PGroupIDs newGroupIDs;
6921 if ( ( theMakeGroups && theCopy ) ||
6922 ( theMakeGroups && theTargetMesh ) )
6923 newGroupIDs = generateGroups( srcNodes, srcElems, groupPostfix, theTargetMesh, false );
6928 //=======================================================================
6930 * \brief Create groups of elements made during transformation
6931 * \param nodeGens - nodes making corresponding myLastCreatedNodes
6932 * \param elemGens - elements making corresponding myLastCreatedElems
6933 * \param postfix - to append to names of new groups
6934 * \param targetMesh - mesh to create groups in
6935 * \param topPresent - is there "top" elements that are created by sweeping
6937 //=======================================================================
6939 SMESH_MeshEditor::PGroupIDs
6940 SMESH_MeshEditor::generateGroups(const SMESH_SequenceOfElemPtr& nodeGens,
6941 const SMESH_SequenceOfElemPtr& elemGens,
6942 const std::string& postfix,
6943 SMESH_Mesh* targetMesh,
6944 const bool topPresent)
6946 PGroupIDs newGroupIDs( new list<int> );
6947 SMESH_Mesh* mesh = targetMesh ? targetMesh : GetMesh();
6949 // Sort existing groups by types and collect their names
6951 // containers to store an old group and generated new ones;
6952 // 1st new group is for result elems of different type than a source one;
6953 // 2nd new group is for same type result elems ("top" group at extrusion)
6955 using boost::make_tuple;
6956 typedef tuple< SMESHDS_GroupBase*, SMESHDS_Group*, SMESHDS_Group* > TOldNewGroup;
6957 vector< list< TOldNewGroup > > groupsByType( SMDSAbs_NbElementTypes );
6958 vector< TOldNewGroup* > orderedOldNewGroups; // in order of old groups
6960 set< string > groupNames;
6962 SMESH_Mesh::GroupIteratorPtr groupIt = GetMesh()->GetGroups();
6963 if ( !groupIt->more() ) return newGroupIDs;
6965 int newGroupID = mesh->GetGroupIds().back()+1;
6966 while ( groupIt->more() )
6968 SMESH_Group * group = groupIt->next();
6969 if ( !group ) continue;
6970 SMESHDS_GroupBase* groupDS = group->GetGroupDS();
6971 if ( !groupDS || groupDS->IsEmpty() ) continue;
6972 groupNames.insert ( group->GetName() );
6973 groupDS->SetStoreName( group->GetName() );
6974 const SMDSAbs_ElementType type = groupDS->GetType();
6975 SMESHDS_Group* newGroup = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
6976 SMESHDS_Group* newTopGroup = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
6977 groupsByType[ type ].push_back( make_tuple( groupDS, newGroup, newTopGroup ));
6978 orderedOldNewGroups.push_back( & groupsByType[ type ].back() );
6981 // Loop on nodes and elements to add them in new groups
6983 vector< const SMDS_MeshElement* > resultElems;
6984 for ( int isNodes = 0; isNodes < 2; ++isNodes )
6986 const SMESH_SequenceOfElemPtr& gens = isNodes ? nodeGens : elemGens;
6987 const SMESH_SequenceOfElemPtr& elems = isNodes ? myLastCreatedNodes : myLastCreatedElems;
6988 if ( gens.Length() != elems.Length() )
6989 throw SALOME_Exception("SMESH_MeshEditor::generateGroups(): invalid args");
6991 // loop on created elements
6992 for (int iElem = 1; iElem <= elems.Length(); ++iElem )
6994 const SMDS_MeshElement* sourceElem = gens( iElem );
6995 if ( !sourceElem ) {
6996 MESSAGE("generateGroups(): NULL source element");
6999 list< TOldNewGroup > & groupsOldNew = groupsByType[ sourceElem->GetType() ];
7000 if ( groupsOldNew.empty() ) { // no groups of this type at all
7001 while ( iElem < gens.Length() && gens( iElem+1 ) == sourceElem )
7002 ++iElem; // skip all elements made by sourceElem
7005 // collect all elements made by the iElem-th sourceElem
7006 resultElems.clear();
7007 if ( const SMDS_MeshElement* resElem = elems( iElem ))
7008 if ( resElem != sourceElem )
7009 resultElems.push_back( resElem );
7010 while ( iElem < gens.Length() && gens( iElem+1 ) == sourceElem )
7011 if ( const SMDS_MeshElement* resElem = elems( ++iElem ))
7012 if ( resElem != sourceElem )
7013 resultElems.push_back( resElem );
7015 const SMDS_MeshElement* topElem = 0;
7016 if ( isNodes ) // there must be a top element
7018 topElem = resultElems.back();
7019 resultElems.pop_back();
7023 vector< const SMDS_MeshElement* >::reverse_iterator resElemIt = resultElems.rbegin();
7024 for ( ; resElemIt != resultElems.rend() ; ++resElemIt )
7025 if ( (*resElemIt)->GetType() == sourceElem->GetType() )
7027 topElem = *resElemIt;
7028 *resElemIt = 0; // erase *resElemIt
7032 // add resultElems to groups originted from ones the sourceElem belongs to
7033 list< TOldNewGroup >::iterator gOldNew, gLast = groupsOldNew.end();
7034 for ( gOldNew = groupsOldNew.begin(); gOldNew != gLast; ++gOldNew )
7036 SMESHDS_GroupBase* oldGroup = gOldNew->get<0>();
7037 if ( oldGroup->Contains( sourceElem )) // sourceElem is in oldGroup
7039 // fill in a new group
7040 SMDS_MeshGroup & newGroup = gOldNew->get<1>()->SMDSGroup();
7041 vector< const SMDS_MeshElement* >::iterator resLast = resultElems.end(), resElemIt;
7042 for ( resElemIt = resultElems.begin(); resElemIt != resLast; ++resElemIt )
7044 newGroup.Add( *resElemIt );
7046 // fill a "top" group
7049 SMDS_MeshGroup & newTopGroup = gOldNew->get<2>()->SMDSGroup();
7050 newTopGroup.Add( topElem );
7054 } // loop on created elements
7055 }// loop on nodes and elements
7057 // Create new SMESH_Groups from SMESHDS_Groups and remove empty SMESHDS_Groups
7059 list<int> topGrouIds;
7060 for ( size_t i = 0; i < orderedOldNewGroups.size(); ++i )
7062 SMESHDS_GroupBase* oldGroupDS = orderedOldNewGroups[i]->get<0>();
7063 SMESHDS_Group* newGroups[2] = { orderedOldNewGroups[i]->get<1>(),
7064 orderedOldNewGroups[i]->get<2>() };
7065 for ( int is2nd = 0; is2nd < 2; ++is2nd )
7067 SMESHDS_Group* newGroupDS = newGroups[ is2nd ];
7068 if ( newGroupDS->IsEmpty() )
7070 mesh->GetMeshDS()->RemoveGroup( newGroupDS );
7075 newGroupDS->SetType( newGroupDS->GetElements()->next()->GetType() );
7078 const bool isTop = ( topPresent &&
7079 newGroupDS->GetType() == oldGroupDS->GetType() &&
7082 string name = oldGroupDS->GetStoreName();
7083 { // remove trailing whitespaces (issue 22599)
7084 size_t size = name.size();
7085 while ( size > 1 && isspace( name[ size-1 ]))
7087 if ( size != name.size() )
7089 name.resize( size );
7090 oldGroupDS->SetStoreName( name.c_str() );
7093 if ( !targetMesh ) {
7094 string suffix = ( isTop ? "top": postfix.c_str() );
7098 while ( !groupNames.insert( name ).second ) // name exists
7099 name = SMESH_Comment( oldGroupDS->GetStoreName() ) << "_" << suffix << "_" << nb++;
7104 newGroupDS->SetStoreName( name.c_str() );
7106 // make a SMESH_Groups
7107 mesh->AddGroup( newGroupDS );
7109 topGrouIds.push_back( newGroupDS->GetID() );
7111 newGroupIDs->push_back( newGroupDS->GetID() );
7115 newGroupIDs->splice( newGroupIDs->end(), topGrouIds );
7120 //================================================================================
7122 * * \brief Return list of group of nodes close to each other within theTolerance
7123 * * Search among theNodes or in the whole mesh if theNodes is empty using
7124 * * an Octree algorithm
7125 * \param [in,out] theNodes - the nodes to treat
7126 * \param [in] theTolerance - the tolerance
7127 * \param [out] theGroupsOfNodes - the result groups of coincident nodes
7128 * \param [in] theSeparateCornersAndMedium - if \c true, in quadratic mesh puts
7129 * corner and medium nodes in separate groups
7131 //================================================================================
7133 void SMESH_MeshEditor::FindCoincidentNodes (TIDSortedNodeSet & theNodes,
7134 const double theTolerance,
7135 TListOfListOfNodes & theGroupsOfNodes,
7136 bool theSeparateCornersAndMedium)
7138 myLastCreatedElems.Clear();
7139 myLastCreatedNodes.Clear();
7141 if ( myMesh->NbEdges ( ORDER_QUADRATIC ) +
7142 myMesh->NbFaces ( ORDER_QUADRATIC ) +
7143 myMesh->NbVolumes( ORDER_QUADRATIC ) == 0 )
7144 theSeparateCornersAndMedium = false;
7146 TIDSortedNodeSet& corners = theNodes;
7147 TIDSortedNodeSet medium;
7149 if ( theNodes.empty() ) // get all nodes in the mesh
7151 TIDSortedNodeSet* nodes[2] = { &corners, &medium };
7152 SMDS_NodeIteratorPtr nIt = GetMeshDS()->nodesIterator(/*idInceasingOrder=*/true);
7153 if ( theSeparateCornersAndMedium )
7154 while ( nIt->more() )
7156 const SMDS_MeshNode* n = nIt->next();
7157 TIDSortedNodeSet* & nodeSet = nodes[ SMESH_MesherHelper::IsMedium( n )];
7158 nodeSet->insert( nodeSet->end(), n );
7161 while ( nIt->more() )
7162 theNodes.insert( theNodes.end(),nIt->next() );
7164 else if ( theSeparateCornersAndMedium ) // separate corners from medium nodes
7166 TIDSortedNodeSet::iterator nIt = corners.begin();
7167 while ( nIt != corners.end() )
7168 if ( SMESH_MesherHelper::IsMedium( *nIt ))
7170 medium.insert( medium.end(), *nIt );
7171 corners.erase( nIt++ );
7179 if ( !corners.empty() )
7180 SMESH_OctreeNode::FindCoincidentNodes ( corners, &theGroupsOfNodes, theTolerance );
7181 if ( !medium.empty() )
7182 SMESH_OctreeNode::FindCoincidentNodes ( medium, &theGroupsOfNodes, theTolerance );
7185 //=======================================================================
7186 //function : SimplifyFace
7187 //purpose : split a chain of nodes into several closed chains
7188 //=======================================================================
7190 int SMESH_MeshEditor::SimplifyFace (const vector<const SMDS_MeshNode *>& faceNodes,
7191 vector<const SMDS_MeshNode *>& poly_nodes,
7192 vector<int>& quantities) const
7194 int nbNodes = faceNodes.size();
7195 while ( faceNodes[ 0 ] == faceNodes[ nbNodes-1 ] && nbNodes > 2 )
7199 size_t prevNbQuant = quantities.size();
7201 vector< const SMDS_MeshNode* > simpleNodes; simpleNodes.reserve( nbNodes );
7202 map< const SMDS_MeshNode*, int > nodeIndices; // indices within simpleNodes
7203 map< const SMDS_MeshNode*, int >::iterator nInd;
7205 nodeIndices.insert( make_pair( faceNodes[0], 0 ));
7206 simpleNodes.push_back( faceNodes[0] );
7207 for ( int iCur = 1; iCur < nbNodes; iCur++ )
7209 if ( faceNodes[ iCur ] != simpleNodes.back() )
7211 int index = simpleNodes.size();
7212 nInd = nodeIndices.insert( make_pair( faceNodes[ iCur ], index )).first;
7213 int prevIndex = nInd->second;
7214 if ( prevIndex < index )
7217 int loopLen = index - prevIndex;
7220 // store the sub-loop
7221 quantities.push_back( loopLen );
7222 for ( int i = prevIndex; i < index; i++ )
7223 poly_nodes.push_back( simpleNodes[ i ]);
7225 simpleNodes.resize( prevIndex+1 );
7229 simpleNodes.push_back( faceNodes[ iCur ]);
7234 if ( simpleNodes.size() > 2 )
7236 quantities.push_back( simpleNodes.size() );
7237 poly_nodes.insert ( poly_nodes.end(), simpleNodes.begin(), simpleNodes.end() );
7240 return quantities.size() - prevNbQuant;
7243 //=======================================================================
7244 //function : MergeNodes
7245 //purpose : In each group, the cdr of nodes are substituted by the first one
7247 //=======================================================================
7249 void SMESH_MeshEditor::MergeNodes (TListOfListOfNodes & theGroupsOfNodes)
7251 myLastCreatedElems.Clear();
7252 myLastCreatedNodes.Clear();
7254 SMESHDS_Mesh* aMesh = GetMeshDS();
7256 TNodeNodeMap nodeNodeMap; // node to replace - new node
7257 set<const SMDS_MeshElement*> elems; // all elements with changed nodes
7258 list< int > rmElemIds, rmNodeIds;
7260 // Fill nodeNodeMap and elems
7262 TListOfListOfNodes::iterator grIt = theGroupsOfNodes.begin();
7263 for ( ; grIt != theGroupsOfNodes.end(); grIt++ )
7265 list<const SMDS_MeshNode*>& nodes = *grIt;
7266 list<const SMDS_MeshNode*>::iterator nIt = nodes.begin();
7267 const SMDS_MeshNode* nToKeep = *nIt;
7268 for ( ++nIt; nIt != nodes.end(); nIt++ )
7270 const SMDS_MeshNode* nToRemove = *nIt;
7271 nodeNodeMap.insert( make_pair( nToRemove, nToKeep ));
7272 if ( nToRemove != nToKeep )
7274 rmNodeIds.push_back( nToRemove->GetID() );
7275 AddToSameGroups( nToKeep, nToRemove, aMesh );
7276 // set _alwaysComputed to a sub-mesh of VERTEX to enable mesh computing
7277 // after MergeNodes() w/o creating node in place of merged ones.
7278 const SMDS_PositionPtr& pos = nToRemove->GetPosition();
7279 if ( pos && pos->GetTypeOfPosition() == SMDS_TOP_VERTEX )
7280 if ( SMESH_subMesh* sm = myMesh->GetSubMeshContaining( nToRemove->getshapeId() ))
7281 sm->SetIsAlwaysComputed( true );
7283 SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
7284 while ( invElemIt->more() ) {
7285 const SMDS_MeshElement* elem = invElemIt->next();
7290 // Change element nodes or remove an element
7292 set<const SMDS_MeshNode*> nodeSet;
7293 vector< const SMDS_MeshNode*> curNodes, uniqueNodes;
7295 ElemFeatures elemType;
7297 set<const SMDS_MeshElement*>::iterator eIt = elems.begin();
7298 for ( ; eIt != elems.end(); eIt++ )
7300 const SMDS_MeshElement* elem = *eIt;
7301 const int nbNodes = elem->NbNodes();
7302 const int aShapeId = FindShape( elem );
7303 SMDSAbs_EntityType entity = elem->GetEntityType();
7306 curNodes.resize( nbNodes );
7307 uniqueNodes.resize( nbNodes );
7308 iRepl.resize( nbNodes );
7309 int iUnique = 0, iCur = 0, nbRepl = 0;
7311 // get new seq of nodes
7312 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
7313 while ( itN->more() )
7315 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( itN->next() );
7317 TNodeNodeMap::iterator nnIt = nodeNodeMap.find( n );
7318 if ( nnIt != nodeNodeMap.end() ) { // n sticks
7320 { ////////// BUG 0020185: begin
7321 bool stopRecur = false;
7322 set<const SMDS_MeshNode*> nodesRecur;
7323 nodesRecur.insert(n);
7324 while (!stopRecur) {
7325 TNodeNodeMap::iterator nnIt_i = nodeNodeMap.find( n );
7326 if ( nnIt_i != nodeNodeMap.end() ) { // n sticks
7327 n = (*nnIt_i).second;
7328 if (!nodesRecur.insert(n).second) {
7329 // error: recursive dependency
7336 } ////////// BUG 0020185: end
7338 curNodes[ iCur ] = n;
7339 bool isUnique = nodeSet.insert( n ).second;
7341 uniqueNodes[ iUnique++ ] = n;
7343 iRepl[ nbRepl++ ] = iCur;
7347 // Analyse element topology after replacement
7350 int nbUniqueNodes = nodeSet.size();
7351 if ( nbNodes != nbUniqueNodes ) // some nodes stick
7353 if ( elem->IsPoly() ) // Polygons and Polyhedral volumes
7355 if ( elem->GetType() == SMDSAbs_Face ) // Polygon
7357 elemType.Init( elem );
7358 const bool isQuad = elemType.myIsQuad;
7360 SMDS_MeshCell::applyInterlace // interlace medium and corner nodes
7361 ( SMDS_MeshCell::interlacedSmdsOrder( SMDSEntity_Quad_Polygon, nbNodes ), curNodes );
7363 // a polygon can divide into several elements
7364 vector<const SMDS_MeshNode *> polygons_nodes;
7365 vector<int> quantities;
7366 int nbNew = SimplifyFace( curNodes, polygons_nodes, quantities );
7369 vector<const SMDS_MeshNode *> face_nodes;
7371 for (int iface = 0; iface < nbNew; iface++)
7373 int nbNewNodes = quantities[iface];
7374 face_nodes.assign( polygons_nodes.begin() + inode,
7375 polygons_nodes.begin() + inode + nbNewNodes );
7376 inode += nbNewNodes;
7377 if ( isQuad ) // check if a result elem is a valid quadratic polygon
7379 bool isValid = ( nbNewNodes % 2 == 0 );
7380 for ( int i = 0; i < nbNewNodes && isValid; ++i )
7381 isValid = ( elem->IsMediumNode( face_nodes[i]) == bool( i % 2 ));
7382 elemType.SetQuad( isValid );
7383 if ( isValid ) // put medium nodes after corners
7384 SMDS_MeshCell::applyInterlaceRev
7385 ( SMDS_MeshCell::interlacedSmdsOrder( SMDSEntity_Quad_Polygon,
7386 nbNewNodes ), face_nodes );
7388 elemType.SetPoly(( nbNewNodes / ( elemType.myIsQuad + 1 ) > 4 ));
7390 SMDS_MeshElement* newElem = AddElement( face_nodes, elemType.SetID(-1));
7392 aMesh->SetMeshElementOnShape(newElem, aShapeId);
7395 rmElemIds.push_back(elem->GetID());
7399 else if ( elem->GetType() == SMDSAbs_Volume ) // Polyhedral volume
7401 if ( nbUniqueNodes < 4 ) {
7402 rmElemIds.push_back(elem->GetID());
7405 // each face has to be analyzed in order to check volume validity
7406 const SMDS_VtkVolume* aPolyedre = dynamic_cast<const SMDS_VtkVolume*>( elem );
7409 int nbFaces = aPolyedre->NbFaces();
7411 vector<const SMDS_MeshNode *> poly_nodes;
7412 vector<int> quantities;
7413 vector<const SMDS_MeshNode *> faceNodes;
7415 for (int iface = 1; iface <= nbFaces; iface++)
7417 int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
7418 faceNodes.resize( nbFaceNodes );
7419 for (int inode = 1; inode <= nbFaceNodes; inode++)
7421 const SMDS_MeshNode * faceNode = aPolyedre->GetFaceNode(iface, inode);
7422 TNodeNodeMap::iterator nnIt = nodeNodeMap.find(faceNode);
7423 if ( nnIt != nodeNodeMap.end() ) // faceNode sticks
7424 faceNode = (*nnIt).second;
7425 faceNodes[inode - 1] = faceNode;
7427 SimplifyFace(faceNodes, poly_nodes, quantities);
7430 if ( quantities.size() > 3 ) {
7431 // TODO: remove coincident faces
7434 if ( quantities.size() > 3 )
7436 const SMDS_MeshElement* newElem =
7437 aMesh->AddPolyhedralVolume( poly_nodes, quantities );
7438 myLastCreatedElems.Append( newElem );
7439 if ( aShapeId && newElem )
7440 aMesh->SetMeshElementOnShape( newElem, aShapeId );
7441 rmElemIds.push_back( elem->GetID() );
7445 rmElemIds.push_back( elem->GetID() );
7456 // TODO not all the possible cases are solved. Find something more generic?
7458 case SMDSEntity_Edge: //////// EDGE
7459 case SMDSEntity_Triangle: //// TRIANGLE
7460 case SMDSEntity_Quad_Triangle:
7461 case SMDSEntity_Tetra:
7462 case SMDSEntity_Quad_Tetra: // TETRAHEDRON
7467 case SMDSEntity_Quad_Edge:
7469 isOk = false; // to linear EDGE ???????
7472 case SMDSEntity_Quadrangle: //////////////////////////////////// QUADRANGLE
7474 if ( nbUniqueNodes < 3 )
7476 else if ( nbRepl == 1 && curNodes[ iRepl[0]] == curNodes[( iRepl[0]+2 )%4 ])
7477 isOk = false; // opposite nodes stick
7480 case SMDSEntity_Quad_Quadrangle: // Quadratic QUADRANGLE
7489 if (( nbUniqueNodes == 6 && nbRepl == 2 ) &&
7490 (( iRepl[0] == 1 && iRepl[1] == 4 && curNodes[1] == curNodes[0] ) ||
7491 ( iRepl[0] == 2 && iRepl[1] == 5 && curNodes[2] == curNodes[1] ) ||
7492 ( iRepl[0] == 3 && iRepl[1] == 6 && curNodes[3] == curNodes[2] ) ||
7493 ( iRepl[0] == 3 && iRepl[1] == 7 && curNodes[3] == curNodes[0] )))
7499 case SMDSEntity_BiQuad_Quadrangle: // Bi-Quadratic QUADRANGLE
7508 if (( nbUniqueNodes == 7 && nbRepl == 2 && iRepl[1] != 8 ) &&
7509 (( iRepl[0] == 1 && iRepl[1] == 4 && curNodes[1] == curNodes[0] ) ||
7510 ( iRepl[0] == 2 && iRepl[1] == 5 && curNodes[2] == curNodes[1] ) ||
7511 ( iRepl[0] == 3 && iRepl[1] == 6 && curNodes[3] == curNodes[2] ) ||
7512 ( iRepl[0] == 3 && iRepl[1] == 7 && curNodes[3] == curNodes[0] )))
7518 case SMDSEntity_Penta: ///////////////////////////////////// PENTAHEDRON
7521 if ( nbUniqueNodes == 4 ) {
7522 // ---------------------------------> tetrahedron
7523 if ( curNodes[3] == curNodes[4] &&
7524 curNodes[3] == curNodes[5] ) {
7528 else if ( curNodes[0] == curNodes[1] &&
7529 curNodes[0] == curNodes[2] ) {
7530 // bottom nodes stick: set a top before
7531 uniqueNodes[ 3 ] = uniqueNodes [ 0 ];
7532 uniqueNodes[ 0 ] = curNodes [ 5 ];
7533 uniqueNodes[ 1 ] = curNodes [ 4 ];
7534 uniqueNodes[ 2 ] = curNodes [ 3 ];
7537 else if (( curNodes[0] == curNodes[3] ) +
7538 ( curNodes[1] == curNodes[4] ) +
7539 ( curNodes[2] == curNodes[5] ) == 2 ) {
7540 // a lateral face turns into a line
7544 else if ( nbUniqueNodes == 5 ) {
7545 // PENTAHEDRON --------------------> pyramid
7546 if ( curNodes[0] == curNodes[3] )
7548 uniqueNodes[ 0 ] = curNodes[ 1 ];
7549 uniqueNodes[ 1 ] = curNodes[ 4 ];
7550 uniqueNodes[ 2 ] = curNodes[ 5 ];
7551 uniqueNodes[ 3 ] = curNodes[ 2 ];
7552 uniqueNodes[ 4 ] = curNodes[ 0 ];
7555 if ( curNodes[1] == curNodes[4] )
7557 uniqueNodes[ 0 ] = curNodes[ 0 ];
7558 uniqueNodes[ 1 ] = curNodes[ 2 ];
7559 uniqueNodes[ 2 ] = curNodes[ 5 ];
7560 uniqueNodes[ 3 ] = curNodes[ 3 ];
7561 uniqueNodes[ 4 ] = curNodes[ 1 ];
7564 if ( curNodes[2] == curNodes[5] )
7566 uniqueNodes[ 0 ] = curNodes[ 0 ];
7567 uniqueNodes[ 1 ] = curNodes[ 3 ];
7568 uniqueNodes[ 2 ] = curNodes[ 4 ];
7569 uniqueNodes[ 3 ] = curNodes[ 1 ];
7570 uniqueNodes[ 4 ] = curNodes[ 2 ];
7576 case SMDSEntity_Hexa:
7578 //////////////////////////////////// HEXAHEDRON
7580 SMDS_VolumeTool hexa (elem);
7581 hexa.SetExternalNormal();
7582 if ( nbUniqueNodes == 4 && nbRepl == 4 ) {
7583 //////////////////////// HEX ---> tetrahedron
7584 for ( int iFace = 0; iFace < 6; iFace++ ) {
7585 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7586 if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7587 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7588 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7589 // one face turns into a point ...
7590 int pickInd = ind[ 0 ];
7591 int iOppFace = hexa.GetOppFaceIndex( iFace );
7592 ind = hexa.GetFaceNodesIndices( iOppFace );
7594 uniqueNodes.clear();
7595 for ( iCur = 0; iCur < 4 && nbStick < 2; iCur++ ) {
7596 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7599 uniqueNodes.push_back( curNodes[ind[ iCur ]]);
7601 if ( nbStick == 1 ) {
7602 // ... and the opposite one - into a triangle.
7604 uniqueNodes.push_back( curNodes[ pickInd ]);
7611 else if ( nbUniqueNodes == 6 && nbRepl == 2 ) {
7612 //////////////////////// HEX ---> prism
7613 int nbTria = 0, iTria[3];
7614 const int *ind; // indices of face nodes
7615 // look for triangular faces
7616 for ( int iFace = 0; iFace < 6 && nbTria < 3; iFace++ ) {
7617 ind = hexa.GetFaceNodesIndices( iFace );
7618 TIDSortedNodeSet faceNodes;
7619 for ( iCur = 0; iCur < 4; iCur++ )
7620 faceNodes.insert( curNodes[ind[iCur]] );
7621 if ( faceNodes.size() == 3 )
7622 iTria[ nbTria++ ] = iFace;
7624 // check if triangles are opposite
7625 if ( nbTria == 2 && iTria[0] == hexa.GetOppFaceIndex( iTria[1] ))
7627 // set nodes of the bottom triangle
7628 ind = hexa.GetFaceNodesIndices( iTria[ 0 ]);
7630 for ( iCur = 0; iCur < 4; iCur++ )
7631 if ( ind[iCur] != iRepl[0] && ind[iCur] != iRepl[1])
7632 indB.push_back( ind[iCur] );
7633 if ( !hexa.IsForward() )
7634 std::swap( indB[0], indB[2] );
7635 for ( iCur = 0; iCur < 3; iCur++ )
7636 uniqueNodes[ iCur ] = curNodes[indB[iCur]];
7637 // set nodes of the top triangle
7638 const int *indT = hexa.GetFaceNodesIndices( iTria[ 1 ]);
7639 for ( iCur = 0; iCur < 3; ++iCur )
7640 for ( int j = 0; j < 4; ++j )
7641 if ( hexa.IsLinked( indB[ iCur ], indT[ j ] ))
7643 uniqueNodes[ iCur + 3 ] = curNodes[ indT[ j ]];
7650 else if (nbUniqueNodes == 5 && nbRepl == 3 ) {
7651 //////////////////// HEXAHEDRON ---> pyramid
7652 for ( int iFace = 0; iFace < 6; iFace++ ) {
7653 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7654 if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7655 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7656 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7657 // one face turns into a point ...
7658 int iOppFace = hexa.GetOppFaceIndex( iFace );
7659 ind = hexa.GetFaceNodesIndices( iOppFace );
7660 uniqueNodes.clear();
7661 for ( iCur = 0; iCur < 4; iCur++ ) {
7662 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7665 uniqueNodes.push_back( curNodes[ind[ iCur ]]);
7667 if ( uniqueNodes.size() == 4 ) {
7668 // ... and the opposite one is a quadrangle
7670 const int* indTop = hexa.GetFaceNodesIndices( iFace );
7671 uniqueNodes.push_back( curNodes[indTop[ 0 ]]);
7679 if ( !isOk && nbUniqueNodes > 4 ) {
7680 ////////////////// HEXAHEDRON ---> polyhedron
7681 hexa.SetExternalNormal();
7682 vector<const SMDS_MeshNode *> poly_nodes; poly_nodes.reserve( 6 * 4 );
7683 vector<int> quantities; quantities.reserve( 6 );
7684 for ( int iFace = 0; iFace < 6; iFace++ )
7686 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7687 if ( curNodes[ind[0]] == curNodes[ind[2]] ||
7688 curNodes[ind[1]] == curNodes[ind[3]] )
7691 break; // opposite nodes stick
7694 for ( iCur = 0; iCur < 4; iCur++ )
7696 if ( nodeSet.insert( curNodes[ind[ iCur ]] ).second )
7697 poly_nodes.push_back( curNodes[ind[ iCur ]]);
7699 if ( nodeSet.size() < 3 )
7700 poly_nodes.resize( poly_nodes.size() - nodeSet.size() );
7702 quantities.push_back( nodeSet.size() );
7704 if ( quantities.size() >= 4 )
7706 const SMDS_MeshElement* newElem = aMesh->AddPolyhedralVolume( poly_nodes, quantities );
7707 myLastCreatedElems.Append( newElem );
7708 if ( aShapeId && newElem )
7709 aMesh->SetMeshElementOnShape( newElem, aShapeId );
7710 rmElemIds.push_back( elem->GetID() );
7714 } // case HEXAHEDRON
7718 } // switch ( nbNodes )
7720 } // if ( nbNodes != nbUniqueNodes ) // some nodes stick
7722 if ( isOk ) // a non-poly elem remains valid after sticking nodes
7724 if ( nbNodes != nbUniqueNodes ||
7725 !aMesh->ChangeElementNodes( elem, & curNodes[0], nbNodes ))
7727 elemType.Init( elem ).SetID( elem->GetID() );
7729 SMESHDS_SubMesh * sm = aShapeId > 0 ? aMesh->MeshElements(aShapeId) : 0;
7730 aMesh->RemoveFreeElement(elem, sm, /*fromGroups=*/false);
7732 uniqueNodes.resize(nbUniqueNodes);
7733 SMDS_MeshElement* newElem = this->AddElement( uniqueNodes, elemType );
7734 if ( sm && newElem )
7735 sm->AddElement( newElem );
7736 if ( elem != newElem )
7737 ReplaceElemInGroups( elem, newElem, aMesh );
7741 // Remove invalid regular element or invalid polygon
7742 rmElemIds.push_back( elem->GetID() );
7745 } // loop on elements
7747 // Remove bad elements, then equal nodes (order important)
7749 Remove( rmElemIds, false );
7750 Remove( rmNodeIds, true );
7756 // ========================================================
7757 // class : SortableElement
7758 // purpose : allow sorting elements basing on their nodes
7759 // ========================================================
7760 class SortableElement : public set <const SMDS_MeshElement*>
7764 SortableElement( const SMDS_MeshElement* theElem )
7767 SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator();
7768 while ( nodeIt->more() )
7769 this->insert( nodeIt->next() );
7772 const SMDS_MeshElement* Get() const
7776 mutable const SMDS_MeshElement* myElem;
7779 //=======================================================================
7780 //function : FindEqualElements
7781 //purpose : Return list of group of elements built on the same nodes.
7782 // Search among theElements or in the whole mesh if theElements is empty
7783 //=======================================================================
7785 void SMESH_MeshEditor::FindEqualElements(TIDSortedElemSet & theElements,
7786 TListOfListOfElementsID & theGroupsOfElementsID)
7788 myLastCreatedElems.Clear();
7789 myLastCreatedNodes.Clear();
7791 typedef map< SortableElement, int > TMapOfNodeSet;
7792 typedef list<int> TGroupOfElems;
7794 if ( theElements.empty() )
7795 { // get all elements in the mesh
7796 SMDS_ElemIteratorPtr eIt = GetMeshDS()->elementsIterator();
7797 while ( eIt->more() )
7798 theElements.insert( theElements.end(), eIt->next() );
7801 vector< TGroupOfElems > arrayOfGroups;
7802 TGroupOfElems groupOfElems;
7803 TMapOfNodeSet mapOfNodeSet;
7805 TIDSortedElemSet::iterator elemIt = theElements.begin();
7806 for ( int i = 0; elemIt != theElements.end(); ++elemIt )
7808 const SMDS_MeshElement* curElem = *elemIt;
7809 SortableElement SE(curElem);
7811 pair< TMapOfNodeSet::iterator, bool> pp = mapOfNodeSet.insert(make_pair(SE, i));
7812 if ( !pp.second ) { // one more coincident elem
7813 TMapOfNodeSet::iterator& itSE = pp.first;
7814 int ind = (*itSE).second;
7815 arrayOfGroups[ind].push_back( curElem->GetID() );
7818 arrayOfGroups.push_back( groupOfElems );
7819 arrayOfGroups.back().push_back( curElem->GetID() );
7824 groupOfElems.clear();
7825 vector< TGroupOfElems >::iterator groupIt = arrayOfGroups.begin();
7826 for ( ; groupIt != arrayOfGroups.end(); ++groupIt )
7828 if ( groupIt->size() > 1 ) {
7829 //groupOfElems.sort(); -- theElements is sorted already
7830 theGroupsOfElementsID.push_back( groupOfElems );
7831 theGroupsOfElementsID.back().splice( theGroupsOfElementsID.back().end(), *groupIt );
7836 //=======================================================================
7837 //function : MergeElements
7838 //purpose : In each given group, substitute all elements by the first one.
7839 //=======================================================================
7841 void SMESH_MeshEditor::MergeElements(TListOfListOfElementsID & theGroupsOfElementsID)
7843 myLastCreatedElems.Clear();
7844 myLastCreatedNodes.Clear();
7846 typedef list<int> TListOfIDs;
7847 TListOfIDs rmElemIds; // IDs of elems to remove
7849 SMESHDS_Mesh* aMesh = GetMeshDS();
7851 TListOfListOfElementsID::iterator groupsIt = theGroupsOfElementsID.begin();
7852 while ( groupsIt != theGroupsOfElementsID.end() ) {
7853 TListOfIDs& aGroupOfElemID = *groupsIt;
7854 aGroupOfElemID.sort();
7855 int elemIDToKeep = aGroupOfElemID.front();
7856 const SMDS_MeshElement* elemToKeep = aMesh->FindElement(elemIDToKeep);
7857 aGroupOfElemID.pop_front();
7858 TListOfIDs::iterator idIt = aGroupOfElemID.begin();
7859 while ( idIt != aGroupOfElemID.end() ) {
7860 int elemIDToRemove = *idIt;
7861 const SMDS_MeshElement* elemToRemove = aMesh->FindElement(elemIDToRemove);
7862 // add the kept element in groups of removed one (PAL15188)
7863 AddToSameGroups( elemToKeep, elemToRemove, aMesh );
7864 rmElemIds.push_back( elemIDToRemove );
7870 Remove( rmElemIds, false );
7873 //=======================================================================
7874 //function : MergeEqualElements
7875 //purpose : Remove all but one of elements built on the same nodes.
7876 //=======================================================================
7878 void SMESH_MeshEditor::MergeEqualElements()
7880 TIDSortedElemSet aMeshElements; /* empty input ==
7881 to merge equal elements in the whole mesh */
7882 TListOfListOfElementsID aGroupsOfElementsID;
7883 FindEqualElements(aMeshElements, aGroupsOfElementsID);
7884 MergeElements(aGroupsOfElementsID);
7887 //=======================================================================
7888 //function : findAdjacentFace
7890 //=======================================================================
7892 static const SMDS_MeshElement* findAdjacentFace(const SMDS_MeshNode* n1,
7893 const SMDS_MeshNode* n2,
7894 const SMDS_MeshElement* elem)
7896 TIDSortedElemSet elemSet, avoidSet;
7898 avoidSet.insert ( elem );
7899 return SMESH_MeshAlgos::FindFaceInSet( n1, n2, elemSet, avoidSet );
7902 //=======================================================================
7903 //function : findSegment
7904 //purpose : Return a mesh segment by two nodes one of which can be medium
7905 //=======================================================================
7907 static const SMDS_MeshElement* findSegment(const SMDS_MeshNode* n1,
7908 const SMDS_MeshNode* n2)
7910 SMDS_ElemIteratorPtr it = n1->GetInverseElementIterator( SMDSAbs_Edge );
7911 while ( it->more() )
7913 const SMDS_MeshElement* seg = it->next();
7914 if ( seg->GetNodeIndex( n2 ) >= 0 )
7920 //=======================================================================
7921 //function : FindFreeBorder
7923 //=======================================================================
7925 #define ControlFreeBorder SMESH::Controls::FreeEdges::IsFreeEdge
7927 bool SMESH_MeshEditor::FindFreeBorder (const SMDS_MeshNode* theFirstNode,
7928 const SMDS_MeshNode* theSecondNode,
7929 const SMDS_MeshNode* theLastNode,
7930 list< const SMDS_MeshNode* > & theNodes,
7931 list< const SMDS_MeshElement* >& theFaces)
7933 if ( !theFirstNode || !theSecondNode )
7935 // find border face between theFirstNode and theSecondNode
7936 const SMDS_MeshElement* curElem = findAdjacentFace( theFirstNode, theSecondNode, 0 );
7940 theFaces.push_back( curElem );
7941 theNodes.push_back( theFirstNode );
7942 theNodes.push_back( theSecondNode );
7944 const SMDS_MeshNode *nIgnore = theFirstNode, *nStart = theSecondNode;
7945 TIDSortedElemSet foundElems;
7946 bool needTheLast = ( theLastNode != 0 );
7948 while ( nStart != theLastNode ) {
7949 if ( nStart == theFirstNode )
7950 return !needTheLast;
7952 // find all free border faces sharing form nStart
7954 list< const SMDS_MeshElement* > curElemList;
7955 list< const SMDS_MeshNode* > nStartList;
7956 SMDS_ElemIteratorPtr invElemIt = nStart->GetInverseElementIterator(SMDSAbs_Face);
7957 while ( invElemIt->more() ) {
7958 const SMDS_MeshElement* e = invElemIt->next();
7959 if ( e == curElem || foundElems.insert( e ).second ) {
7961 int iNode = 0, nbNodes = e->NbNodes();
7962 vector<const SMDS_MeshNode*> nodes(nbNodes+1);
7964 if ( e->IsQuadratic() ) {
7965 const SMDS_VtkFace* F =
7966 dynamic_cast<const SMDS_VtkFace*>(e);
7967 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
7968 // use special nodes iterator
7969 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
7970 while( anIter->more() ) {
7971 nodes[ iNode++ ] = cast2Node(anIter->next());
7975 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
7976 while ( nIt->more() )
7977 nodes[ iNode++ ] = static_cast<const SMDS_MeshNode*>( nIt->next() );
7979 nodes[ iNode ] = nodes[ 0 ];
7981 for ( iNode = 0; iNode < nbNodes; iNode++ )
7982 if (((nodes[ iNode ] == nStart && nodes[ iNode + 1] != nIgnore ) ||
7983 (nodes[ iNode + 1] == nStart && nodes[ iNode ] != nIgnore )) &&
7984 ControlFreeBorder( &nodes[ iNode ], e->GetID() ))
7986 nStartList.push_back( nodes[ iNode + ( nodes[ iNode ] == nStart ? 1 : 0 )]);
7987 curElemList.push_back( e );
7991 // analyse the found
7993 int nbNewBorders = curElemList.size();
7994 if ( nbNewBorders == 0 ) {
7995 // no free border furthermore
7996 return !needTheLast;
7998 else if ( nbNewBorders == 1 ) {
7999 // one more element found
8001 nStart = nStartList.front();
8002 curElem = curElemList.front();
8003 theFaces.push_back( curElem );
8004 theNodes.push_back( nStart );
8007 // several continuations found
8008 list< const SMDS_MeshElement* >::iterator curElemIt;
8009 list< const SMDS_MeshNode* >::iterator nStartIt;
8010 // check if one of them reached the last node
8011 if ( needTheLast ) {
8012 for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
8013 curElemIt!= curElemList.end();
8014 curElemIt++, nStartIt++ )
8015 if ( *nStartIt == theLastNode ) {
8016 theFaces.push_back( *curElemIt );
8017 theNodes.push_back( *nStartIt );
8021 // find the best free border by the continuations
8022 list<const SMDS_MeshNode*> contNodes[ 2 ], *cNL;
8023 list<const SMDS_MeshElement*> contFaces[ 2 ], *cFL;
8024 for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
8025 curElemIt!= curElemList.end();
8026 curElemIt++, nStartIt++ )
8028 cNL = & contNodes[ contNodes[0].empty() ? 0 : 1 ];
8029 cFL = & contFaces[ contFaces[0].empty() ? 0 : 1 ];
8030 // find one more free border
8031 if ( ! SMESH_MeshEditor::FindFreeBorder( nStart, *nStartIt, theLastNode, *cNL, *cFL )) {
8035 else if ( !contNodes[0].empty() && !contNodes[1].empty() ) {
8036 // choice: clear a worse one
8037 int iLongest = ( contNodes[0].size() < contNodes[1].size() ? 1 : 0 );
8038 int iWorse = ( needTheLast ? 1 - iLongest : iLongest );
8039 contNodes[ iWorse ].clear();
8040 contFaces[ iWorse ].clear();
8043 if ( contNodes[0].empty() && contNodes[1].empty() )
8046 // append the best free border
8047 cNL = & contNodes[ contNodes[0].empty() ? 1 : 0 ];
8048 cFL = & contFaces[ contFaces[0].empty() ? 1 : 0 ];
8049 theNodes.pop_back(); // remove nIgnore
8050 theNodes.pop_back(); // remove nStart
8051 theFaces.pop_back(); // remove curElem
8052 list< const SMDS_MeshNode* >::iterator nIt = cNL->begin();
8053 list< const SMDS_MeshElement* >::iterator fIt = cFL->begin();
8054 for ( ; nIt != cNL->end(); nIt++ ) theNodes.push_back( *nIt );
8055 for ( ; fIt != cFL->end(); fIt++ ) theFaces.push_back( *fIt );
8058 } // several continuations found
8059 } // while ( nStart != theLastNode )
8064 //=======================================================================
8065 //function : CheckFreeBorderNodes
8066 //purpose : Return true if the tree nodes are on a free border
8067 //=======================================================================
8069 bool SMESH_MeshEditor::CheckFreeBorderNodes(const SMDS_MeshNode* theNode1,
8070 const SMDS_MeshNode* theNode2,
8071 const SMDS_MeshNode* theNode3)
8073 list< const SMDS_MeshNode* > nodes;
8074 list< const SMDS_MeshElement* > faces;
8075 return FindFreeBorder( theNode1, theNode2, theNode3, nodes, faces);
8078 //=======================================================================
8079 //function : SewFreeBorder
8081 //warning : for border-to-side sewing theSideSecondNode is considered as
8082 // the last side node and theSideThirdNode is not used
8083 //=======================================================================
8085 SMESH_MeshEditor::Sew_Error
8086 SMESH_MeshEditor::SewFreeBorder (const SMDS_MeshNode* theBordFirstNode,
8087 const SMDS_MeshNode* theBordSecondNode,
8088 const SMDS_MeshNode* theBordLastNode,
8089 const SMDS_MeshNode* theSideFirstNode,
8090 const SMDS_MeshNode* theSideSecondNode,
8091 const SMDS_MeshNode* theSideThirdNode,
8092 const bool theSideIsFreeBorder,
8093 const bool toCreatePolygons,
8094 const bool toCreatePolyedrs)
8096 myLastCreatedElems.Clear();
8097 myLastCreatedNodes.Clear();
8099 Sew_Error aResult = SEW_OK;
8101 // ====================================
8102 // find side nodes and elements
8103 // ====================================
8105 list< const SMDS_MeshNode* > nSide[ 2 ];
8106 list< const SMDS_MeshElement* > eSide[ 2 ];
8107 list< const SMDS_MeshNode* >::iterator nIt[ 2 ];
8108 list< const SMDS_MeshElement* >::iterator eIt[ 2 ];
8112 if (!FindFreeBorder(theBordFirstNode,theBordSecondNode,theBordLastNode,
8113 nSide[0], eSide[0])) {
8114 MESSAGE(" Free Border 1 not found " );
8115 aResult = SEW_BORDER1_NOT_FOUND;
8117 if (theSideIsFreeBorder) {
8120 if (!FindFreeBorder(theSideFirstNode, theSideSecondNode, theSideThirdNode,
8121 nSide[1], eSide[1])) {
8122 MESSAGE(" Free Border 2 not found " );
8123 aResult = ( aResult != SEW_OK ? SEW_BOTH_BORDERS_NOT_FOUND : SEW_BORDER2_NOT_FOUND );
8126 if ( aResult != SEW_OK )
8129 if (!theSideIsFreeBorder) {
8133 // -------------------------------------------------------------------------
8135 // 1. If nodes to merge are not coincident, move nodes of the free border
8136 // from the coord sys defined by the direction from the first to last
8137 // nodes of the border to the correspondent sys of the side 2
8138 // 2. On the side 2, find the links most co-directed with the correspondent
8139 // links of the free border
8140 // -------------------------------------------------------------------------
8142 // 1. Since sewing may break if there are volumes to split on the side 2,
8143 // we wont move nodes but just compute new coordinates for them
8144 typedef map<const SMDS_MeshNode*, gp_XYZ> TNodeXYZMap;
8145 TNodeXYZMap nBordXYZ;
8146 list< const SMDS_MeshNode* >& bordNodes = nSide[ 0 ];
8147 list< const SMDS_MeshNode* >::iterator nBordIt;
8149 gp_XYZ Pb1( theBordFirstNode->X(), theBordFirstNode->Y(), theBordFirstNode->Z() );
8150 gp_XYZ Pb2( theBordLastNode->X(), theBordLastNode->Y(), theBordLastNode->Z() );
8151 gp_XYZ Ps1( theSideFirstNode->X(), theSideFirstNode->Y(), theSideFirstNode->Z() );
8152 gp_XYZ Ps2( theSideSecondNode->X(), theSideSecondNode->Y(), theSideSecondNode->Z() );
8153 double tol2 = 1.e-8;
8154 gp_Vec Vbs1( Pb1 - Ps1 ),Vbs2( Pb2 - Ps2 );
8155 if ( Vbs1.SquareMagnitude() > tol2 || Vbs2.SquareMagnitude() > tol2 ) {
8156 // Need node movement.
8158 // find X and Z axes to create trsf
8159 gp_Vec Zb( Pb1 - Pb2 ), Zs( Ps1 - Ps2 );
8161 if ( X.SquareMagnitude() <= gp::Resolution() * gp::Resolution() )
8163 X = gp_Ax2( gp::Origin(), Zb ).XDirection();
8166 gp_Ax3 toBordAx( Pb1, Zb, X );
8167 gp_Ax3 fromSideAx( Ps1, Zs, X );
8168 gp_Ax3 toGlobalAx( gp::Origin(), gp::DZ(), gp::DX() );
8170 gp_Trsf toBordSys, fromSide2Sys;
8171 toBordSys.SetTransformation( toBordAx );
8172 fromSide2Sys.SetTransformation( fromSideAx, toGlobalAx );
8173 fromSide2Sys.SetScaleFactor( Zs.Magnitude() / Zb.Magnitude() );
8176 for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
8177 const SMDS_MeshNode* n = *nBordIt;
8178 gp_XYZ xyz( n->X(),n->Y(),n->Z() );
8179 toBordSys.Transforms( xyz );
8180 fromSide2Sys.Transforms( xyz );
8181 nBordXYZ.insert( TNodeXYZMap::value_type( n, xyz ));
8185 // just insert nodes XYZ in the nBordXYZ map
8186 for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
8187 const SMDS_MeshNode* n = *nBordIt;
8188 nBordXYZ.insert( TNodeXYZMap::value_type( n, gp_XYZ( n->X(),n->Y(),n->Z() )));
8192 // 2. On the side 2, find the links most co-directed with the correspondent
8193 // links of the free border
8195 list< const SMDS_MeshElement* >& sideElems = eSide[ 1 ];
8196 list< const SMDS_MeshNode* >& sideNodes = nSide[ 1 ];
8197 sideNodes.push_back( theSideFirstNode );
8199 bool hasVolumes = false;
8200 LinkID_Gen aLinkID_Gen( GetMeshDS() );
8201 set<long> foundSideLinkIDs, checkedLinkIDs;
8202 SMDS_VolumeTool volume;
8203 //const SMDS_MeshNode* faceNodes[ 4 ];
8205 const SMDS_MeshNode* sideNode;
8206 const SMDS_MeshElement* sideElem = 0;
8207 const SMDS_MeshNode* prevSideNode = theSideFirstNode;
8208 const SMDS_MeshNode* prevBordNode = theBordFirstNode;
8209 nBordIt = bordNodes.begin();
8211 // border node position and border link direction to compare with
8212 gp_XYZ bordPos = nBordXYZ[ *nBordIt ];
8213 gp_XYZ bordDir = bordPos - nBordXYZ[ prevBordNode ];
8214 // choose next side node by link direction or by closeness to
8215 // the current border node:
8216 bool searchByDir = ( *nBordIt != theBordLastNode );
8218 // find the next node on the Side 2
8220 double maxDot = -DBL_MAX, minDist = DBL_MAX;
8222 checkedLinkIDs.clear();
8223 gp_XYZ prevXYZ( prevSideNode->X(), prevSideNode->Y(), prevSideNode->Z() );
8225 // loop on inverse elements of current node (prevSideNode) on the Side 2
8226 SMDS_ElemIteratorPtr invElemIt = prevSideNode->GetInverseElementIterator();
8227 while ( invElemIt->more() )
8229 const SMDS_MeshElement* elem = invElemIt->next();
8230 // prepare data for a loop on links coming to prevSideNode, of a face or a volume
8231 int iPrevNode = 0, iNode = 0, nbNodes = elem->NbNodes();
8232 vector< const SMDS_MeshNode* > faceNodes( nbNodes, (const SMDS_MeshNode*)0 );
8233 bool isVolume = volume.Set( elem );
8234 const SMDS_MeshNode** nodes = isVolume ? volume.GetNodes() : & faceNodes[0];
8235 if ( isVolume ) // --volume
8237 else if ( elem->GetType()==SMDSAbs_Face ) { // --face
8238 // retrieve all face nodes and find iPrevNode - an index of the prevSideNode
8239 if(elem->IsQuadratic()) {
8240 const SMDS_VtkFace* F =
8241 dynamic_cast<const SMDS_VtkFace*>(elem);
8242 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8243 // use special nodes iterator
8244 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8245 while( anIter->more() ) {
8246 nodes[ iNode ] = cast2Node(anIter->next());
8247 if ( nodes[ iNode++ ] == prevSideNode )
8248 iPrevNode = iNode - 1;
8252 SMDS_ElemIteratorPtr nIt = elem->nodesIterator();
8253 while ( nIt->more() ) {
8254 nodes[ iNode ] = cast2Node( nIt->next() );
8255 if ( nodes[ iNode++ ] == prevSideNode )
8256 iPrevNode = iNode - 1;
8259 // there are 2 links to check
8264 // loop on links, to be precise, on the second node of links
8265 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
8266 const SMDS_MeshNode* n = nodes[ iNode ];
8268 if ( !volume.IsLinked( n, prevSideNode ))
8272 if ( iNode ) // a node before prevSideNode
8273 n = nodes[ iPrevNode == 0 ? elem->NbNodes() - 1 : iPrevNode - 1 ];
8274 else // a node after prevSideNode
8275 n = nodes[ iPrevNode + 1 == elem->NbNodes() ? 0 : iPrevNode + 1 ];
8277 // check if this link was already used
8278 long iLink = aLinkID_Gen.GetLinkID( prevSideNode, n );
8279 bool isJustChecked = !checkedLinkIDs.insert( iLink ).second;
8280 if (!isJustChecked &&
8281 foundSideLinkIDs.find( iLink ) == foundSideLinkIDs.end() )
8283 // test a link geometrically
8284 gp_XYZ nextXYZ ( n->X(), n->Y(), n->Z() );
8285 bool linkIsBetter = false;
8286 double dot = 0.0, dist = 0.0;
8287 if ( searchByDir ) { // choose most co-directed link
8288 dot = bordDir * ( nextXYZ - prevXYZ ).Normalized();
8289 linkIsBetter = ( dot > maxDot );
8291 else { // choose link with the node closest to bordPos
8292 dist = ( nextXYZ - bordPos ).SquareModulus();
8293 linkIsBetter = ( dist < minDist );
8295 if ( linkIsBetter ) {
8304 } // loop on inverse elements of prevSideNode
8307 MESSAGE(" Cant find path by links of the Side 2 ");
8308 return SEW_BAD_SIDE_NODES;
8310 sideNodes.push_back( sideNode );
8311 sideElems.push_back( sideElem );
8312 foundSideLinkIDs.insert ( linkID );
8313 prevSideNode = sideNode;
8315 if ( *nBordIt == theBordLastNode )
8316 searchByDir = false;
8318 // find the next border link to compare with
8319 gp_XYZ sidePos( sideNode->X(), sideNode->Y(), sideNode->Z() );
8320 searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8321 // move to next border node if sideNode is before forward border node (bordPos)
8322 while ( *nBordIt != theBordLastNode && !searchByDir ) {
8323 prevBordNode = *nBordIt;
8325 bordPos = nBordXYZ[ *nBordIt ];
8326 bordDir = bordPos - nBordXYZ[ prevBordNode ];
8327 searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8331 while ( sideNode != theSideSecondNode );
8333 if ( hasVolumes && sideNodes.size () != bordNodes.size() && !toCreatePolyedrs) {
8334 MESSAGE("VOLUME SPLITTING IS FORBIDDEN");
8335 return SEW_VOLUMES_TO_SPLIT; // volume splitting is forbidden
8337 } // end nodes search on the side 2
8339 // ============================
8340 // sew the border to the side 2
8341 // ============================
8343 int nbNodes[] = { (int)nSide[0].size(), (int)nSide[1].size() };
8344 int maxNbNodes = Max( nbNodes[0], nbNodes[1] );
8346 bool toMergeConformal = ( nbNodes[0] == nbNodes[1] );
8347 if ( toMergeConformal && toCreatePolygons )
8349 // do not merge quadrangles if polygons are OK (IPAL0052824)
8350 eIt[0] = eSide[0].begin();
8351 eIt[1] = eSide[1].begin();
8352 bool allQuads[2] = { true, true };
8353 for ( int iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8354 for ( ; allQuads[iBord] && eIt[iBord] != eSide[iBord].end(); ++eIt[iBord] )
8355 allQuads[iBord] = ( (*eIt[iBord])->NbCornerNodes() == 4 );
8357 toMergeConformal = ( !allQuads[0] && !allQuads[1] );
8360 TListOfListOfNodes nodeGroupsToMerge;
8361 if (( toMergeConformal ) ||
8362 ( theSideIsFreeBorder && !theSideThirdNode )) {
8364 // all nodes are to be merged
8366 for (nIt[0] = nSide[0].begin(), nIt[1] = nSide[1].begin();
8367 nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end();
8368 nIt[0]++, nIt[1]++ )
8370 nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8371 nodeGroupsToMerge.back().push_back( *nIt[1] ); // to keep
8372 nodeGroupsToMerge.back().push_back( *nIt[0] ); // to remove
8377 // insert new nodes into the border and the side to get equal nb of segments
8379 // get normalized parameters of nodes on the borders
8380 vector< double > param[ 2 ];
8381 param[0].resize( maxNbNodes );
8382 param[1].resize( maxNbNodes );
8384 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8385 list< const SMDS_MeshNode* >& nodes = nSide[ iBord ];
8386 list< const SMDS_MeshNode* >::iterator nIt = nodes.begin();
8387 const SMDS_MeshNode* nPrev = *nIt;
8388 double bordLength = 0;
8389 for ( iNode = 0; nIt != nodes.end(); nIt++, iNode++ ) { // loop on border nodes
8390 const SMDS_MeshNode* nCur = *nIt;
8391 gp_XYZ segment (nCur->X() - nPrev->X(),
8392 nCur->Y() - nPrev->Y(),
8393 nCur->Z() - nPrev->Z());
8394 double segmentLen = segment.Modulus();
8395 bordLength += segmentLen;
8396 param[ iBord ][ iNode ] = bordLength;
8399 // normalize within [0,1]
8400 for ( iNode = 0; iNode < nbNodes[ iBord ]; iNode++ ) {
8401 param[ iBord ][ iNode ] /= bordLength;
8405 // loop on border segments
8406 const SMDS_MeshNode *nPrev[ 2 ] = { 0, 0 };
8407 int i[ 2 ] = { 0, 0 };
8408 nIt[0] = nSide[0].begin(); eIt[0] = eSide[0].begin();
8409 nIt[1] = nSide[1].begin(); eIt[1] = eSide[1].begin();
8411 TElemOfNodeListMap insertMap;
8412 TElemOfNodeListMap::iterator insertMapIt;
8414 // key: elem to insert nodes into
8415 // value: 2 nodes to insert between + nodes to be inserted
8417 bool next[ 2 ] = { false, false };
8419 // find min adjacent segment length after sewing
8420 double nextParam = 10., prevParam = 0;
8421 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8422 if ( i[ iBord ] + 1 < nbNodes[ iBord ])
8423 nextParam = Min( nextParam, param[iBord][ i[iBord] + 1 ]);
8424 if ( i[ iBord ] > 0 )
8425 prevParam = Max( prevParam, param[iBord][ i[iBord] - 1 ]);
8427 double minParam = Min( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8428 double maxParam = Max( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8429 double minSegLen = Min( nextParam - minParam, maxParam - prevParam );
8431 // choose to insert or to merge nodes
8432 double du = param[ 1 ][ i[1] ] - param[ 0 ][ i[0] ];
8433 if ( Abs( du ) <= minSegLen * 0.2 ) {
8436 nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8437 const SMDS_MeshNode* n0 = *nIt[0];
8438 const SMDS_MeshNode* n1 = *nIt[1];
8439 nodeGroupsToMerge.back().push_back( n1 );
8440 nodeGroupsToMerge.back().push_back( n0 );
8441 // position of node of the border changes due to merge
8442 param[ 0 ][ i[0] ] += du;
8443 // move n1 for the sake of elem shape evaluation during insertion.
8444 // n1 will be removed by MergeNodes() anyway
8445 const_cast<SMDS_MeshNode*>( n0 )->setXYZ( n1->X(), n1->Y(), n1->Z() );
8446 next[0] = next[1] = true;
8451 int intoBord = ( du < 0 ) ? 0 : 1;
8452 const SMDS_MeshElement* elem = *eIt [ intoBord ];
8453 const SMDS_MeshNode* n1 = nPrev[ intoBord ];
8454 const SMDS_MeshNode* n2 = *nIt [ intoBord ];
8455 const SMDS_MeshNode* nIns = *nIt [ 1 - intoBord ];
8456 if ( intoBord == 1 ) {
8457 // move node of the border to be on a link of elem of the side
8458 gp_XYZ p1 (n1->X(), n1->Y(), n1->Z());
8459 gp_XYZ p2 (n2->X(), n2->Y(), n2->Z());
8460 double ratio = du / ( param[ 1 ][ i[1] ] - param[ 1 ][ i[1]-1 ]);
8461 gp_XYZ p = p2 * ( 1 - ratio ) + p1 * ratio;
8462 GetMeshDS()->MoveNode( nIns, p.X(), p.Y(), p.Z() );
8464 insertMapIt = insertMap.find( elem );
8465 bool notFound = ( insertMapIt == insertMap.end() );
8466 bool otherLink = ( !notFound && (*insertMapIt).second.front() != n1 );
8468 // insert into another link of the same element:
8469 // 1. perform insertion into the other link of the elem
8470 list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8471 const SMDS_MeshNode* n12 = nodeList.front(); nodeList.pop_front();
8472 const SMDS_MeshNode* n22 = nodeList.front(); nodeList.pop_front();
8473 InsertNodesIntoLink( elem, n12, n22, nodeList, toCreatePolygons );
8474 // 2. perform insertion into the link of adjacent faces
8475 while ( const SMDS_MeshElement* adjElem = findAdjacentFace( n12, n22, elem )) {
8476 InsertNodesIntoLink( adjElem, n12, n22, nodeList, toCreatePolygons );
8478 while ( const SMDS_MeshElement* seg = findSegment( n12, n22 )) {
8479 InsertNodesIntoLink( seg, n12, n22, nodeList );
8481 if (toCreatePolyedrs) {
8482 // perform insertion into the links of adjacent volumes
8483 UpdateVolumes(n12, n22, nodeList);
8485 // 3. find an element appeared on n1 and n2 after the insertion
8486 insertMap.erase( elem );
8487 elem = findAdjacentFace( n1, n2, 0 );
8489 if ( notFound || otherLink ) {
8490 // add element and nodes of the side into the insertMap
8491 insertMapIt = insertMap.insert( make_pair( elem, list<const SMDS_MeshNode*>() )).first;
8492 (*insertMapIt).second.push_back( n1 );
8493 (*insertMapIt).second.push_back( n2 );
8495 // add node to be inserted into elem
8496 (*insertMapIt).second.push_back( nIns );
8497 next[ 1 - intoBord ] = true;
8500 // go to the next segment
8501 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8502 if ( next[ iBord ] ) {
8503 if ( i[ iBord ] != 0 && eIt[ iBord ] != eSide[ iBord ].end())
8505 nPrev[ iBord ] = *nIt[ iBord ];
8506 nIt[ iBord ]++; i[ iBord ]++;
8510 while ( nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end());
8512 // perform insertion of nodes into elements
8514 for (insertMapIt = insertMap.begin();
8515 insertMapIt != insertMap.end();
8518 const SMDS_MeshElement* elem = (*insertMapIt).first;
8519 list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8520 const SMDS_MeshNode* n1 = nodeList.front(); nodeList.pop_front();
8521 const SMDS_MeshNode* n2 = nodeList.front(); nodeList.pop_front();
8523 InsertNodesIntoLink( elem, n1, n2, nodeList, toCreatePolygons );
8525 while ( const SMDS_MeshElement* seg = findSegment( n1, n2 )) {
8526 InsertNodesIntoLink( seg, n1, n2, nodeList );
8529 if ( !theSideIsFreeBorder ) {
8530 // look for and insert nodes into the faces adjacent to elem
8531 while ( const SMDS_MeshElement* adjElem = findAdjacentFace( n1, n2, elem )) {
8532 InsertNodesIntoLink( adjElem, n1, n2, nodeList, toCreatePolygons );
8535 if (toCreatePolyedrs) {
8536 // perform insertion into the links of adjacent volumes
8537 UpdateVolumes(n1, n2, nodeList);
8540 } // end: insert new nodes
8542 MergeNodes ( nodeGroupsToMerge );
8545 // Remove coincident segments
8548 TIDSortedElemSet segments;
8549 SMESH_SequenceOfElemPtr newFaces;
8550 for ( int i = 1; i <= myLastCreatedElems.Length(); ++i )
8552 if ( !myLastCreatedElems(i) ) continue;
8553 if ( myLastCreatedElems(i)->GetType() == SMDSAbs_Edge )
8554 segments.insert( segments.end(), myLastCreatedElems(i) );
8556 newFaces.Append( myLastCreatedElems(i) );
8558 // get segments adjacent to merged nodes
8559 TListOfListOfNodes::iterator groupIt = nodeGroupsToMerge.begin();
8560 for ( ; groupIt != nodeGroupsToMerge.end(); groupIt++ )
8562 const list<const SMDS_MeshNode*>& nodes = *groupIt;
8563 SMDS_ElemIteratorPtr segIt = nodes.front()->GetInverseElementIterator( SMDSAbs_Edge );
8564 while ( segIt->more() )
8565 segments.insert( segIt->next() );
8569 TListOfListOfElementsID equalGroups;
8570 if ( !segments.empty() )
8571 FindEqualElements( segments, equalGroups );
8572 if ( !equalGroups.empty() )
8574 // remove from segments those that will be removed
8575 TListOfListOfElementsID::iterator itGroups = equalGroups.begin();
8576 for ( ; itGroups != equalGroups.end(); ++itGroups )
8578 list< int >& group = *itGroups;
8579 list< int >::iterator id = group.begin();
8580 for ( ++id; id != group.end(); ++id )
8581 if ( const SMDS_MeshElement* seg = GetMeshDS()->FindElement( *id ))
8582 segments.erase( seg );
8584 // remove equal segments
8585 MergeElements( equalGroups );
8587 // restore myLastCreatedElems
8588 myLastCreatedElems = newFaces;
8589 TIDSortedElemSet::iterator seg = segments.begin();
8590 for ( ; seg != segments.end(); ++seg )
8591 myLastCreatedElems.Append( *seg );
8597 //=======================================================================
8598 //function : InsertNodesIntoLink
8599 //purpose : insert theNodesToInsert into theElement between theBetweenNode1
8600 // and theBetweenNode2 and split theElement
8601 //=======================================================================
8603 void SMESH_MeshEditor::InsertNodesIntoLink(const SMDS_MeshElement* theElement,
8604 const SMDS_MeshNode* theBetweenNode1,
8605 const SMDS_MeshNode* theBetweenNode2,
8606 list<const SMDS_MeshNode*>& theNodesToInsert,
8607 const bool toCreatePoly)
8609 if ( !theElement ) return;
8611 SMESHDS_Mesh *aMesh = GetMeshDS();
8612 vector<const SMDS_MeshElement*> newElems;
8614 if ( theElement->GetType() == SMDSAbs_Edge )
8616 theNodesToInsert.push_front( theBetweenNode1 );
8617 theNodesToInsert.push_back ( theBetweenNode2 );
8618 list<const SMDS_MeshNode*>::iterator n = theNodesToInsert.begin();
8619 const SMDS_MeshNode* n1 = *n;
8620 for ( ++n; n != theNodesToInsert.end(); ++n )
8622 const SMDS_MeshNode* n2 = *n;
8623 if ( const SMDS_MeshElement* seg = aMesh->FindEdge( n1, n2 ))
8624 AddToSameGroups( seg, theElement, aMesh );
8626 newElems.push_back( aMesh->AddEdge ( n1, n2 ));
8629 theNodesToInsert.pop_front();
8630 theNodesToInsert.pop_back();
8632 if ( theElement->IsQuadratic() ) // add a not split part
8634 vector<const SMDS_MeshNode*> nodes( theElement->begin_nodes(),
8635 theElement->end_nodes() );
8636 int iOther = 0, nbN = nodes.size();
8637 for ( ; iOther < nbN; ++iOther )
8638 if ( nodes[iOther] != theBetweenNode1 &&
8639 nodes[iOther] != theBetweenNode2 )
8643 if ( const SMDS_MeshElement* seg = aMesh->FindEdge( nodes[0], nodes[1] ))
8644 AddToSameGroups( seg, theElement, aMesh );
8646 newElems.push_back( aMesh->AddEdge ( nodes[0], nodes[1] ));
8648 else if ( iOther == 2 )
8650 if ( const SMDS_MeshElement* seg = aMesh->FindEdge( nodes[1], nodes[2] ))
8651 AddToSameGroups( seg, theElement, aMesh );
8653 newElems.push_back( aMesh->AddEdge ( nodes[1], nodes[2] ));
8656 // treat new elements
8657 for ( size_t i = 0; i < newElems.size(); ++i )
8660 aMesh->SetMeshElementOnShape( newElems[i], theElement->getshapeId() );
8661 myLastCreatedElems.Append( newElems[i] );
8663 ReplaceElemInGroups( theElement, newElems, aMesh );
8664 aMesh->RemoveElement( theElement );
8667 } // if ( theElement->GetType() == SMDSAbs_Edge )
8669 const SMDS_MeshElement* theFace = theElement;
8670 if ( theFace->GetType() != SMDSAbs_Face ) return;
8672 // find indices of 2 link nodes and of the rest nodes
8673 int iNode = 0, il1, il2, i3, i4;
8674 il1 = il2 = i3 = i4 = -1;
8675 vector<const SMDS_MeshNode*> nodes( theFace->NbNodes() );
8677 SMDS_NodeIteratorPtr nodeIt = theFace->interlacedNodesIterator();
8678 while ( nodeIt->more() ) {
8679 const SMDS_MeshNode* n = nodeIt->next();
8680 if ( n == theBetweenNode1 )
8682 else if ( n == theBetweenNode2 )
8688 nodes[ iNode++ ] = n;
8690 if ( il1 < 0 || il2 < 0 || i3 < 0 )
8693 // arrange link nodes to go one after another regarding the face orientation
8694 bool reverse = ( Abs( il2 - il1 ) == 1 ? il2 < il1 : il1 < il2 );
8695 list<const SMDS_MeshNode *> aNodesToInsert = theNodesToInsert;
8700 aNodesToInsert.reverse();
8702 // check that not link nodes of a quadrangles are in good order
8703 int nbFaceNodes = theFace->NbNodes();
8704 if ( nbFaceNodes == 4 && i4 - i3 != 1 ) {
8710 if (toCreatePoly || theFace->IsPoly()) {
8713 vector<const SMDS_MeshNode *> poly_nodes (nbFaceNodes + aNodesToInsert.size());
8715 // add nodes of face up to first node of link
8718 if ( theFace->IsQuadratic() ) {
8719 const SMDS_VtkFace* F = dynamic_cast<const SMDS_VtkFace*>(theFace);
8720 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8721 // use special nodes iterator
8722 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8723 while( anIter->more() && !isFLN ) {
8724 const SMDS_MeshNode* n = cast2Node(anIter->next());
8725 poly_nodes[iNode++] = n;
8726 if (n == nodes[il1]) {
8730 // add nodes to insert
8731 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8732 for (; nIt != aNodesToInsert.end(); nIt++) {
8733 poly_nodes[iNode++] = *nIt;
8735 // add nodes of face starting from last node of link
8736 while ( anIter->more() ) {
8737 poly_nodes[iNode++] = cast2Node(anIter->next());
8741 SMDS_ElemIteratorPtr nodeIt = theFace->nodesIterator();
8742 while ( nodeIt->more() && !isFLN ) {
8743 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8744 poly_nodes[iNode++] = n;
8745 if (n == nodes[il1]) {
8749 // add nodes to insert
8750 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8751 for (; nIt != aNodesToInsert.end(); nIt++) {
8752 poly_nodes[iNode++] = *nIt;
8754 // add nodes of face starting from last node of link
8755 while ( nodeIt->more() ) {
8756 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8757 poly_nodes[iNode++] = n;
8762 newElems.push_back( aMesh->AddPolygonalFace( poly_nodes ));
8765 else if ( !theFace->IsQuadratic() )
8767 // put aNodesToInsert between theBetweenNode1 and theBetweenNode2
8768 int nbLinkNodes = 2 + aNodesToInsert.size();
8769 //const SMDS_MeshNode* linkNodes[ nbLinkNodes ];
8770 vector<const SMDS_MeshNode*> linkNodes( nbLinkNodes );
8771 linkNodes[ 0 ] = nodes[ il1 ];
8772 linkNodes[ nbLinkNodes - 1 ] = nodes[ il2 ];
8773 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8774 for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
8775 linkNodes[ iNode++ ] = *nIt;
8777 // decide how to split a quadrangle: compare possible variants
8778 // and choose which of splits to be a quadrangle
8779 int i1, i2, iSplit, nbSplits = nbLinkNodes - 1, iBestQuad = 0;
8780 if ( nbFaceNodes == 3 ) {
8781 iBestQuad = nbSplits;
8784 else if ( nbFaceNodes == 4 ) {
8785 SMESH::Controls::NumericalFunctorPtr aCrit( new SMESH::Controls::AspectRatio);
8786 double aBestRate = DBL_MAX;
8787 for ( int iQuad = 0; iQuad < nbSplits; iQuad++ ) {
8789 double aBadRate = 0;
8790 // evaluate elements quality
8791 for ( iSplit = 0; iSplit < nbSplits; iSplit++ ) {
8792 if ( iSplit == iQuad ) {
8793 SMDS_FaceOfNodes quad (linkNodes[ i1++ ],
8797 aBadRate += getBadRate( &quad, aCrit );
8800 SMDS_FaceOfNodes tria (linkNodes[ i1++ ],
8802 nodes[ iSplit < iQuad ? i4 : i3 ]);
8803 aBadRate += getBadRate( &tria, aCrit );
8807 if ( aBadRate < aBestRate ) {
8809 aBestRate = aBadRate;
8814 // create new elements
8816 for ( iSplit = 0; iSplit < nbSplits - 1; iSplit++ )
8818 if ( iSplit == iBestQuad )
8819 newElems.push_back( aMesh->AddFace (linkNodes[ i1++ ],
8824 newElems.push_back( aMesh->AddFace (linkNodes[ i1++ ],
8826 nodes[ iSplit < iBestQuad ? i4 : i3 ]));
8829 const SMDS_MeshNode* newNodes[ 4 ];
8830 newNodes[ 0 ] = linkNodes[ i1 ];
8831 newNodes[ 1 ] = linkNodes[ i2 ];
8832 newNodes[ 2 ] = nodes[ iSplit >= iBestQuad ? i3 : i4 ];
8833 newNodes[ 3 ] = nodes[ i4 ];
8834 if (iSplit == iBestQuad)
8835 newElems.push_back( aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2], newNodes[3] ));
8837 newElems.push_back( aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2] ));
8839 } // end if(!theFace->IsQuadratic())
8841 else { // theFace is quadratic
8842 // we have to split theFace on simple triangles and one simple quadrangle
8844 int nbshift = tmp*2;
8845 // shift nodes in nodes[] by nbshift
8847 for(i=0; i<nbshift; i++) {
8848 const SMDS_MeshNode* n = nodes[0];
8849 for(j=0; j<nbFaceNodes-1; j++) {
8850 nodes[j] = nodes[j+1];
8852 nodes[nbFaceNodes-1] = n;
8854 il1 = il1 - nbshift;
8855 // now have to insert nodes between n0 and n1 or n1 and n2 (see below)
8856 // n0 n1 n2 n0 n1 n2
8857 // +-----+-----+ +-----+-----+
8866 // create new elements
8868 if ( nbFaceNodes == 6 ) { // quadratic triangle
8869 newElems.push_back( aMesh->AddFace( nodes[3], nodes[4], nodes[5] ));
8870 if ( theFace->IsMediumNode(nodes[il1]) ) {
8871 // create quadrangle
8872 newElems.push_back( aMesh->AddFace( nodes[0], nodes[1], nodes[3], nodes[5] ));
8878 // create quadrangle
8879 newElems.push_back( aMesh->AddFace( nodes[1], nodes[2], nodes[3], nodes[5] ));
8885 else { // nbFaceNodes==8 - quadratic quadrangle
8886 newElems.push_back( aMesh->AddFace( nodes[3], nodes[4], nodes[5] ));
8887 newElems.push_back( aMesh->AddFace( nodes[5], nodes[6], nodes[7] ));
8888 newElems.push_back( aMesh->AddFace( nodes[5], nodes[7], nodes[3] ));
8889 if ( theFace->IsMediumNode( nodes[ il1 ])) {
8890 // create quadrangle
8891 newElems.push_back( aMesh->AddFace( nodes[0], nodes[1], nodes[3], nodes[7] ));
8897 // create quadrangle
8898 newElems.push_back( aMesh->AddFace( nodes[1], nodes[2], nodes[3], nodes[7] ));
8904 // create needed triangles using n1,n2,n3 and inserted nodes
8905 int nbn = 2 + aNodesToInsert.size();
8906 vector<const SMDS_MeshNode*> aNodes(nbn);
8907 aNodes[0 ] = nodes[n1];
8908 aNodes[nbn-1] = nodes[n2];
8909 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8910 for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
8911 aNodes[iNode++] = *nIt;
8913 for ( i = 1; i < nbn; i++ )
8914 newElems.push_back( aMesh->AddFace( aNodes[i-1], aNodes[i], nodes[n3] ));
8917 // remove the old face
8918 for ( size_t i = 0; i < newElems.size(); ++i )
8921 aMesh->SetMeshElementOnShape( newElems[i], theFace->getshapeId() );
8922 myLastCreatedElems.Append( newElems[i] );
8924 ReplaceElemInGroups( theFace, newElems, aMesh );
8925 aMesh->RemoveElement(theFace);
8927 } // InsertNodesIntoLink()
8929 //=======================================================================
8930 //function : UpdateVolumes
8932 //=======================================================================
8934 void SMESH_MeshEditor::UpdateVolumes (const SMDS_MeshNode* theBetweenNode1,
8935 const SMDS_MeshNode* theBetweenNode2,
8936 list<const SMDS_MeshNode*>& theNodesToInsert)
8938 myLastCreatedElems.Clear();
8939 myLastCreatedNodes.Clear();
8941 SMDS_ElemIteratorPtr invElemIt = theBetweenNode1->GetInverseElementIterator(SMDSAbs_Volume);
8942 while (invElemIt->more()) { // loop on inverse elements of theBetweenNode1
8943 const SMDS_MeshElement* elem = invElemIt->next();
8945 // check, if current volume has link theBetweenNode1 - theBetweenNode2
8946 SMDS_VolumeTool aVolume (elem);
8947 if (!aVolume.IsLinked(theBetweenNode1, theBetweenNode2))
8950 // insert new nodes in all faces of the volume, sharing link theBetweenNode1 - theBetweenNode2
8951 int iface, nbFaces = aVolume.NbFaces();
8952 vector<const SMDS_MeshNode *> poly_nodes;
8953 vector<int> quantities (nbFaces);
8955 for (iface = 0; iface < nbFaces; iface++) {
8956 int nbFaceNodes = aVolume.NbFaceNodes(iface), nbInserted = 0;
8957 // faceNodes will contain (nbFaceNodes + 1) nodes, last = first
8958 const SMDS_MeshNode** faceNodes = aVolume.GetFaceNodes(iface);
8960 for (int inode = 0; inode < nbFaceNodes; inode++) {
8961 poly_nodes.push_back(faceNodes[inode]);
8963 if (nbInserted == 0) {
8964 if (faceNodes[inode] == theBetweenNode1) {
8965 if (faceNodes[inode + 1] == theBetweenNode2) {
8966 nbInserted = theNodesToInsert.size();
8968 // add nodes to insert
8969 list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.begin();
8970 for (; nIt != theNodesToInsert.end(); nIt++) {
8971 poly_nodes.push_back(*nIt);
8975 else if (faceNodes[inode] == theBetweenNode2) {
8976 if (faceNodes[inode + 1] == theBetweenNode1) {
8977 nbInserted = theNodesToInsert.size();
8979 // add nodes to insert in reversed order
8980 list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.end();
8982 for (; nIt != theNodesToInsert.begin(); nIt--) {
8983 poly_nodes.push_back(*nIt);
8985 poly_nodes.push_back(*nIt);
8992 quantities[iface] = nbFaceNodes + nbInserted;
8995 // Replace the volume
8996 SMESHDS_Mesh *aMesh = GetMeshDS();
8998 if ( SMDS_MeshElement* newElem = aMesh->AddPolyhedralVolume( poly_nodes, quantities ))
9000 aMesh->SetMeshElementOnShape( newElem, elem->getshapeId() );
9001 myLastCreatedElems.Append( newElem );
9002 ReplaceElemInGroups( elem, newElem, aMesh );
9004 aMesh->RemoveElement( elem );
9010 //================================================================================
9012 * \brief Transform any volume into data of SMDSEntity_Polyhedra
9014 //================================================================================
9016 void volumeToPolyhedron( const SMDS_MeshElement* elem,
9017 vector<const SMDS_MeshNode *> & nodes,
9018 vector<int> & nbNodeInFaces )
9021 nbNodeInFaces.clear();
9022 SMDS_VolumeTool vTool ( elem );
9023 for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
9025 const SMDS_MeshNode** fNodes = vTool.GetFaceNodes( iF );
9026 nodes.insert( nodes.end(), fNodes, fNodes + vTool.NbFaceNodes( iF ));
9027 nbNodeInFaces.push_back( vTool.NbFaceNodes( iF ));
9032 //=======================================================================
9034 * \brief Convert elements contained in a sub-mesh to quadratic
9035 * \return int - nb of checked elements
9037 //=======================================================================
9039 int SMESH_MeshEditor::convertElemToQuadratic(SMESHDS_SubMesh * theSm,
9040 SMESH_MesherHelper& theHelper,
9041 const bool theForce3d)
9044 if( !theSm ) return nbElem;
9046 vector<int> nbNodeInFaces;
9047 vector<const SMDS_MeshNode *> nodes;
9048 SMDS_ElemIteratorPtr ElemItr = theSm->GetElements();
9049 while(ElemItr->more())
9052 const SMDS_MeshElement* elem = ElemItr->next();
9053 if( !elem ) continue;
9055 // analyse a necessity of conversion
9056 const SMDSAbs_ElementType aType = elem->GetType();
9057 if ( aType < SMDSAbs_Edge || aType > SMDSAbs_Volume )
9059 const SMDSAbs_EntityType aGeomType = elem->GetEntityType();
9060 bool hasCentralNodes = false;
9061 if ( elem->IsQuadratic() )
9064 switch ( aGeomType ) {
9065 case SMDSEntity_Quad_Triangle:
9066 case SMDSEntity_Quad_Quadrangle:
9067 case SMDSEntity_Quad_Hexa:
9068 alreadyOK = !theHelper.GetIsBiQuadratic(); break;
9070 case SMDSEntity_BiQuad_Triangle:
9071 case SMDSEntity_BiQuad_Quadrangle:
9072 case SMDSEntity_TriQuad_Hexa:
9073 alreadyOK = theHelper.GetIsBiQuadratic();
9074 hasCentralNodes = true;
9079 // take into account already present modium nodes
9081 case SMDSAbs_Volume:
9082 theHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( elem )); break;
9084 theHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( elem )); break;
9086 theHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( elem )); break;
9092 // get elem data needed to re-create it
9094 const int id = elem->GetID();
9095 const int nbNodes = elem->NbCornerNodes();
9096 nodes.assign(elem->begin_nodes(), elem->end_nodes());
9097 if ( aGeomType == SMDSEntity_Polyhedra )
9098 nbNodeInFaces = static_cast<const SMDS_VtkVolume* >( elem )->GetQuantities();
9099 else if ( aGeomType == SMDSEntity_Hexagonal_Prism )
9100 volumeToPolyhedron( elem, nodes, nbNodeInFaces );
9102 // remove a linear element
9103 GetMeshDS()->RemoveFreeElement(elem, theSm, /*fromGroups=*/false);
9105 // remove central nodes of biquadratic elements (biquad->quad convertion)
9106 if ( hasCentralNodes )
9107 for ( size_t i = nbNodes * 2; i < nodes.size(); ++i )
9108 if ( nodes[i]->NbInverseElements() == 0 )
9109 GetMeshDS()->RemoveFreeNode( nodes[i], theSm, /*fromGroups=*/true );
9111 const SMDS_MeshElement* NewElem = 0;
9117 NewElem = theHelper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9125 NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
9128 NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9131 NewElem = theHelper.AddPolygonalFace(nodes, id, theForce3d);
9135 case SMDSAbs_Volume :
9139 case SMDSEntity_Tetra:
9140 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9142 case SMDSEntity_Pyramid:
9143 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], id, theForce3d);
9145 case SMDSEntity_Penta:
9146 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], nodes[5], id, theForce3d);
9148 case SMDSEntity_Hexa:
9149 case SMDSEntity_Quad_Hexa:
9150 case SMDSEntity_TriQuad_Hexa:
9151 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9152 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9154 case SMDSEntity_Hexagonal_Prism:
9156 NewElem = theHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9163 ReplaceElemInGroups( elem, NewElem, GetMeshDS());
9164 if( NewElem && NewElem->getshapeId() < 1 )
9165 theSm->AddElement( NewElem );
9169 //=======================================================================
9170 //function : ConvertToQuadratic
9172 //=======================================================================
9174 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d, const bool theToBiQuad)
9176 SMESHDS_Mesh* meshDS = GetMeshDS();
9178 SMESH_MesherHelper aHelper(*myMesh);
9180 aHelper.SetIsQuadratic( true );
9181 aHelper.SetIsBiQuadratic( theToBiQuad );
9182 aHelper.SetElementsOnShape(true);
9183 aHelper.ToFixNodeParameters( true );
9185 // convert elements assigned to sub-meshes
9186 int nbCheckedElems = 0;
9187 if ( myMesh->HasShapeToMesh() )
9189 if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9191 SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9192 while ( smIt->more() ) {
9193 SMESH_subMesh* sm = smIt->next();
9194 if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() ) {
9195 aHelper.SetSubShape( sm->GetSubShape() );
9196 nbCheckedElems += convertElemToQuadratic(smDS, aHelper, theForce3d);
9202 // convert elements NOT assigned to sub-meshes
9203 int totalNbElems = meshDS->NbEdges() + meshDS->NbFaces() + meshDS->NbVolumes();
9204 if ( nbCheckedElems < totalNbElems ) // not all elements are in sub-meshes
9206 aHelper.SetElementsOnShape(false);
9207 SMESHDS_SubMesh *smDS = 0;
9210 SMDS_EdgeIteratorPtr aEdgeItr = meshDS->edgesIterator();
9211 while( aEdgeItr->more() )
9213 const SMDS_MeshEdge* edge = aEdgeItr->next();
9214 if ( !edge->IsQuadratic() )
9216 int id = edge->GetID();
9217 const SMDS_MeshNode* n1 = edge->GetNode(0);
9218 const SMDS_MeshNode* n2 = edge->GetNode(1);
9220 meshDS->RemoveFreeElement(edge, smDS, /*fromGroups=*/false);
9222 const SMDS_MeshEdge* NewEdge = aHelper.AddEdge(n1, n2, id, theForce3d);
9223 ReplaceElemInGroups( edge, NewEdge, GetMeshDS());
9227 aHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( edge ));
9232 SMDS_FaceIteratorPtr aFaceItr = meshDS->facesIterator();
9233 while( aFaceItr->more() )
9235 const SMDS_MeshFace* face = aFaceItr->next();
9236 if ( !face ) continue;
9238 const SMDSAbs_EntityType type = face->GetEntityType();
9242 case SMDSEntity_Quad_Triangle:
9243 case SMDSEntity_Quad_Quadrangle:
9244 alreadyOK = !theToBiQuad;
9245 aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
9247 case SMDSEntity_BiQuad_Triangle:
9248 case SMDSEntity_BiQuad_Quadrangle:
9249 alreadyOK = theToBiQuad;
9250 aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
9252 default: alreadyOK = false;
9257 const int id = face->GetID();
9258 vector<const SMDS_MeshNode *> nodes ( face->begin_nodes(), face->end_nodes());
9260 meshDS->RemoveFreeElement(face, smDS, /*fromGroups=*/false);
9262 SMDS_MeshFace * NewFace = 0;
9265 case SMDSEntity_Triangle:
9266 case SMDSEntity_Quad_Triangle:
9267 case SMDSEntity_BiQuad_Triangle:
9268 NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
9269 if ( nodes.size() == 7 && nodes[6]->NbInverseElements() == 0 ) // rm a central node
9270 GetMeshDS()->RemoveFreeNode( nodes[6], /*sm=*/0, /*fromGroups=*/true );
9273 case SMDSEntity_Quadrangle:
9274 case SMDSEntity_Quad_Quadrangle:
9275 case SMDSEntity_BiQuad_Quadrangle:
9276 NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9277 if ( nodes.size() == 9 && nodes[8]->NbInverseElements() == 0 ) // rm a central node
9278 GetMeshDS()->RemoveFreeNode( nodes[8], /*sm=*/0, /*fromGroups=*/true );
9282 NewFace = aHelper.AddPolygonalFace(nodes, id, theForce3d);
9284 ReplaceElemInGroups( face, NewFace, GetMeshDS());
9288 vector<int> nbNodeInFaces;
9289 SMDS_VolumeIteratorPtr aVolumeItr = meshDS->volumesIterator();
9290 while(aVolumeItr->more())
9292 const SMDS_MeshVolume* volume = aVolumeItr->next();
9293 if ( !volume ) continue;
9295 const SMDSAbs_EntityType type = volume->GetEntityType();
9296 if ( volume->IsQuadratic() )
9301 case SMDSEntity_Quad_Hexa: alreadyOK = !theToBiQuad; break;
9302 case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; break;
9303 default: alreadyOK = true;
9307 aHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( volume ));
9311 const int id = volume->GetID();
9312 vector<const SMDS_MeshNode *> nodes (volume->begin_nodes(), volume->end_nodes());
9313 if ( type == SMDSEntity_Polyhedra )
9314 nbNodeInFaces = static_cast<const SMDS_VtkVolume* >(volume)->GetQuantities();
9315 else if ( type == SMDSEntity_Hexagonal_Prism )
9316 volumeToPolyhedron( volume, nodes, nbNodeInFaces );
9318 meshDS->RemoveFreeElement(volume, smDS, /*fromGroups=*/false);
9320 SMDS_MeshVolume * NewVolume = 0;
9323 case SMDSEntity_Tetra:
9324 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d );
9326 case SMDSEntity_Hexa:
9327 case SMDSEntity_Quad_Hexa:
9328 case SMDSEntity_TriQuad_Hexa:
9329 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9330 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9331 for ( size_t i = 20; i < nodes.size(); ++i ) // rm central nodes
9332 if ( nodes[i]->NbInverseElements() == 0 )
9333 GetMeshDS()->RemoveFreeNode( nodes[i], /*sm=*/0, /*fromGroups=*/true );
9335 case SMDSEntity_Pyramid:
9336 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9337 nodes[3], nodes[4], id, theForce3d);
9339 case SMDSEntity_Penta:
9340 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9341 nodes[3], nodes[4], nodes[5], id, theForce3d);
9343 case SMDSEntity_Hexagonal_Prism:
9345 NewVolume = aHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9347 ReplaceElemInGroups(volume, NewVolume, meshDS);
9352 { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9353 // aHelper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9354 // aHelper.FixQuadraticElements(myError);
9355 SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
9359 //================================================================================
9361 * \brief Makes given elements quadratic
9362 * \param theForce3d - if true, the medium nodes will be placed in the middle of link
9363 * \param theElements - elements to make quadratic
9365 //================================================================================
9367 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d,
9368 TIDSortedElemSet& theElements,
9369 const bool theToBiQuad)
9371 if ( theElements.empty() ) return;
9373 // we believe that all theElements are of the same type
9374 const SMDSAbs_ElementType elemType = (*theElements.begin())->GetType();
9376 // get all nodes shared by theElements
9377 TIDSortedNodeSet allNodes;
9378 TIDSortedElemSet::iterator eIt = theElements.begin();
9379 for ( ; eIt != theElements.end(); ++eIt )
9380 allNodes.insert( (*eIt)->begin_nodes(), (*eIt)->end_nodes() );
9382 // complete theElements with elements of lower dim whose all nodes are in allNodes
9384 TIDSortedElemSet quadAdjacentElems [ SMDSAbs_NbElementTypes ]; // quadratic adjacent elements
9385 TIDSortedElemSet checkedAdjacentElems [ SMDSAbs_NbElementTypes ];
9386 TIDSortedNodeSet::iterator nIt = allNodes.begin();
9387 for ( ; nIt != allNodes.end(); ++nIt )
9389 const SMDS_MeshNode* n = *nIt;
9390 SMDS_ElemIteratorPtr invIt = n->GetInverseElementIterator();
9391 while ( invIt->more() )
9393 const SMDS_MeshElement* e = invIt->next();
9394 const SMDSAbs_ElementType type = e->GetType();
9395 if ( e->IsQuadratic() )
9397 quadAdjacentElems[ type ].insert( e );
9400 switch ( e->GetEntityType() ) {
9401 case SMDSEntity_Quad_Triangle:
9402 case SMDSEntity_Quad_Quadrangle:
9403 case SMDSEntity_Quad_Hexa: alreadyOK = !theToBiQuad; break;
9404 case SMDSEntity_BiQuad_Triangle:
9405 case SMDSEntity_BiQuad_Quadrangle:
9406 case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; break;
9407 default: alreadyOK = true;
9412 if ( type >= elemType )
9413 continue; // same type or more complex linear element
9415 if ( !checkedAdjacentElems[ type ].insert( e ).second )
9416 continue; // e is already checked
9420 SMDS_NodeIteratorPtr nodeIt = e->nodeIterator();
9421 while ( nodeIt->more() && allIn )
9422 allIn = allNodes.count( nodeIt->next() );
9424 theElements.insert(e );
9428 SMESH_MesherHelper helper(*myMesh);
9429 helper.SetIsQuadratic( true );
9430 helper.SetIsBiQuadratic( theToBiQuad );
9432 // add links of quadratic adjacent elements to the helper
9434 if ( !quadAdjacentElems[SMDSAbs_Edge].empty() )
9435 for ( eIt = quadAdjacentElems[SMDSAbs_Edge].begin();
9436 eIt != quadAdjacentElems[SMDSAbs_Edge].end(); ++eIt )
9438 helper.AddTLinks( static_cast< const SMDS_MeshEdge*> (*eIt) );
9440 if ( !quadAdjacentElems[SMDSAbs_Face].empty() )
9441 for ( eIt = quadAdjacentElems[SMDSAbs_Face].begin();
9442 eIt != quadAdjacentElems[SMDSAbs_Face].end(); ++eIt )
9444 helper.AddTLinks( static_cast< const SMDS_MeshFace*> (*eIt) );
9446 if ( !quadAdjacentElems[SMDSAbs_Volume].empty() )
9447 for ( eIt = quadAdjacentElems[SMDSAbs_Volume].begin();
9448 eIt != quadAdjacentElems[SMDSAbs_Volume].end(); ++eIt )
9450 helper.AddTLinks( static_cast< const SMDS_MeshVolume*> (*eIt) );
9453 // make quadratic (or bi-tri-quadratic) elements instead of linear ones
9455 SMESHDS_Mesh* meshDS = GetMeshDS();
9456 SMESHDS_SubMesh* smDS = 0;
9457 for ( eIt = theElements.begin(); eIt != theElements.end(); ++eIt )
9459 const SMDS_MeshElement* elem = *eIt;
9462 int nbCentralNodes = 0;
9463 switch ( elem->GetEntityType() ) {
9464 // linear convertible
9465 case SMDSEntity_Edge:
9466 case SMDSEntity_Triangle:
9467 case SMDSEntity_Quadrangle:
9468 case SMDSEntity_Tetra:
9469 case SMDSEntity_Pyramid:
9470 case SMDSEntity_Hexa:
9471 case SMDSEntity_Penta: alreadyOK = false; nbCentralNodes = 0; break;
9472 // quadratic that can become bi-quadratic
9473 case SMDSEntity_Quad_Triangle:
9474 case SMDSEntity_Quad_Quadrangle:
9475 case SMDSEntity_Quad_Hexa: alreadyOK =!theToBiQuad; nbCentralNodes = 0; break;
9477 case SMDSEntity_BiQuad_Triangle:
9478 case SMDSEntity_BiQuad_Quadrangle: alreadyOK = theToBiQuad; nbCentralNodes = 1; break;
9479 case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; nbCentralNodes = 7; break;
9481 default: alreadyOK = true;
9483 if ( alreadyOK ) continue;
9485 const SMDSAbs_ElementType type = elem->GetType();
9486 const int id = elem->GetID();
9487 const int nbNodes = elem->NbCornerNodes();
9488 vector<const SMDS_MeshNode *> nodes ( elem->begin_nodes(), elem->end_nodes());
9490 helper.SetSubShape( elem->getshapeId() );
9492 if ( !smDS || !smDS->Contains( elem ))
9493 smDS = meshDS->MeshElements( elem->getshapeId() );
9494 meshDS->RemoveFreeElement(elem, smDS, /*fromGroups=*/false);
9496 SMDS_MeshElement * newElem = 0;
9499 case 4: // cases for most frequently used element types go first (for optimization)
9500 if ( type == SMDSAbs_Volume )
9501 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9503 newElem = helper.AddFace (nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9506 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9507 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9510 newElem = helper.AddFace (nodes[0], nodes[1], nodes[2], id, theForce3d);
9513 newElem = helper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9516 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9517 nodes[4], id, theForce3d);
9520 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9521 nodes[4], nodes[5], id, theForce3d);
9525 ReplaceElemInGroups( elem, newElem, meshDS);
9526 if( newElem && smDS )
9527 smDS->AddElement( newElem );
9529 // remove central nodes
9530 for ( size_t i = nodes.size() - nbCentralNodes; i < nodes.size(); ++i )
9531 if ( nodes[i]->NbInverseElements() == 0 )
9532 meshDS->RemoveFreeNode( nodes[i], smDS, /*fromGroups=*/true );
9534 } // loop on theElements
9537 { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9538 // helper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9539 // helper.FixQuadraticElements( myError );
9540 SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
9544 //=======================================================================
9546 * \brief Convert quadratic elements to linear ones and remove quadratic nodes
9547 * \return int - nb of checked elements
9549 //=======================================================================
9551 int SMESH_MeshEditor::removeQuadElem(SMESHDS_SubMesh * theSm,
9552 SMDS_ElemIteratorPtr theItr,
9553 const int theShapeID)
9556 SMESHDS_Mesh* meshDS = GetMeshDS();
9557 ElemFeatures elemType;
9558 vector<const SMDS_MeshNode *> nodes;
9560 while( theItr->more() )
9562 const SMDS_MeshElement* elem = theItr->next();
9564 if( elem && elem->IsQuadratic())
9567 int nbCornerNodes = elem->NbCornerNodes();
9568 nodes.assign( elem->begin_nodes(), elem->end_nodes() );
9570 elemType.Init( elem, /*basicOnly=*/false ).SetID( elem->GetID() ).SetQuad( false );
9572 //remove a quadratic element
9573 if ( !theSm || !theSm->Contains( elem ))
9574 theSm = meshDS->MeshElements( elem->getshapeId() );
9575 meshDS->RemoveFreeElement( elem, theSm, /*fromGroups=*/false );
9577 // remove medium nodes
9578 for ( size_t i = nbCornerNodes; i < nodes.size(); ++i )
9579 if ( nodes[i]->NbInverseElements() == 0 )
9580 meshDS->RemoveFreeNode( nodes[i], theSm );
9582 // add a linear element
9583 nodes.resize( nbCornerNodes );
9584 SMDS_MeshElement * newElem = AddElement( nodes, elemType );
9585 ReplaceElemInGroups(elem, newElem, meshDS);
9586 if( theSm && newElem )
9587 theSm->AddElement( newElem );
9593 //=======================================================================
9594 //function : ConvertFromQuadratic
9596 //=======================================================================
9598 bool SMESH_MeshEditor::ConvertFromQuadratic()
9600 int nbCheckedElems = 0;
9601 if ( myMesh->HasShapeToMesh() )
9603 if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9605 SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9606 while ( smIt->more() ) {
9607 SMESH_subMesh* sm = smIt->next();
9608 if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() )
9609 nbCheckedElems += removeQuadElem( smDS, smDS->GetElements(), sm->GetId() );
9615 GetMeshDS()->NbEdges() + GetMeshDS()->NbFaces() + GetMeshDS()->NbVolumes();
9616 if ( nbCheckedElems < totalNbElems ) // not all elements are in submeshes
9618 SMESHDS_SubMesh *aSM = 0;
9619 removeQuadElem( aSM, GetMeshDS()->elementsIterator(), 0 );
9627 //================================================================================
9629 * \brief Return true if all medium nodes of the element are in the node set
9631 //================================================================================
9633 bool allMediumNodesIn(const SMDS_MeshElement* elem, TIDSortedNodeSet& nodeSet )
9635 for ( int i = elem->NbCornerNodes(); i < elem->NbNodes(); ++i )
9636 if ( !nodeSet.count( elem->GetNode(i) ))
9642 //================================================================================
9644 * \brief Makes given elements linear
9646 //================================================================================
9648 void SMESH_MeshEditor::ConvertFromQuadratic(TIDSortedElemSet& theElements)
9650 if ( theElements.empty() ) return;
9652 // collect IDs of medium nodes of theElements; some of these nodes will be removed
9653 set<int> mediumNodeIDs;
9654 TIDSortedElemSet::iterator eIt = theElements.begin();
9655 for ( ; eIt != theElements.end(); ++eIt )
9657 const SMDS_MeshElement* e = *eIt;
9658 for ( int i = e->NbCornerNodes(); i < e->NbNodes(); ++i )
9659 mediumNodeIDs.insert( e->GetNode(i)->GetID() );
9662 // replace given elements by linear ones
9663 SMDS_ElemIteratorPtr elemIt = elemSetIterator( theElements );
9664 removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9666 // we need to convert remaining elements whose all medium nodes are in mediumNodeIDs
9667 // except those elements sharing medium nodes of quadratic element whose medium nodes
9668 // are not all in mediumNodeIDs
9670 // get remaining medium nodes
9671 TIDSortedNodeSet mediumNodes;
9672 set<int>::iterator nIdsIt = mediumNodeIDs.begin();
9673 for ( ; nIdsIt != mediumNodeIDs.end(); ++nIdsIt )
9674 if ( const SMDS_MeshNode* n = GetMeshDS()->FindNode( *nIdsIt ))
9675 mediumNodes.insert( mediumNodes.end(), n );
9677 // find more quadratic elements to convert
9678 TIDSortedElemSet moreElemsToConvert;
9679 TIDSortedNodeSet::iterator nIt = mediumNodes.begin();
9680 for ( ; nIt != mediumNodes.end(); ++nIt )
9682 SMDS_ElemIteratorPtr invIt = (*nIt)->GetInverseElementIterator();
9683 while ( invIt->more() )
9685 const SMDS_MeshElement* e = invIt->next();
9686 if ( e->IsQuadratic() && allMediumNodesIn( e, mediumNodes ))
9688 // find a more complex element including e and
9689 // whose medium nodes are not in mediumNodes
9690 bool complexFound = false;
9691 for ( int type = e->GetType() + 1; type < SMDSAbs_0DElement; ++type )
9693 SMDS_ElemIteratorPtr invIt2 =
9694 (*nIt)->GetInverseElementIterator( SMDSAbs_ElementType( type ));
9695 while ( invIt2->more() )
9697 const SMDS_MeshElement* eComplex = invIt2->next();
9698 if ( eComplex->IsQuadratic() && !allMediumNodesIn( eComplex, mediumNodes))
9700 int nbCommonNodes = SMESH_MeshAlgos::GetCommonNodes( e, eComplex ).size();
9701 if ( nbCommonNodes == e->NbNodes())
9703 complexFound = true;
9704 type = SMDSAbs_NbElementTypes; // to quit from the outer loop
9710 if ( !complexFound )
9711 moreElemsToConvert.insert( e );
9715 elemIt = elemSetIterator( moreElemsToConvert );
9716 removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9719 //=======================================================================
9720 //function : SewSideElements
9722 //=======================================================================
9724 SMESH_MeshEditor::Sew_Error
9725 SMESH_MeshEditor::SewSideElements (TIDSortedElemSet& theSide1,
9726 TIDSortedElemSet& theSide2,
9727 const SMDS_MeshNode* theFirstNode1,
9728 const SMDS_MeshNode* theFirstNode2,
9729 const SMDS_MeshNode* theSecondNode1,
9730 const SMDS_MeshNode* theSecondNode2)
9732 myLastCreatedElems.Clear();
9733 myLastCreatedNodes.Clear();
9735 if ( theSide1.size() != theSide2.size() )
9736 return SEW_DIFF_NB_OF_ELEMENTS;
9738 Sew_Error aResult = SEW_OK;
9740 // 1. Build set of faces representing each side
9741 // 2. Find which nodes of the side 1 to merge with ones on the side 2
9742 // 3. Replace nodes in elements of the side 1 and remove replaced nodes
9744 // =======================================================================
9745 // 1. Build set of faces representing each side:
9746 // =======================================================================
9747 // a. build set of nodes belonging to faces
9748 // b. complete set of faces: find missing faces whose nodes are in set of nodes
9749 // c. create temporary faces representing side of volumes if correspondent
9750 // face does not exist
9752 SMESHDS_Mesh* aMesh = GetMeshDS();
9753 // TODO algoritm not OK with vtkUnstructuredGrid: 2 meshes can't share nodes
9754 //SMDS_Mesh aTmpFacesMesh; // try to use the same mesh
9755 TIDSortedElemSet faceSet1, faceSet2;
9756 set<const SMDS_MeshElement*> volSet1, volSet2;
9757 set<const SMDS_MeshNode*> nodeSet1, nodeSet2;
9758 TIDSortedElemSet * faceSetPtr[] = { &faceSet1, &faceSet2 };
9759 set<const SMDS_MeshElement*> * volSetPtr[] = { &volSet1, &volSet2 };
9760 set<const SMDS_MeshNode*> * nodeSetPtr[] = { &nodeSet1, &nodeSet2 };
9761 TIDSortedElemSet * elemSetPtr[] = { &theSide1, &theSide2 };
9762 int iSide, iFace, iNode;
9764 list<const SMDS_MeshElement* > tempFaceList;
9765 for ( iSide = 0; iSide < 2; iSide++ ) {
9766 set<const SMDS_MeshNode*> * nodeSet = nodeSetPtr[ iSide ];
9767 TIDSortedElemSet * elemSet = elemSetPtr[ iSide ];
9768 TIDSortedElemSet * faceSet = faceSetPtr[ iSide ];
9769 set<const SMDS_MeshElement*> * volSet = volSetPtr [ iSide ];
9770 set<const SMDS_MeshElement*>::iterator vIt;
9771 TIDSortedElemSet::iterator eIt;
9772 set<const SMDS_MeshNode*>::iterator nIt;
9774 // check that given nodes belong to given elements
9775 const SMDS_MeshNode* n1 = ( iSide == 0 ) ? theFirstNode1 : theFirstNode2;
9776 const SMDS_MeshNode* n2 = ( iSide == 0 ) ? theSecondNode1 : theSecondNode2;
9777 int firstIndex = -1, secondIndex = -1;
9778 for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9779 const SMDS_MeshElement* elem = *eIt;
9780 if ( firstIndex < 0 ) firstIndex = elem->GetNodeIndex( n1 );
9781 if ( secondIndex < 0 ) secondIndex = elem->GetNodeIndex( n2 );
9782 if ( firstIndex > -1 && secondIndex > -1 ) break;
9784 if ( firstIndex < 0 || secondIndex < 0 ) {
9785 // we can simply return until temporary faces created
9786 return (iSide == 0 ) ? SEW_BAD_SIDE1_NODES : SEW_BAD_SIDE2_NODES;
9789 // -----------------------------------------------------------
9790 // 1a. Collect nodes of existing faces
9791 // and build set of face nodes in order to detect missing
9792 // faces corresponding to sides of volumes
9793 // -----------------------------------------------------------
9795 set< set <const SMDS_MeshNode*> > setOfFaceNodeSet;
9797 // loop on the given element of a side
9798 for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9799 //const SMDS_MeshElement* elem = *eIt;
9800 const SMDS_MeshElement* elem = *eIt;
9801 if ( elem->GetType() == SMDSAbs_Face ) {
9802 faceSet->insert( elem );
9803 set <const SMDS_MeshNode*> faceNodeSet;
9804 SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
9805 while ( nodeIt->more() ) {
9806 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9807 nodeSet->insert( n );
9808 faceNodeSet.insert( n );
9810 setOfFaceNodeSet.insert( faceNodeSet );
9812 else if ( elem->GetType() == SMDSAbs_Volume )
9813 volSet->insert( elem );
9815 // ------------------------------------------------------------------------------
9816 // 1b. Complete set of faces: find missing faces whose nodes are in set of nodes
9817 // ------------------------------------------------------------------------------
9819 for ( nIt = nodeSet->begin(); nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
9820 SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
9821 while ( fIt->more() ) { // loop on faces sharing a node
9822 const SMDS_MeshElement* f = fIt->next();
9823 if ( faceSet->find( f ) == faceSet->end() ) {
9824 // check if all nodes are in nodeSet and
9825 // complete setOfFaceNodeSet if they are
9826 set <const SMDS_MeshNode*> faceNodeSet;
9827 SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
9828 bool allInSet = true;
9829 while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
9830 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9831 if ( nodeSet->find( n ) == nodeSet->end() )
9834 faceNodeSet.insert( n );
9837 faceSet->insert( f );
9838 setOfFaceNodeSet.insert( faceNodeSet );
9844 // -------------------------------------------------------------------------
9845 // 1c. Create temporary faces representing sides of volumes if correspondent
9846 // face does not exist
9847 // -------------------------------------------------------------------------
9849 if ( !volSet->empty() ) {
9850 //int nodeSetSize = nodeSet->size();
9852 // loop on given volumes
9853 for ( vIt = volSet->begin(); vIt != volSet->end(); vIt++ ) {
9854 SMDS_VolumeTool vol (*vIt);
9855 // loop on volume faces: find free faces
9856 // --------------------------------------
9857 list<const SMDS_MeshElement* > freeFaceList;
9858 for ( iFace = 0; iFace < vol.NbFaces(); iFace++ ) {
9859 if ( !vol.IsFreeFace( iFace ))
9861 // check if there is already a face with same nodes in a face set
9862 const SMDS_MeshElement* aFreeFace = 0;
9863 const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iFace );
9864 int nbNodes = vol.NbFaceNodes( iFace );
9865 set <const SMDS_MeshNode*> faceNodeSet;
9866 vol.GetFaceNodes( iFace, faceNodeSet );
9867 bool isNewFace = setOfFaceNodeSet.insert( faceNodeSet ).second;
9869 // no such a face is given but it still can exist, check it
9870 vector<const SMDS_MeshNode *> nodes ( fNodes, fNodes + nbNodes);
9871 aFreeFace = aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false );
9874 // create a temporary face
9875 if ( nbNodes == 3 ) {
9876 //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2] );
9877 aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2] );
9879 else if ( nbNodes == 4 ) {
9880 //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
9881 aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
9884 vector<const SMDS_MeshNode *> poly_nodes ( fNodes, & fNodes[nbNodes]);
9885 //aFreeFace = aTmpFacesMesh.AddPolygonalFace(poly_nodes);
9886 aFreeFace = aMesh->AddPolygonalFace(poly_nodes);
9889 tempFaceList.push_back( aFreeFace );
9893 freeFaceList.push_back( aFreeFace );
9895 } // loop on faces of a volume
9897 // choose one of several free faces of a volume
9898 // --------------------------------------------
9899 if ( freeFaceList.size() > 1 ) {
9900 // choose a face having max nb of nodes shared by other elems of a side
9901 int maxNbNodes = -1;
9902 list<const SMDS_MeshElement* >::iterator fIt = freeFaceList.begin();
9903 while ( fIt != freeFaceList.end() ) { // loop on free faces
9904 int nbSharedNodes = 0;
9905 SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
9906 while ( nodeIt->more() ) { // loop on free face nodes
9907 const SMDS_MeshNode* n =
9908 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9909 SMDS_ElemIteratorPtr invElemIt = n->GetInverseElementIterator();
9910 while ( invElemIt->more() ) {
9911 const SMDS_MeshElement* e = invElemIt->next();
9912 nbSharedNodes += faceSet->count( e );
9913 nbSharedNodes += elemSet->count( e );
9916 if ( nbSharedNodes > maxNbNodes ) {
9917 maxNbNodes = nbSharedNodes;
9918 freeFaceList.erase( freeFaceList.begin(), fIt++ );
9920 else if ( nbSharedNodes == maxNbNodes ) {
9924 freeFaceList.erase( fIt++ ); // here fIt++ occurs before erase
9927 if ( freeFaceList.size() > 1 )
9929 // could not choose one face, use another way
9930 // choose a face most close to the bary center of the opposite side
9931 gp_XYZ aBC( 0., 0., 0. );
9932 set <const SMDS_MeshNode*> addedNodes;
9933 TIDSortedElemSet * elemSet2 = elemSetPtr[ 1 - iSide ];
9934 eIt = elemSet2->begin();
9935 for ( eIt = elemSet2->begin(); eIt != elemSet2->end(); eIt++ ) {
9936 SMDS_ElemIteratorPtr nodeIt = (*eIt)->nodesIterator();
9937 while ( nodeIt->more() ) { // loop on free face nodes
9938 const SMDS_MeshNode* n =
9939 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9940 if ( addedNodes.insert( n ).second )
9941 aBC += gp_XYZ( n->X(),n->Y(),n->Z() );
9944 aBC /= addedNodes.size();
9945 double minDist = DBL_MAX;
9946 fIt = freeFaceList.begin();
9947 while ( fIt != freeFaceList.end() ) { // loop on free faces
9949 SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
9950 while ( nodeIt->more() ) { // loop on free face nodes
9951 const SMDS_MeshNode* n =
9952 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9953 gp_XYZ p( n->X(),n->Y(),n->Z() );
9954 dist += ( aBC - p ).SquareModulus();
9956 if ( dist < minDist ) {
9958 freeFaceList.erase( freeFaceList.begin(), fIt++ );
9961 fIt = freeFaceList.erase( fIt++ );
9964 } // choose one of several free faces of a volume
9966 if ( freeFaceList.size() == 1 ) {
9967 const SMDS_MeshElement* aFreeFace = freeFaceList.front();
9968 faceSet->insert( aFreeFace );
9969 // complete a node set with nodes of a found free face
9970 // for ( iNode = 0; iNode < ; iNode++ )
9971 // nodeSet->insert( fNodes[ iNode ] );
9974 } // loop on volumes of a side
9976 // // complete a set of faces if new nodes in a nodeSet appeared
9977 // // ----------------------------------------------------------
9978 // if ( nodeSetSize != nodeSet->size() ) {
9979 // for ( ; nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
9980 // SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
9981 // while ( fIt->more() ) { // loop on faces sharing a node
9982 // const SMDS_MeshElement* f = fIt->next();
9983 // if ( faceSet->find( f ) == faceSet->end() ) {
9984 // // check if all nodes are in nodeSet and
9985 // // complete setOfFaceNodeSet if they are
9986 // set <const SMDS_MeshNode*> faceNodeSet;
9987 // SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
9988 // bool allInSet = true;
9989 // while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
9990 // const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9991 // if ( nodeSet->find( n ) == nodeSet->end() )
9992 // allInSet = false;
9994 // faceNodeSet.insert( n );
9996 // if ( allInSet ) {
9997 // faceSet->insert( f );
9998 // setOfFaceNodeSet.insert( faceNodeSet );
10004 } // Create temporary faces, if there are volumes given
10007 if ( faceSet1.size() != faceSet2.size() ) {
10008 // delete temporary faces: they are in reverseElements of actual nodes
10009 // SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
10010 // while ( tmpFaceIt->more() )
10011 // aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
10012 // list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
10013 // for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
10014 // aMesh->RemoveElement(*tmpFaceIt);
10015 MESSAGE("Diff nb of faces");
10016 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10019 // ============================================================
10020 // 2. Find nodes to merge:
10021 // bind a node to remove to a node to put instead
10022 // ============================================================
10024 TNodeNodeMap nReplaceMap; // bind a node to remove to a node to put instead
10025 if ( theFirstNode1 != theFirstNode2 )
10026 nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
10027 if ( theSecondNode1 != theSecondNode2 )
10028 nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
10030 LinkID_Gen aLinkID_Gen( GetMeshDS() );
10031 set< long > linkIdSet; // links to process
10032 linkIdSet.insert( aLinkID_Gen.GetLinkID( theFirstNode1, theSecondNode1 ));
10034 typedef pair< const SMDS_MeshNode*, const SMDS_MeshNode* > NLink;
10035 list< NLink > linkList[2];
10036 linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
10037 linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
10038 // loop on links in linkList; find faces by links and append links
10039 // of the found faces to linkList
10040 list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
10041 for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ )
10043 NLink link[] = { *linkIt[0], *linkIt[1] };
10044 long linkID = aLinkID_Gen.GetLinkID( link[0].first, link[0].second );
10045 if ( !linkIdSet.count( linkID ) )
10048 // by links, find faces in the face sets,
10049 // and find indices of link nodes in the found faces;
10050 // in a face set, there is only one or no face sharing a link
10051 // ---------------------------------------------------------------
10053 const SMDS_MeshElement* face[] = { 0, 0 };
10054 vector<const SMDS_MeshNode*> fnodes[2];
10055 int iLinkNode[2][2];
10056 TIDSortedElemSet avoidSet;
10057 for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
10058 const SMDS_MeshNode* n1 = link[iSide].first;
10059 const SMDS_MeshNode* n2 = link[iSide].second;
10060 //cout << "Side " << iSide << " ";
10061 //cout << "L( " << n1->GetID() << ", " << n2->GetID() << " ) " << endl;
10062 // find a face by two link nodes
10063 face[ iSide ] = SMESH_MeshAlgos::FindFaceInSet( n1, n2,
10064 *faceSetPtr[ iSide ], avoidSet,
10065 &iLinkNode[iSide][0],
10066 &iLinkNode[iSide][1] );
10067 if ( face[ iSide ])
10069 //cout << " F " << face[ iSide]->GetID() <<endl;
10070 faceSetPtr[ iSide ]->erase( face[ iSide ]);
10071 // put face nodes to fnodes
10072 if ( face[ iSide ]->IsQuadratic() )
10074 // use interlaced nodes iterator
10075 const SMDS_VtkFace* F = dynamic_cast<const SMDS_VtkFace*>( face[ iSide ]);
10076 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
10077 SMDS_ElemIteratorPtr nIter = F->interlacedNodesElemIterator();
10078 while ( nIter->more() )
10079 fnodes[ iSide ].push_back( cast2Node( nIter->next() ));
10083 fnodes[ iSide ].assign( face[ iSide ]->begin_nodes(),
10084 face[ iSide ]->end_nodes() );
10086 fnodes[ iSide ].push_back( fnodes[ iSide ].front());
10090 // check similarity of elements of the sides
10091 if (aResult == SEW_OK && (( face[0] && !face[1] ) || ( !face[0] && face[1] ))) {
10092 MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
10093 if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
10094 aResult = ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10097 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10099 break; // do not return because it's necessary to remove tmp faces
10102 // set nodes to merge
10103 // -------------------
10105 if ( face[0] && face[1] ) {
10106 const int nbNodes = face[0]->NbNodes();
10107 if ( nbNodes != face[1]->NbNodes() ) {
10108 MESSAGE("Diff nb of face nodes");
10109 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10110 break; // do not return because it s necessary to remove tmp faces
10112 bool reverse[] = { false, false }; // order of nodes in the link
10113 for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
10114 // analyse link orientation in faces
10115 int i1 = iLinkNode[ iSide ][ 0 ];
10116 int i2 = iLinkNode[ iSide ][ 1 ];
10117 reverse[ iSide ] = Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1;
10119 int di1 = reverse[0] ? -1 : +1, i1 = iLinkNode[0][1] + di1;
10120 int di2 = reverse[1] ? -1 : +1, i2 = iLinkNode[1][1] + di2;
10121 for ( int i = nbNodes - 2; i > 0; --i, i1 += di1, i2 += di2 )
10123 nReplaceMap.insert ( make_pair ( fnodes[0][ ( i1 + nbNodes ) % nbNodes ],
10124 fnodes[1][ ( i2 + nbNodes ) % nbNodes ]));
10127 // add other links of the faces to linkList
10128 // -----------------------------------------
10130 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
10131 linkID = aLinkID_Gen.GetLinkID( fnodes[0][iNode], fnodes[0][iNode+1] );
10132 pair< set<long>::iterator, bool > iter_isnew = linkIdSet.insert( linkID );
10133 if ( !iter_isnew.second ) { // already in a set: no need to process
10134 linkIdSet.erase( iter_isnew.first );
10136 else // new in set == encountered for the first time: add
10138 const SMDS_MeshNode* n1 = fnodes[0][ iNode ];
10139 const SMDS_MeshNode* n2 = fnodes[0][ iNode + 1];
10140 linkList[0].push_back ( NLink( n1, n2 ));
10141 linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10146 if ( faceSetPtr[0]->empty() || faceSetPtr[1]->empty() )
10149 } // loop on link lists
10151 if ( aResult == SEW_OK &&
10152 ( //linkIt[0] != linkList[0].end() ||
10153 !faceSetPtr[0]->empty() || !faceSetPtr[1]->empty() )) {
10154 MESSAGE( (linkIt[0] != linkList[0].end()) <<" "<< (faceSetPtr[0]->empty()) <<
10155 " " << (faceSetPtr[1]->empty()));
10156 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10159 // ====================================================================
10160 // 3. Replace nodes in elements of the side 1 and remove replaced nodes
10161 // ====================================================================
10163 // delete temporary faces
10164 // SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
10165 // while ( tmpFaceIt->more() )
10166 // aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
10167 list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
10168 for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
10169 aMesh->RemoveElement(*tmpFaceIt);
10171 if ( aResult != SEW_OK)
10174 list< int > nodeIDsToRemove;
10175 vector< const SMDS_MeshNode*> nodes;
10176 ElemFeatures elemType;
10178 // loop on nodes replacement map
10179 TNodeNodeMap::iterator nReplaceMapIt = nReplaceMap.begin(), nnIt;
10180 for ( ; nReplaceMapIt != nReplaceMap.end(); nReplaceMapIt++ )
10181 if ( (*nReplaceMapIt).first != (*nReplaceMapIt).second )
10183 const SMDS_MeshNode* nToRemove = (*nReplaceMapIt).first;
10184 nodeIDsToRemove.push_back( nToRemove->GetID() );
10185 // loop on elements sharing nToRemove
10186 SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
10187 while ( invElemIt->more() ) {
10188 const SMDS_MeshElement* e = invElemIt->next();
10189 // get a new suite of nodes: make replacement
10190 int nbReplaced = 0, i = 0, nbNodes = e->NbNodes();
10191 nodes.resize( nbNodes );
10192 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
10193 while ( nIt->more() ) {
10194 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nIt->next() );
10195 nnIt = nReplaceMap.find( n );
10196 if ( nnIt != nReplaceMap.end() ) {
10198 n = (*nnIt).second;
10202 // if ( nbReplaced == nbNodes && e->GetType() == SMDSAbs_Face )
10203 // elemIDsToRemove.push_back( e->GetID() );
10207 elemType.Init( e, /*basicOnly=*/false ).SetID( e->GetID() );
10208 aMesh->RemoveElement( e );
10210 if ( SMDS_MeshElement* newElem = this->AddElement( nodes, elemType ))
10212 AddToSameGroups( newElem, e, aMesh );
10213 if ( int aShapeId = e->getshapeId() )
10214 aMesh->SetMeshElementOnShape( newElem, aShapeId );
10220 Remove( nodeIDsToRemove, true );
10225 //================================================================================
10227 * \brief Find corresponding nodes in two sets of faces
10228 * \param theSide1 - first face set
10229 * \param theSide2 - second first face
10230 * \param theFirstNode1 - a boundary node of set 1
10231 * \param theFirstNode2 - a node of set 2 corresponding to theFirstNode1
10232 * \param theSecondNode1 - a boundary node of set 1 linked with theFirstNode1
10233 * \param theSecondNode2 - a node of set 2 corresponding to theSecondNode1
10234 * \param nReplaceMap - output map of corresponding nodes
10235 * \return bool - is a success or not
10237 //================================================================================
10240 //#define DEBUG_MATCHING_NODES
10243 SMESH_MeshEditor::Sew_Error
10244 SMESH_MeshEditor::FindMatchingNodes(set<const SMDS_MeshElement*>& theSide1,
10245 set<const SMDS_MeshElement*>& theSide2,
10246 const SMDS_MeshNode* theFirstNode1,
10247 const SMDS_MeshNode* theFirstNode2,
10248 const SMDS_MeshNode* theSecondNode1,
10249 const SMDS_MeshNode* theSecondNode2,
10250 TNodeNodeMap & nReplaceMap)
10252 set<const SMDS_MeshElement*> * faceSetPtr[] = { &theSide1, &theSide2 };
10254 nReplaceMap.clear();
10255 if ( theFirstNode1 != theFirstNode2 )
10256 nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
10257 if ( theSecondNode1 != theSecondNode2 )
10258 nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
10260 set< SMESH_TLink > linkSet; // set of nodes where order of nodes is ignored
10261 linkSet.insert( SMESH_TLink( theFirstNode1, theSecondNode1 ));
10263 list< NLink > linkList[2];
10264 linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
10265 linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
10267 // loop on links in linkList; find faces by links and append links
10268 // of the found faces to linkList
10269 list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
10270 for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ ) {
10271 NLink link[] = { *linkIt[0], *linkIt[1] };
10272 if ( linkSet.find( link[0] ) == linkSet.end() )
10275 // by links, find faces in the face sets,
10276 // and find indices of link nodes in the found faces;
10277 // in a face set, there is only one or no face sharing a link
10278 // ---------------------------------------------------------------
10280 const SMDS_MeshElement* face[] = { 0, 0 };
10281 list<const SMDS_MeshNode*> notLinkNodes[2];
10282 //bool reverse[] = { false, false }; // order of notLinkNodes
10284 for ( int iSide = 0; iSide < 2; iSide++ ) // loop on 2 sides
10286 const SMDS_MeshNode* n1 = link[iSide].first;
10287 const SMDS_MeshNode* n2 = link[iSide].second;
10288 set<const SMDS_MeshElement*> * faceSet = faceSetPtr[ iSide ];
10289 set< const SMDS_MeshElement* > facesOfNode1;
10290 for ( int iNode = 0; iNode < 2; iNode++ ) // loop on 2 nodes of a link
10292 // during a loop of the first node, we find all faces around n1,
10293 // during a loop of the second node, we find one face sharing both n1 and n2
10294 const SMDS_MeshNode* n = iNode ? n1 : n2; // a node of a link
10295 SMDS_ElemIteratorPtr fIt = n->GetInverseElementIterator(SMDSAbs_Face);
10296 while ( fIt->more() ) { // loop on faces sharing a node
10297 const SMDS_MeshElement* f = fIt->next();
10298 if (faceSet->find( f ) != faceSet->end() && // f is in face set
10299 ! facesOfNode1.insert( f ).second ) // f encounters twice
10301 if ( face[ iSide ] ) {
10302 MESSAGE( "2 faces per link " );
10303 return ( iSide ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10306 faceSet->erase( f );
10308 // get not link nodes
10309 int nbN = f->NbNodes();
10310 if ( f->IsQuadratic() )
10312 nbNodes[ iSide ] = nbN;
10313 list< const SMDS_MeshNode* > & nodes = notLinkNodes[ iSide ];
10314 int i1 = f->GetNodeIndex( n1 );
10315 int i2 = f->GetNodeIndex( n2 );
10316 int iEnd = nbN, iBeg = -1, iDelta = 1;
10317 bool reverse = ( Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1 );
10319 std::swap( iEnd, iBeg ); iDelta = -1;
10324 if ( i == iEnd ) i = iBeg + iDelta;
10325 if ( i == i1 ) break;
10326 nodes.push_back ( f->GetNode( i ) );
10332 // check similarity of elements of the sides
10333 if (( face[0] && !face[1] ) || ( !face[0] && face[1] )) {
10334 MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
10335 if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
10336 return ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10339 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10343 // set nodes to merge
10344 // -------------------
10346 if ( face[0] && face[1] ) {
10347 if ( nbNodes[0] != nbNodes[1] ) {
10348 MESSAGE("Diff nb of face nodes");
10349 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10351 #ifdef DEBUG_MATCHING_NODES
10352 MESSAGE ( " Link 1: " << link[0].first->GetID() <<" "<< link[0].second->GetID()
10353 << " F 1: " << face[0] << "| Link 2: " << link[1].first->GetID() <<" "
10354 << link[1].second->GetID() << " F 2: " << face[1] << " | Bind: " ) ;
10356 int nbN = nbNodes[0];
10358 list<const SMDS_MeshNode*>::iterator n1 = notLinkNodes[0].begin();
10359 list<const SMDS_MeshNode*>::iterator n2 = notLinkNodes[1].begin();
10360 for ( int i = 0 ; i < nbN - 2; ++i ) {
10361 #ifdef DEBUG_MATCHING_NODES
10362 MESSAGE ( (*n1)->GetID() << " to " << (*n2)->GetID() );
10364 nReplaceMap.insert( make_pair( *(n1++), *(n2++) ));
10368 // add other links of the face 1 to linkList
10369 // -----------------------------------------
10371 const SMDS_MeshElement* f0 = face[0];
10372 const SMDS_MeshNode* n1 = f0->GetNode( nbN - 1 );
10373 for ( int i = 0; i < nbN; i++ )
10375 const SMDS_MeshNode* n2 = f0->GetNode( i );
10376 pair< set< SMESH_TLink >::iterator, bool > iter_isnew =
10377 linkSet.insert( SMESH_TLink( n1, n2 ));
10378 if ( !iter_isnew.second ) { // already in a set: no need to process
10379 linkSet.erase( iter_isnew.first );
10381 else // new in set == encountered for the first time: add
10383 #ifdef DEBUG_MATCHING_NODES
10384 MESSAGE ( "Add link 1: " << n1->GetID() << " " << n2->GetID() << " "
10385 << " | link 2: " << nReplaceMap[n1]->GetID() << " " << nReplaceMap[n2]->GetID() << " " );
10387 linkList[0].push_back ( NLink( n1, n2 ));
10388 linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10393 } // loop on link lists
10398 //================================================================================
10400 * \brief Create elements equal (on same nodes) to given ones
10401 * \param [in] theElements - a set of elems to duplicate. If it is empty, all
10402 * elements of the uppest dimension are duplicated.
10404 //================================================================================
10406 void SMESH_MeshEditor::DoubleElements( const TIDSortedElemSet& theElements )
10408 ClearLastCreated();
10409 SMESHDS_Mesh* mesh = GetMeshDS();
10411 // get an element type and an iterator over elements
10413 SMDSAbs_ElementType type = SMDSAbs_All;
10414 SMDS_ElemIteratorPtr elemIt;
10415 vector< const SMDS_MeshElement* > allElems;
10416 if ( theElements.empty() )
10418 if ( mesh->NbNodes() == 0 )
10420 // get most complex type
10421 SMDSAbs_ElementType types[SMDSAbs_NbElementTypes] = {
10422 SMDSAbs_Volume, SMDSAbs_Face, SMDSAbs_Edge,
10423 SMDSAbs_0DElement, SMDSAbs_Ball, SMDSAbs_Node
10425 for ( int i = 0; i < SMDSAbs_NbElementTypes; ++i )
10426 if ( mesh->GetMeshInfo().NbElements( types[i] ))
10431 // put all elements in the vector <allElems>
10432 allElems.reserve( mesh->GetMeshInfo().NbElements( type ));
10433 elemIt = mesh->elementsIterator( type );
10434 while ( elemIt->more() )
10435 allElems.push_back( elemIt->next());
10436 elemIt = elemSetIterator( allElems );
10440 type = (*theElements.begin())->GetType();
10441 elemIt = elemSetIterator( theElements );
10444 // duplicate elements
10446 ElemFeatures elemType;
10448 vector< const SMDS_MeshNode* > nodes;
10449 while ( elemIt->more() )
10451 const SMDS_MeshElement* elem = elemIt->next();
10452 if ( elem->GetType() != type )
10455 elemType.Init( elem, /*basicOnly=*/false );
10456 nodes.assign( elem->begin_nodes(), elem->end_nodes() );
10458 AddElement( nodes, elemType );
10462 //================================================================================
10464 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10465 \param theElems - the list of elements (edges or faces) to be replicated
10466 The nodes for duplication could be found from these elements
10467 \param theNodesNot - list of nodes to NOT replicate
10468 \param theAffectedElems - the list of elements (cells and edges) to which the
10469 replicated nodes should be associated to.
10470 \return TRUE if operation has been completed successfully, FALSE otherwise
10472 //================================================================================
10474 bool SMESH_MeshEditor::DoubleNodes( const TIDSortedElemSet& theElems,
10475 const TIDSortedElemSet& theNodesNot,
10476 const TIDSortedElemSet& theAffectedElems )
10478 myLastCreatedElems.Clear();
10479 myLastCreatedNodes.Clear();
10481 if ( theElems.size() == 0 )
10484 SMESHDS_Mesh* aMeshDS = GetMeshDS();
10489 TNodeNodeMap anOldNodeToNewNode;
10490 // duplicate elements and nodes
10491 res = doubleNodes( aMeshDS, theElems, theNodesNot, anOldNodeToNewNode, true );
10492 // replce nodes by duplications
10493 res = doubleNodes( aMeshDS, theAffectedElems, theNodesNot, anOldNodeToNewNode, false );
10497 //================================================================================
10499 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10500 \param theMeshDS - mesh instance
10501 \param theElems - the elements replicated or modified (nodes should be changed)
10502 \param theNodesNot - nodes to NOT replicate
10503 \param theNodeNodeMap - relation of old node to new created node
10504 \param theIsDoubleElem - flag os to replicate element or modify
10505 \return TRUE if operation has been completed successfully, FALSE otherwise
10507 //================================================================================
10509 bool SMESH_MeshEditor::doubleNodes(SMESHDS_Mesh* theMeshDS,
10510 const TIDSortedElemSet& theElems,
10511 const TIDSortedElemSet& theNodesNot,
10512 TNodeNodeMap& theNodeNodeMap,
10513 const bool theIsDoubleElem )
10515 // iterate through element and duplicate them (by nodes duplication)
10517 std::vector<const SMDS_MeshNode*> newNodes;
10518 ElemFeatures elemType;
10520 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10521 for ( ; elemItr != theElems.end(); ++elemItr )
10523 const SMDS_MeshElement* anElem = *elemItr;
10527 // duplicate nodes to duplicate element
10528 bool isDuplicate = false;
10529 newNodes.resize( anElem->NbNodes() );
10530 SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
10532 while ( anIter->more() )
10534 const SMDS_MeshNode* aCurrNode = static_cast<const SMDS_MeshNode*>( anIter->next() );
10535 const SMDS_MeshNode* aNewNode = aCurrNode;
10536 TNodeNodeMap::iterator n2n = theNodeNodeMap.find( aCurrNode );
10537 if ( n2n != theNodeNodeMap.end() )
10539 aNewNode = n2n->second;
10541 else if ( theIsDoubleElem && !theNodesNot.count( aCurrNode ))
10544 aNewNode = theMeshDS->AddNode( aCurrNode->X(), aCurrNode->Y(), aCurrNode->Z() );
10545 copyPosition( aCurrNode, aNewNode );
10546 theNodeNodeMap[ aCurrNode ] = aNewNode;
10547 myLastCreatedNodes.Append( aNewNode );
10549 isDuplicate |= (aCurrNode != aNewNode);
10550 newNodes[ ind++ ] = aNewNode;
10552 if ( !isDuplicate )
10555 if ( theIsDoubleElem )
10556 AddElement( newNodes, elemType.Init( anElem, /*basicOnly=*/false ));
10558 theMeshDS->ChangeElementNodes( anElem, &newNodes[ 0 ], newNodes.size() );
10565 //================================================================================
10567 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10568 \param theNodes - identifiers of nodes to be doubled
10569 \param theModifiedElems - identifiers of elements to be updated by the new (doubled)
10570 nodes. If list of element identifiers is empty then nodes are doubled but
10571 they not assigned to elements
10572 \return TRUE if operation has been completed successfully, FALSE otherwise
10574 //================================================================================
10576 bool SMESH_MeshEditor::DoubleNodes( const std::list< int >& theListOfNodes,
10577 const std::list< int >& theListOfModifiedElems )
10579 myLastCreatedElems.Clear();
10580 myLastCreatedNodes.Clear();
10582 if ( theListOfNodes.size() == 0 )
10585 SMESHDS_Mesh* aMeshDS = GetMeshDS();
10589 // iterate through nodes and duplicate them
10591 std::map< const SMDS_MeshNode*, const SMDS_MeshNode* > anOldNodeToNewNode;
10593 std::list< int >::const_iterator aNodeIter;
10594 for ( aNodeIter = theListOfNodes.begin(); aNodeIter != theListOfNodes.end(); ++aNodeIter )
10596 int aCurr = *aNodeIter;
10597 SMDS_MeshNode* aNode = (SMDS_MeshNode*)aMeshDS->FindNode( aCurr );
10603 const SMDS_MeshNode* aNewNode = aMeshDS->AddNode( aNode->X(), aNode->Y(), aNode->Z() );
10606 copyPosition( aNode, aNewNode );
10607 anOldNodeToNewNode[ aNode ] = aNewNode;
10608 myLastCreatedNodes.Append( aNewNode );
10612 // Create map of new nodes for modified elements
10614 std::map< SMDS_MeshElement*, vector<const SMDS_MeshNode*> > anElemToNodes;
10616 std::list< int >::const_iterator anElemIter;
10617 for ( anElemIter = theListOfModifiedElems.begin();
10618 anElemIter != theListOfModifiedElems.end(); ++anElemIter )
10620 int aCurr = *anElemIter;
10621 SMDS_MeshElement* anElem = (SMDS_MeshElement*)aMeshDS->FindElement( aCurr );
10625 vector<const SMDS_MeshNode*> aNodeArr( anElem->NbNodes() );
10627 SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
10629 while ( anIter->more() )
10631 SMDS_MeshNode* aCurrNode = (SMDS_MeshNode*)anIter->next();
10632 if ( aCurr && anOldNodeToNewNode.find( aCurrNode ) != anOldNodeToNewNode.end() )
10634 const SMDS_MeshNode* aNewNode = anOldNodeToNewNode[ aCurrNode ];
10635 aNodeArr[ ind++ ] = aNewNode;
10638 aNodeArr[ ind++ ] = aCurrNode;
10640 anElemToNodes[ anElem ] = aNodeArr;
10643 // Change nodes of elements
10645 std::map< SMDS_MeshElement*, vector<const SMDS_MeshNode*> >::iterator
10646 anElemToNodesIter = anElemToNodes.begin();
10647 for ( ; anElemToNodesIter != anElemToNodes.end(); ++anElemToNodesIter )
10649 const SMDS_MeshElement* anElem = anElemToNodesIter->first;
10650 vector<const SMDS_MeshNode*> aNodeArr = anElemToNodesIter->second;
10653 aMeshDS->ChangeElementNodes( anElem, &aNodeArr[ 0 ], anElem->NbNodes() );
10662 //================================================================================
10664 \brief Check if element located inside shape
10665 \return TRUE if IN or ON shape, FALSE otherwise
10667 //================================================================================
10669 template<class Classifier>
10670 bool isInside(const SMDS_MeshElement* theElem,
10671 Classifier& theClassifier,
10672 const double theTol)
10674 gp_XYZ centerXYZ (0, 0, 0);
10675 SMDS_ElemIteratorPtr aNodeItr = theElem->nodesIterator();
10676 while (aNodeItr->more())
10677 centerXYZ += SMESH_TNodeXYZ(cast2Node( aNodeItr->next()));
10679 gp_Pnt aPnt = centerXYZ / theElem->NbNodes();
10680 theClassifier.Perform(aPnt, theTol);
10681 TopAbs_State aState = theClassifier.State();
10682 return (aState == TopAbs_IN || aState == TopAbs_ON );
10685 //================================================================================
10687 * \brief Classifier of the 3D point on the TopoDS_Face
10688 * with interaface suitable for isInside()
10690 //================================================================================
10692 struct _FaceClassifier
10694 Extrema_ExtPS _extremum;
10695 BRepAdaptor_Surface _surface;
10696 TopAbs_State _state;
10698 _FaceClassifier(const TopoDS_Face& face):_extremum(),_surface(face),_state(TopAbs_OUT)
10700 _extremum.Initialize( _surface,
10701 _surface.FirstUParameter(), _surface.LastUParameter(),
10702 _surface.FirstVParameter(), _surface.LastVParameter(),
10703 _surface.Tolerance(), _surface.Tolerance() );
10705 void Perform(const gp_Pnt& aPnt, double theTol)
10708 _state = TopAbs_OUT;
10709 _extremum.Perform(aPnt);
10710 if ( _extremum.IsDone() )
10711 for ( int iSol = 1; iSol <= _extremum.NbExt() && _state == TopAbs_OUT; ++iSol)
10712 _state = ( _extremum.SquareDistance(iSol) <= theTol ? TopAbs_IN : TopAbs_OUT );
10714 TopAbs_State State() const
10721 //================================================================================
10723 \brief Identify the elements that will be affected by node duplication (actual duplication is not performed).
10724 This method is the first step of DoubleNodeElemGroupsInRegion.
10725 \param theElems - list of groups of elements (edges or faces) to be replicated
10726 \param theNodesNot - list of groups of nodes not to replicated
10727 \param theShape - shape to detect affected elements (element which geometric center
10728 located on or inside shape). If the shape is null, detection is done on faces orientations
10729 (select elements with a gravity center on the side given by faces normals).
10730 This mode (null shape) is faster, but works only when theElems are faces, with coherents orientations.
10731 The replicated nodes should be associated to affected elements.
10732 \return groups of affected elements
10733 \sa DoubleNodeElemGroupsInRegion()
10735 //================================================================================
10737 bool SMESH_MeshEditor::AffectedElemGroupsInRegion( const TIDSortedElemSet& theElems,
10738 const TIDSortedElemSet& theNodesNot,
10739 const TopoDS_Shape& theShape,
10740 TIDSortedElemSet& theAffectedElems)
10742 if ( theShape.IsNull() )
10744 std::set<const SMDS_MeshNode*> alreadyCheckedNodes;
10745 std::set<const SMDS_MeshElement*> alreadyCheckedElems;
10746 std::set<const SMDS_MeshElement*> edgesToCheck;
10747 alreadyCheckedNodes.clear();
10748 alreadyCheckedElems.clear();
10749 edgesToCheck.clear();
10751 // --- iterates on elements to be replicated and get elements by back references from their nodes
10753 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10755 for ( ielem=1; elemItr != theElems.end(); ++elemItr )
10757 SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
10758 if (!anElem || (anElem->GetType() != SMDSAbs_Face))
10761 SMESH_MeshAlgos::FaceNormal( anElem, normal, /*normalized=*/true );
10762 std::set<const SMDS_MeshNode*> nodesElem;
10764 SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
10765 while ( nodeItr->more() )
10767 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10768 nodesElem.insert(aNode);
10770 std::set<const SMDS_MeshNode*>::iterator nodit = nodesElem.begin();
10771 for (; nodit != nodesElem.end(); nodit++)
10773 const SMDS_MeshNode* aNode = *nodit;
10774 if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
10776 if (alreadyCheckedNodes.find(aNode) != alreadyCheckedNodes.end())
10778 alreadyCheckedNodes.insert(aNode);
10779 SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
10780 while ( backElemItr->more() )
10782 const SMDS_MeshElement* curElem = backElemItr->next();
10783 if (alreadyCheckedElems.find(curElem) != alreadyCheckedElems.end())
10785 if (theElems.find(curElem) != theElems.end())
10787 alreadyCheckedElems.insert(curElem);
10788 double x=0, y=0, z=0;
10790 SMDS_ElemIteratorPtr nodeItr2 = curElem->nodesIterator();
10791 while ( nodeItr2->more() )
10793 const SMDS_MeshNode* anotherNode = cast2Node(nodeItr2->next());
10794 x += anotherNode->X();
10795 y += anotherNode->Y();
10796 z += anotherNode->Z();
10800 p.SetCoord( x/nb -aNode->X(),
10802 z/nb -aNode->Z() );
10805 theAffectedElems.insert( curElem );
10807 else if (curElem->GetType() == SMDSAbs_Edge)
10808 edgesToCheck.insert(curElem);
10812 // --- add also edges lying on the set of faces (all nodes in alreadyCheckedNodes)
10813 std::set<const SMDS_MeshElement*>::iterator eit = edgesToCheck.begin();
10814 for( ; eit != edgesToCheck.end(); eit++)
10816 bool onside = true;
10817 const SMDS_MeshElement* anEdge = *eit;
10818 SMDS_ElemIteratorPtr nodeItr = anEdge->nodesIterator();
10819 while ( nodeItr->more() )
10821 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10822 if (alreadyCheckedNodes.find(aNode) == alreadyCheckedNodes.end())
10830 theAffectedElems.insert(anEdge);
10836 const double aTol = Precision::Confusion();
10837 auto_ptr< BRepClass3d_SolidClassifier> bsc3d;
10838 auto_ptr<_FaceClassifier> aFaceClassifier;
10839 if ( theShape.ShapeType() == TopAbs_SOLID )
10841 bsc3d.reset( new BRepClass3d_SolidClassifier(theShape));;
10842 bsc3d->PerformInfinitePoint(aTol);
10844 else if (theShape.ShapeType() == TopAbs_FACE )
10846 aFaceClassifier.reset( new _FaceClassifier(TopoDS::Face(theShape)));
10849 // iterates on indicated elements and get elements by back references from their nodes
10850 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10852 for ( ielem = 1; elemItr != theElems.end(); ++elemItr )
10854 SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
10857 SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
10858 while ( nodeItr->more() )
10860 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10861 if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
10863 SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
10864 while ( backElemItr->more() )
10866 const SMDS_MeshElement* curElem = backElemItr->next();
10867 if ( curElem && theElems.find(curElem) == theElems.end() &&
10869 isInside( curElem, *bsc3d, aTol ) :
10870 isInside( curElem, *aFaceClassifier, aTol )))
10871 theAffectedElems.insert( curElem );
10879 //================================================================================
10881 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10882 \param theElems - group of of elements (edges or faces) to be replicated
10883 \param theNodesNot - group of nodes not to replicate
10884 \param theShape - shape to detect affected elements (element which geometric center
10885 located on or inside shape).
10886 The replicated nodes should be associated to affected elements.
10887 \return TRUE if operation has been completed successfully, FALSE otherwise
10889 //================================================================================
10891 bool SMESH_MeshEditor::DoubleNodesInRegion( const TIDSortedElemSet& theElems,
10892 const TIDSortedElemSet& theNodesNot,
10893 const TopoDS_Shape& theShape )
10895 if ( theShape.IsNull() )
10898 const double aTol = Precision::Confusion();
10899 SMESHUtils::Deleter< BRepClass3d_SolidClassifier> bsc3d;
10900 SMESHUtils::Deleter<_FaceClassifier> aFaceClassifier;
10901 if ( theShape.ShapeType() == TopAbs_SOLID )
10903 bsc3d._obj = new BRepClass3d_SolidClassifier( theShape );
10904 bsc3d->PerformInfinitePoint(aTol);
10906 else if (theShape.ShapeType() == TopAbs_FACE )
10908 aFaceClassifier._obj = new _FaceClassifier( TopoDS::Face( theShape ));
10911 // iterates on indicated elements and get elements by back references from their nodes
10912 TIDSortedElemSet anAffected;
10913 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10914 for ( ; elemItr != theElems.end(); ++elemItr )
10916 SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
10920 SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
10921 while ( nodeItr->more() )
10923 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10924 if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
10926 SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
10927 while ( backElemItr->more() )
10929 const SMDS_MeshElement* curElem = backElemItr->next();
10930 if ( curElem && theElems.find(curElem) == theElems.end() &&
10932 isInside( curElem, *bsc3d, aTol ) :
10933 isInside( curElem, *aFaceClassifier, aTol )))
10934 anAffected.insert( curElem );
10938 return DoubleNodes( theElems, theNodesNot, anAffected );
10942 * \brief compute an oriented angle between two planes defined by four points.
10943 * The vector (p0,p1) defines the intersection of the 2 planes (p0,p1,g1) and (p0,p1,g2)
10944 * @param p0 base of the rotation axe
10945 * @param p1 extremity of the rotation axe
10946 * @param g1 belongs to the first plane
10947 * @param g2 belongs to the second plane
10949 double SMESH_MeshEditor::OrientedAngle(const gp_Pnt& p0, const gp_Pnt& p1, const gp_Pnt& g1, const gp_Pnt& g2)
10951 gp_Vec vref(p0, p1);
10954 gp_Vec n1 = vref.Crossed(v1);
10955 gp_Vec n2 = vref.Crossed(v2);
10957 return n2.AngleWithRef(n1, vref);
10959 catch ( Standard_Failure ) {
10961 return Max( v1.Magnitude(), v2.Magnitude() );
10965 * \brief Double nodes on shared faces between groups of volumes and create flat elements on demand.
10966 * The list of groups must contain at least two groups. The groups have to be disjoint: no common element into two different groups.
10967 * The nodes of the internal faces at the boundaries of the groups are doubled. Optionally, the internal faces are replaced by flat elements.
10968 * Triangles are transformed into prisms, and quadrangles into hexahedrons.
10969 * The flat elements are stored in groups of volumes. These groups are named according to the position of the group in the list:
10970 * 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.
10971 * 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.
10972 * All the flat elements are gathered into the group named "joints3D" (or "joints2D" in 2D situation).
10973 * The flat element of the multiple junctions between the simple junction are stored in a group named "jointsMultiples".
10974 * \param theElems - list of groups of volumes, where a group of volume is a set of
10975 * SMDS_MeshElements sorted by Id.
10976 * \param createJointElems - if TRUE, create the elements
10977 * \param onAllBoundaries - if TRUE, the nodes and elements are also created on
10978 * the boundary between \a theDomains and the rest mesh
10979 * \return TRUE if operation has been completed successfully, FALSE otherwise
10981 bool SMESH_MeshEditor::DoubleNodesOnGroupBoundaries( const std::vector<TIDSortedElemSet>& theElems,
10982 bool createJointElems,
10983 bool onAllBoundaries)
10985 MESSAGE("----------------------------------------------");
10986 MESSAGE("SMESH_MeshEditor::doubleNodesOnGroupBoundaries");
10987 MESSAGE("----------------------------------------------");
10989 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
10990 meshDS->BuildDownWardConnectivity(true);
10992 SMDS_UnstructuredGrid *grid = meshDS->getGrid();
10994 // --- build the list of faces shared by 2 domains (group of elements), with their domain and volume indexes
10995 // build the list of cells with only a node or an edge on the border, with their domain and volume indexes
10996 // build the list of nodes shared by 2 or more domains, with their domain indexes
10998 std::map<DownIdType, std::map<int,int>, DownIdCompare> faceDomains; // face --> (id domain --> id volume)
10999 std::map<int,int>celldom; // cell vtkId --> domain
11000 std::map<DownIdType, std::map<int,int>, DownIdCompare> cellDomains; // oldNode --> (id domain --> id cell)
11001 std::map<int, std::map<int,int> > nodeDomains; // oldId --> (domainId --> newId)
11002 faceDomains.clear();
11004 cellDomains.clear();
11005 nodeDomains.clear();
11006 std::map<int,int> emptyMap;
11007 std::set<int> emptySet;
11010 MESSAGE(".. Number of domains :"<<theElems.size());
11012 TIDSortedElemSet theRestDomElems;
11013 const int iRestDom = -1;
11014 const int idom0 = onAllBoundaries ? iRestDom : 0;
11015 const int nbDomains = theElems.size();
11017 // Check if the domains do not share an element
11018 for (int idom = 0; idom < nbDomains-1; idom++)
11020 // MESSAGE("... Check of domain #" << idom);
11021 const TIDSortedElemSet& domain = theElems[idom];
11022 TIDSortedElemSet::const_iterator elemItr = domain.begin();
11023 for (; elemItr != domain.end(); ++elemItr)
11025 const SMDS_MeshElement* anElem = *elemItr;
11026 int idombisdeb = idom + 1 ;
11027 // check if the element belongs to a domain further in the list
11028 for ( size_t idombis = idombisdeb; idombis < theElems.size(); idombis++ )
11030 const TIDSortedElemSet& domainbis = theElems[idombis];
11031 if ( domainbis.count( anElem ))
11033 MESSAGE(".... Domain #" << idom);
11034 MESSAGE(".... Domain #" << idombis);
11035 throw SALOME_Exception("The domains are not disjoint.");
11042 for (int idom = 0; idom < nbDomains; idom++)
11045 // --- build a map (face to duplicate --> volume to modify)
11046 // with all the faces shared by 2 domains (group of elements)
11047 // and corresponding volume of this domain, for each shared face.
11048 // a volume has a face shared by 2 domains if it has a neighbor which is not in his domain.
11050 MESSAGE("... Neighbors of domain #" << idom);
11051 const TIDSortedElemSet& domain = theElems[idom];
11052 TIDSortedElemSet::const_iterator elemItr = domain.begin();
11053 for (; elemItr != domain.end(); ++elemItr)
11055 const SMDS_MeshElement* anElem = *elemItr;
11058 int vtkId = anElem->getVtkId();
11059 //MESSAGE(" vtkId " << vtkId << " smdsId " << anElem->GetID());
11060 int neighborsVtkIds[NBMAXNEIGHBORS];
11061 int downIds[NBMAXNEIGHBORS];
11062 unsigned char downTypes[NBMAXNEIGHBORS];
11063 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
11064 for (int n = 0; n < nbNeighbors; n++)
11066 int smdsId = meshDS->fromVtkToSmds(neighborsVtkIds[n]);
11067 const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
11068 if (elem && ! domain.count(elem)) // neighbor is in another domain : face is shared
11071 for ( size_t idombis = 0; idombis < theElems.size() && !ok; idombis++) // check if the neighbor belongs to another domain of the list
11073 // MESSAGE("Domain " << idombis);
11074 const TIDSortedElemSet& domainbis = theElems[idombis];
11075 if ( domainbis.count(elem)) ok = true ; // neighbor is in a correct domain : face is kept
11077 if ( ok || onAllBoundaries ) // the characteristics of the face is stored
11079 DownIdType face(downIds[n], downTypes[n]);
11080 if (!faceDomains[face].count(idom))
11082 faceDomains[face][idom] = vtkId; // volume associated to face in this domain
11083 celldom[vtkId] = idom;
11084 //MESSAGE(" cell with a border " << vtkId << " domain " << idom);
11088 theRestDomElems.insert( elem );
11089 faceDomains[face][iRestDom] = neighborsVtkIds[n];
11090 celldom[neighborsVtkIds[n]] = iRestDom;
11098 //MESSAGE("Number of shared faces " << faceDomains.size());
11099 std::map<DownIdType, std::map<int, int>, DownIdCompare>::iterator itface;
11101 // --- explore the shared faces domain by domain,
11102 // explore the nodes of the face and see if they belong to a cell in the domain,
11103 // which has only a node or an edge on the border (not a shared face)
11105 for (int idomain = idom0; idomain < nbDomains; idomain++)
11107 //MESSAGE("Domain " << idomain);
11108 const TIDSortedElemSet& domain = (idomain == iRestDom) ? theRestDomElems : theElems[idomain];
11109 itface = faceDomains.begin();
11110 for (; itface != faceDomains.end(); ++itface)
11112 const std::map<int, int>& domvol = itface->second;
11113 if (!domvol.count(idomain))
11115 DownIdType face = itface->first;
11116 //MESSAGE(" --- face " << face.cellId);
11117 std::set<int> oldNodes;
11119 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11120 std::set<int>::iterator itn = oldNodes.begin();
11121 for (; itn != oldNodes.end(); ++itn)
11124 //MESSAGE(" node " << oldId);
11125 vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
11126 for (int i=0; i<l.ncells; i++)
11128 int vtkId = l.cells[i];
11129 const SMDS_MeshElement* anElem = GetMeshDS()->FindElement(GetMeshDS()->fromVtkToSmds(vtkId));
11130 if (!domain.count(anElem))
11132 int vtkType = grid->GetCellType(vtkId);
11133 int downId = grid->CellIdToDownId(vtkId);
11136 MESSAGE("doubleNodesOnGroupBoundaries: internal algorithm problem");
11137 continue; // not OK at this stage of the algorithm:
11138 //no cells created after BuildDownWardConnectivity
11140 DownIdType aCell(downId, vtkType);
11141 cellDomains[aCell][idomain] = vtkId;
11142 celldom[vtkId] = idomain;
11143 //MESSAGE(" cell " << vtkId << " domain " << idomain);
11149 // --- explore the shared faces domain by domain, to duplicate the nodes in a coherent way
11150 // for each shared face, get the nodes
11151 // for each node, for each domain of the face, create a clone of the node
11153 // --- edges at the intersection of 3 or 4 domains, with the order of domains to build
11154 // junction elements of type prism or hexa. the key is the pair of nodesId (lower first)
11155 // the value is the ordered domain ids. (more than 4 domains not taken into account)
11157 std::map<std::vector<int>, std::vector<int> > edgesMultiDomains; // nodes of edge --> ordered domains
11158 std::map<int, std::vector<int> > mutipleNodes; // nodes multi domains with domain order
11159 std::map<int, std::vector<int> > mutipleNodesToFace; // nodes multi domains with domain order to transform in Face (junction between 3 or more 2D domains)
11161 MESSAGE(".. Duplication of the nodes");
11162 for (int idomain = idom0; idomain < nbDomains; idomain++)
11164 itface = faceDomains.begin();
11165 for (; itface != faceDomains.end(); ++itface)
11167 const std::map<int, int>& domvol = itface->second;
11168 if (!domvol.count(idomain))
11170 DownIdType face = itface->first;
11171 //MESSAGE(" --- face " << face.cellId);
11172 std::set<int> oldNodes;
11174 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11175 std::set<int>::iterator itn = oldNodes.begin();
11176 for (; itn != oldNodes.end(); ++itn)
11179 if (nodeDomains[oldId].empty())
11181 nodeDomains[oldId][idomain] = oldId; // keep the old node in the first domain
11182 //MESSAGE("-+-+-b oldNode " << oldId << " domain " << idomain);
11184 std::map<int, int>::const_iterator itdom = domvol.begin();
11185 for (; itdom != domvol.end(); ++itdom)
11187 int idom = itdom->first;
11188 //MESSAGE(" domain " << idom);
11189 if (!nodeDomains[oldId].count(idom)) // --- node to clone
11191 if (nodeDomains[oldId].size() >= 2) // a multiple node
11193 vector<int> orderedDoms;
11194 //MESSAGE("multiple node " << oldId);
11195 if (mutipleNodes.count(oldId))
11196 orderedDoms = mutipleNodes[oldId];
11199 map<int,int>::iterator it = nodeDomains[oldId].begin();
11200 for (; it != nodeDomains[oldId].end(); ++it)
11201 orderedDoms.push_back(it->first);
11203 orderedDoms.push_back(idom); // TODO order ==> push_front or back
11204 //stringstream txt;
11205 //for (int i=0; i<orderedDoms.size(); i++)
11206 // txt << orderedDoms[i] << " ";
11207 //MESSAGE("orderedDoms " << txt.str());
11208 mutipleNodes[oldId] = orderedDoms;
11210 double *coords = grid->GetPoint(oldId);
11211 SMDS_MeshNode *newNode = meshDS->AddNode(coords[0], coords[1], coords[2]);
11212 copyPosition( meshDS->FindNodeVtk( oldId ), newNode );
11213 int newId = newNode->getVtkId();
11214 nodeDomains[oldId][idom] = newId; // cloned node for other domains
11215 //MESSAGE("-+-+-c oldNode " << oldId << " domain " << idomain << " newNode " << newId << " domain " << idom << " size=" <<nodeDomains[oldId].size());
11222 MESSAGE(".. Creation of elements");
11223 for (int idomain = idom0; idomain < nbDomains; idomain++)
11225 itface = faceDomains.begin();
11226 for (; itface != faceDomains.end(); ++itface)
11228 std::map<int, int> domvol = itface->second;
11229 if (!domvol.count(idomain))
11231 DownIdType face = itface->first;
11232 //MESSAGE(" --- face " << face.cellId);
11233 std::set<int> oldNodes;
11235 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11236 int nbMultipleNodes = 0;
11237 std::set<int>::iterator itn = oldNodes.begin();
11238 for (; itn != oldNodes.end(); ++itn)
11241 if (mutipleNodes.count(oldId))
11244 if (nbMultipleNodes > 1) // check if an edge of the face is shared between 3 or more domains
11246 //MESSAGE("multiple Nodes detected on a shared face");
11247 int downId = itface->first.cellId;
11248 unsigned char cellType = itface->first.cellType;
11249 // --- shared edge or shared face ?
11250 if ((cellType == VTK_LINE) || (cellType == VTK_QUADRATIC_EDGE)) // shared edge (between two faces)
11253 int nbNodes = grid->getDownArray(cellType)->getNodes(downId, nodes);
11254 for (int i=0; i< nbNodes; i=i+nbNodes-1) // i=0 , i=nbNodes-1
11255 if (mutipleNodes.count(nodes[i]))
11256 if (!mutipleNodesToFace.count(nodes[i]))
11257 mutipleNodesToFace[nodes[i]] = mutipleNodes[nodes[i]];
11259 else // shared face (between two volumes)
11261 int nbEdges = grid->getDownArray(cellType)->getNumberOfDownCells(downId);
11262 const int* downEdgeIds = grid->getDownArray(cellType)->getDownCells(downId);
11263 const unsigned char* edgeType = grid->getDownArray(cellType)->getDownTypes(downId);
11264 for (int ie =0; ie < nbEdges; ie++)
11267 int nbNodes = grid->getDownArray(edgeType[ie])->getNodes(downEdgeIds[ie], nodes);
11268 if ( mutipleNodes.count(nodes[0]) && mutipleNodes.count( nodes[ nbNodes-1 ]))
11270 vector<int> vn0 = mutipleNodes[nodes[0]];
11271 vector<int> vn1 = mutipleNodes[nodes[nbNodes - 1]];
11273 for ( size_t i0 = 0; i0 < vn0.size(); i0++ )
11274 for ( size_t i1 = 0; i1 < vn1.size(); i1++ )
11275 if ( vn0[i0] == vn1[i1] )
11276 doms.push_back( vn0[ i0 ]);
11277 if ( doms.size() > 2 )
11279 //MESSAGE(" detect edgesMultiDomains " << nodes[0] << " " << nodes[nbNodes - 1]);
11280 double *coords = grid->GetPoint(nodes[0]);
11281 gp_Pnt p0(coords[0], coords[1], coords[2]);
11282 coords = grid->GetPoint(nodes[nbNodes - 1]);
11283 gp_Pnt p1(coords[0], coords[1], coords[2]);
11285 int vtkVolIds[1000]; // an edge can belong to a lot of volumes
11286 map<int, SMDS_VtkVolume*> domvol; // domain --> a volume with the edge
11287 map<int, double> angleDom; // oriented angles between planes defined by edge and volume centers
11288 int nbvol = grid->GetParentVolumes(vtkVolIds, downEdgeIds[ie], edgeType[ie]);
11289 for ( size_t id = 0; id < doms.size(); id++ )
11291 int idom = doms[id];
11292 const TIDSortedElemSet& domain = (idom == iRestDom) ? theRestDomElems : theElems[idom];
11293 for ( int ivol = 0; ivol < nbvol; ivol++ )
11295 int smdsId = meshDS->fromVtkToSmds(vtkVolIds[ivol]);
11296 SMDS_MeshElement* elem = (SMDS_MeshElement*)meshDS->FindElement(smdsId);
11297 if (domain.count(elem))
11299 SMDS_VtkVolume* svol = dynamic_cast<SMDS_VtkVolume*>(elem);
11300 domvol[idom] = svol;
11301 //MESSAGE(" domain " << idom << " volume " << elem->GetID());
11303 vtkIdType npts = 0;
11304 vtkIdType* pts = 0;
11305 grid->GetCellPoints(vtkVolIds[ivol], npts, pts);
11306 SMDS_VtkVolume::gravityCenter(grid, pts, npts, values);
11309 gref.SetXYZ(gp_XYZ(values[0], values[1], values[2]));
11310 angleDom[idom] = 0;
11314 gp_Pnt g(values[0], values[1], values[2]);
11315 angleDom[idom] = OrientedAngle(p0, p1, gref, g); // -pi<angle<+pi
11316 //MESSAGE(" angle=" << angleDom[idom]);
11322 map<double, int> sortedDom; // sort domains by angle
11323 for (map<int, double>::iterator ia = angleDom.begin(); ia != angleDom.end(); ++ia)
11324 sortedDom[ia->second] = ia->first;
11325 vector<int> vnodes;
11327 for (map<double, int>::iterator ib = sortedDom.begin(); ib != sortedDom.end(); ++ib)
11329 vdom.push_back(ib->second);
11330 //MESSAGE(" ordered domain " << ib->second << " angle " << ib->first);
11332 for (int ino = 0; ino < nbNodes; ino++)
11333 vnodes.push_back(nodes[ino]);
11334 edgesMultiDomains[vnodes] = vdom; // nodes vector --> ordered domains
11343 // --- iterate on shared faces (volumes to modify, face to extrude)
11344 // get node id's of the face (id SMDS = id VTK)
11345 // create flat element with old and new nodes if requested
11347 // --- new quad nodes on flat quad elements: oldId --> ((domain1 X domain2) --> newId)
11348 // (domain1 X domain2) = domain1 + MAXINT*domain2
11350 std::map<int, std::map<long,int> > nodeQuadDomains;
11351 std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11353 MESSAGE(".. Creation of elements: simple junction");
11354 if (createJointElems)
11357 string joints2DName = "joints2D";
11358 mapOfJunctionGroups[joints2DName] = this->myMesh->AddGroup(SMDSAbs_Face, joints2DName.c_str(), idg);
11359 SMESHDS_Group *joints2DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints2DName]->GetGroupDS());
11360 string joints3DName = "joints3D";
11361 mapOfJunctionGroups[joints3DName] = this->myMesh->AddGroup(SMDSAbs_Volume, joints3DName.c_str(), idg);
11362 SMESHDS_Group *joints3DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints3DName]->GetGroupDS());
11364 itface = faceDomains.begin();
11365 for (; itface != faceDomains.end(); ++itface)
11367 DownIdType face = itface->first;
11368 std::set<int> oldNodes;
11369 std::set<int>::iterator itn;
11371 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11373 std::map<int, int> domvol = itface->second;
11374 std::map<int, int>::iterator itdom = domvol.begin();
11375 int dom1 = itdom->first;
11376 int vtkVolId = itdom->second;
11378 int dom2 = itdom->first;
11379 SMDS_MeshCell *vol = grid->extrudeVolumeFromFace(vtkVolId, dom1, dom2, oldNodes, nodeDomains,
11381 stringstream grpname;
11384 grpname << dom1 << "_" << dom2;
11386 grpname << dom2 << "_" << dom1;
11387 string namegrp = grpname.str();
11388 if (!mapOfJunctionGroups.count(namegrp))
11389 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(vol->GetType(), namegrp.c_str(), idg);
11390 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11392 sgrp->Add(vol->GetID());
11393 if (vol->GetType() == SMDSAbs_Volume)
11394 joints3DGrp->Add(vol->GetID());
11395 else if (vol->GetType() == SMDSAbs_Face)
11396 joints2DGrp->Add(vol->GetID());
11400 // --- create volumes on multiple domain intersection if requested
11401 // iterate on mutipleNodesToFace
11402 // iterate on edgesMultiDomains
11404 MESSAGE(".. Creation of elements: multiple junction");
11405 if (createJointElems)
11407 // --- iterate on mutipleNodesToFace
11409 std::map<int, std::vector<int> >::iterator itn = mutipleNodesToFace.begin();
11410 for (; itn != mutipleNodesToFace.end(); ++itn)
11412 int node = itn->first;
11413 vector<int> orderDom = itn->second;
11414 vector<vtkIdType> orderedNodes;
11415 for ( size_t idom = 0; idom < orderDom.size(); idom++ )
11416 orderedNodes.push_back( nodeDomains[ node ][ orderDom[ idom ]]);
11417 SMDS_MeshFace* face = this->GetMeshDS()->AddFaceFromVtkIds(orderedNodes);
11419 stringstream grpname;
11421 grpname << 0 << "_" << 0;
11423 string namegrp = grpname.str();
11424 if (!mapOfJunctionGroups.count(namegrp))
11425 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Face, namegrp.c_str(), idg);
11426 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11428 sgrp->Add(face->GetID());
11431 // --- iterate on edgesMultiDomains
11433 std::map<std::vector<int>, std::vector<int> >::iterator ite = edgesMultiDomains.begin();
11434 for (; ite != edgesMultiDomains.end(); ++ite)
11436 vector<int> nodes = ite->first;
11437 vector<int> orderDom = ite->second;
11438 vector<vtkIdType> orderedNodes;
11439 if (nodes.size() == 2)
11441 //MESSAGE(" use edgesMultiDomains " << nodes[0] << " " << nodes[1]);
11442 for ( size_t ino = 0; ino < nodes.size(); ino++ )
11443 if ( orderDom.size() == 3 )
11444 for ( size_t idom = 0; idom < orderDom.size(); idom++ )
11445 orderedNodes.push_back( nodeDomains[ nodes[ ino ]][ orderDom[ idom ]]);
11447 for (int idom = orderDom.size()-1; idom >=0; idom--)
11448 orderedNodes.push_back( nodeDomains[ nodes[ ino ]][ orderDom[ idom ]]);
11449 SMDS_MeshVolume* vol = this->GetMeshDS()->AddVolumeFromVtkIds(orderedNodes);
11452 string namegrp = "jointsMultiples";
11453 if (!mapOfJunctionGroups.count(namegrp))
11454 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
11455 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11457 sgrp->Add(vol->GetID());
11461 //INFOS("Quadratic multiple joints not implemented");
11462 // TODO quadratic nodes
11467 // --- list the explicit faces and edges of the mesh that need to be modified,
11468 // i.e. faces and edges built with one or more duplicated nodes.
11469 // associate these faces or edges to their corresponding domain.
11470 // only the first domain found is kept when a face or edge is shared
11472 std::map<DownIdType, std::map<int,int>, DownIdCompare> faceOrEdgeDom; // cellToModify --> (id domain --> id cell)
11473 std::map<int,int> feDom; // vtk id of cell to modify --> id domain
11474 faceOrEdgeDom.clear();
11477 MESSAGE(".. Modification of elements");
11478 for (int idomain = idom0; idomain < nbDomains; idomain++)
11480 std::map<int, std::map<int, int> >::const_iterator itnod = nodeDomains.begin();
11481 for (; itnod != nodeDomains.end(); ++itnod)
11483 int oldId = itnod->first;
11484 //MESSAGE(" node " << oldId);
11485 vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
11486 for (int i = 0; i < l.ncells; i++)
11488 int vtkId = l.cells[i];
11489 int vtkType = grid->GetCellType(vtkId);
11490 int downId = grid->CellIdToDownId(vtkId);
11492 continue; // new cells: not to be modified
11493 DownIdType aCell(downId, vtkType);
11494 int volParents[1000];
11495 int nbvol = grid->GetParentVolumes(volParents, vtkId);
11496 for (int j = 0; j < nbvol; j++)
11497 if (celldom.count(volParents[j]) && (celldom[volParents[j]] == idomain))
11498 if (!feDom.count(vtkId))
11500 feDom[vtkId] = idomain;
11501 faceOrEdgeDom[aCell] = emptyMap;
11502 faceOrEdgeDom[aCell][idomain] = vtkId; // affect face or edge to the first domain only
11503 //MESSAGE("affect cell " << this->GetMeshDS()->fromVtkToSmds(vtkId) << " domain " << idomain
11504 // << " type " << vtkType << " downId " << downId);
11510 // --- iterate on shared faces (volumes to modify, face to extrude)
11511 // get node id's of the face
11512 // replace old nodes by new nodes in volumes, and update inverse connectivity
11514 std::map<DownIdType, std::map<int,int>, DownIdCompare>* maps[3] = {&faceDomains, &cellDomains, &faceOrEdgeDom};
11515 for (int m=0; m<3; m++)
11517 std::map<DownIdType, std::map<int,int>, DownIdCompare>* amap = maps[m];
11518 itface = (*amap).begin();
11519 for (; itface != (*amap).end(); ++itface)
11521 DownIdType face = itface->first;
11522 std::set<int> oldNodes;
11523 std::set<int>::iterator itn;
11525 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11526 //MESSAGE("examine cell, downId " << face.cellId << " type " << int(face.cellType));
11527 std::map<int, int> localClonedNodeIds;
11529 std::map<int, int> domvol = itface->second;
11530 std::map<int, int>::iterator itdom = domvol.begin();
11531 for (; itdom != domvol.end(); ++itdom)
11533 int idom = itdom->first;
11534 int vtkVolId = itdom->second;
11535 //MESSAGE("modify nodes of cell " << this->GetMeshDS()->fromVtkToSmds(vtkVolId) << " domain " << idom);
11536 localClonedNodeIds.clear();
11537 for (itn = oldNodes.begin(); itn != oldNodes.end(); ++itn)
11540 if (nodeDomains[oldId].count(idom))
11542 localClonedNodeIds[oldId] = nodeDomains[oldId][idom];
11543 //MESSAGE(" node " << oldId << " --> " << localClonedNodeIds[oldId]);
11546 meshDS->ModifyCellNodes(vtkVolId, localClonedNodeIds);
11551 // Remove empty groups (issue 0022812)
11552 std::map<std::string, SMESH_Group*>::iterator name_group = mapOfJunctionGroups.begin();
11553 for ( ; name_group != mapOfJunctionGroups.end(); ++name_group )
11555 if ( name_group->second && name_group->second->GetGroupDS()->IsEmpty() )
11556 myMesh->RemoveGroup( name_group->second->GetGroupDS()->GetID() );
11559 meshDS->CleanDownWardConnectivity(); // Mesh has been modified, downward connectivity is no more usable, free memory
11560 grid->BuildLinks();
11568 * \brief Double nodes on some external faces and create flat elements.
11569 * Flat elements are mainly used by some types of mechanic calculations.
11571 * Each group of the list must be constituted of faces.
11572 * Triangles are transformed in prisms, and quadrangles in hexahedrons.
11573 * @param theElems - list of groups of faces, where a group of faces is a set of
11574 * SMDS_MeshElements sorted by Id.
11575 * @return TRUE if operation has been completed successfully, FALSE otherwise
11577 bool SMESH_MeshEditor::CreateFlatElementsOnFacesGroups(const std::vector<TIDSortedElemSet>& theElems)
11579 MESSAGE("-------------------------------------------------");
11580 MESSAGE("SMESH_MeshEditor::CreateFlatElementsOnFacesGroups");
11581 MESSAGE("-------------------------------------------------");
11583 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11585 // --- For each group of faces
11586 // duplicate the nodes, create a flat element based on the face
11587 // replace the nodes of the faces by their clones
11589 std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> clonedNodes;
11590 std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> intermediateNodes;
11591 clonedNodes.clear();
11592 intermediateNodes.clear();
11593 std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11594 mapOfJunctionGroups.clear();
11596 for ( size_t idom = 0; idom < theElems.size(); idom++ )
11598 const TIDSortedElemSet& domain = theElems[idom];
11599 TIDSortedElemSet::const_iterator elemItr = domain.begin();
11600 for ( ; elemItr != domain.end(); ++elemItr )
11602 SMDS_MeshElement* anElem = (SMDS_MeshElement*) *elemItr;
11603 SMDS_MeshFace* aFace = dynamic_cast<SMDS_MeshFace*> (anElem);
11606 // MESSAGE("aFace=" << aFace->GetID());
11607 bool isQuad = aFace->IsQuadratic();
11608 vector<const SMDS_MeshNode*> ln0, ln1, ln2, ln3, ln4;
11610 // --- clone the nodes, create intermediate nodes for non medium nodes of a quad face
11612 SMDS_ElemIteratorPtr nodeIt = aFace->nodesIterator();
11613 while (nodeIt->more())
11615 const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*> (nodeIt->next());
11616 bool isMedium = isQuad && (aFace->IsMediumNode(node));
11618 ln2.push_back(node);
11620 ln0.push_back(node);
11622 const SMDS_MeshNode* clone = 0;
11623 if (!clonedNodes.count(node))
11625 clone = meshDS->AddNode(node->X(), node->Y(), node->Z());
11626 copyPosition( node, clone );
11627 clonedNodes[node] = clone;
11630 clone = clonedNodes[node];
11633 ln3.push_back(clone);
11635 ln1.push_back(clone);
11637 const SMDS_MeshNode* inter = 0;
11638 if (isQuad && (!isMedium))
11640 if (!intermediateNodes.count(node))
11642 inter = meshDS->AddNode(node->X(), node->Y(), node->Z());
11643 copyPosition( node, inter );
11644 intermediateNodes[node] = inter;
11647 inter = intermediateNodes[node];
11648 ln4.push_back(inter);
11652 // --- extrude the face
11654 vector<const SMDS_MeshNode*> ln;
11655 SMDS_MeshVolume* vol = 0;
11656 vtkIdType aType = aFace->GetVtkType();
11660 vol = meshDS->AddVolume(ln0[2], ln0[1], ln0[0], ln1[2], ln1[1], ln1[0]);
11661 // MESSAGE("vol prism " << vol->GetID());
11662 ln.push_back(ln1[0]);
11663 ln.push_back(ln1[1]);
11664 ln.push_back(ln1[2]);
11667 vol = meshDS->AddVolume(ln0[3], ln0[2], ln0[1], ln0[0], ln1[3], ln1[2], ln1[1], ln1[0]);
11668 // MESSAGE("vol hexa " << vol->GetID());
11669 ln.push_back(ln1[0]);
11670 ln.push_back(ln1[1]);
11671 ln.push_back(ln1[2]);
11672 ln.push_back(ln1[3]);
11674 case VTK_QUADRATIC_TRIANGLE:
11675 vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln0[0], ln0[1], ln0[2], ln3[0], ln3[1], ln3[2],
11676 ln2[0], ln2[1], ln2[2], ln4[0], ln4[1], ln4[2]);
11677 // MESSAGE("vol quad prism " << vol->GetID());
11678 ln.push_back(ln1[0]);
11679 ln.push_back(ln1[1]);
11680 ln.push_back(ln1[2]);
11681 ln.push_back(ln3[0]);
11682 ln.push_back(ln3[1]);
11683 ln.push_back(ln3[2]);
11685 case VTK_QUADRATIC_QUAD:
11686 // vol = meshDS->AddVolume(ln0[0], ln0[1], ln0[2], ln0[3], ln1[0], ln1[1], ln1[2], ln1[3],
11687 // ln2[0], ln2[1], ln2[2], ln2[3], ln3[0], ln3[1], ln3[2], ln3[3],
11688 // ln4[0], ln4[1], ln4[2], ln4[3]);
11689 vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln1[3], ln0[0], ln0[1], ln0[2], ln0[3],
11690 ln3[0], ln3[1], ln3[2], ln3[3], ln2[0], ln2[1], ln2[2], ln2[3],
11691 ln4[0], ln4[1], ln4[2], ln4[3]);
11692 // MESSAGE("vol quad hexa " << vol->GetID());
11693 ln.push_back(ln1[0]);
11694 ln.push_back(ln1[1]);
11695 ln.push_back(ln1[2]);
11696 ln.push_back(ln1[3]);
11697 ln.push_back(ln3[0]);
11698 ln.push_back(ln3[1]);
11699 ln.push_back(ln3[2]);
11700 ln.push_back(ln3[3]);
11710 stringstream grpname;
11714 string namegrp = grpname.str();
11715 if (!mapOfJunctionGroups.count(namegrp))
11716 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
11717 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11719 sgrp->Add(vol->GetID());
11722 // --- modify the face
11724 aFace->ChangeNodes(&ln[0], ln.size());
11731 * \brief identify all the elements around a geom shape, get the faces delimiting the hole
11732 * Build groups of volume to remove, groups of faces to replace on the skin of the object,
11733 * groups of faces to remove inside the object, (idem edges).
11734 * Build ordered list of nodes at the border of each group of faces to replace (to be used to build a geom subshape)
11736 void SMESH_MeshEditor::CreateHoleSkin(double radius,
11737 const TopoDS_Shape& theShape,
11738 SMESH_NodeSearcher* theNodeSearcher,
11739 const char* groupName,
11740 std::vector<double>& nodesCoords,
11741 std::vector<std::vector<int> >& listOfListOfNodes)
11743 MESSAGE("--------------------------------");
11744 MESSAGE("SMESH_MeshEditor::CreateHoleSkin");
11745 MESSAGE("--------------------------------");
11747 // --- zone of volumes to remove is given :
11748 // 1 either by a geom shape (one or more vertices) and a radius,
11749 // 2 either by a group of nodes (representative of the shape)to use with the radius,
11750 // 3 either by a group of nodes where all the elements build on one of this nodes are to remove,
11751 // In the case 2, the group of nodes is an external group of nodes from another mesh,
11752 // In the case 3, the group of nodes is an internal group of the mesh (obtained for instance by a filter),
11753 // defined by it's name.
11755 SMESHDS_GroupBase* groupDS = 0;
11756 SMESH_Mesh::GroupIteratorPtr groupIt = this->myMesh->GetGroups();
11757 while ( groupIt->more() )
11760 SMESH_Group * group = groupIt->next();
11761 if ( !group ) continue;
11762 groupDS = group->GetGroupDS();
11763 if ( !groupDS || groupDS->IsEmpty() ) continue;
11764 std::string grpName = group->GetName();
11765 //MESSAGE("grpName=" << grpName);
11766 if (grpName == groupName)
11772 bool isNodeGroup = false;
11773 bool isNodeCoords = false;
11776 if (groupDS->GetType() != SMDSAbs_Node)
11778 isNodeGroup = true; // a group of nodes exists and it is in this mesh
11781 if (nodesCoords.size() > 0)
11782 isNodeCoords = true; // a list o nodes given by their coordinates
11783 //MESSAGE("---" << isNodeGroup << " " << isNodeCoords);
11785 // --- define groups to build
11787 int idg; // --- group of SMDS volumes
11788 string grpvName = groupName;
11789 grpvName += "_vol";
11790 SMESH_Group *grp = this->myMesh->AddGroup(SMDSAbs_Volume, grpvName.c_str(), idg);
11793 MESSAGE("group not created " << grpvName);
11796 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(grp->GetGroupDS());
11798 int idgs; // --- group of SMDS faces on the skin
11799 string grpsName = groupName;
11800 grpsName += "_skin";
11801 SMESH_Group *grps = this->myMesh->AddGroup(SMDSAbs_Face, grpsName.c_str(), idgs);
11804 MESSAGE("group not created " << grpsName);
11807 SMESHDS_Group *sgrps = dynamic_cast<SMESHDS_Group*>(grps->GetGroupDS());
11809 int idgi; // --- group of SMDS faces internal (several shapes)
11810 string grpiName = groupName;
11811 grpiName += "_internalFaces";
11812 SMESH_Group *grpi = this->myMesh->AddGroup(SMDSAbs_Face, grpiName.c_str(), idgi);
11815 MESSAGE("group not created " << grpiName);
11818 SMESHDS_Group *sgrpi = dynamic_cast<SMESHDS_Group*>(grpi->GetGroupDS());
11820 int idgei; // --- group of SMDS faces internal (several shapes)
11821 string grpeiName = groupName;
11822 grpeiName += "_internalEdges";
11823 SMESH_Group *grpei = this->myMesh->AddGroup(SMDSAbs_Edge, grpeiName.c_str(), idgei);
11826 MESSAGE("group not created " << grpeiName);
11829 SMESHDS_Group *sgrpei = dynamic_cast<SMESHDS_Group*>(grpei->GetGroupDS());
11831 // --- build downward connectivity
11833 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11834 meshDS->BuildDownWardConnectivity(true);
11835 SMDS_UnstructuredGrid* grid = meshDS->getGrid();
11837 // --- set of volumes detected inside
11839 std::set<int> setOfInsideVol;
11840 std::set<int> setOfVolToCheck;
11842 std::vector<gp_Pnt> gpnts;
11845 if (isNodeGroup) // --- a group of nodes is provided : find all the volumes using one or more of this nodes
11847 MESSAGE("group of nodes provided");
11848 SMDS_ElemIteratorPtr elemIt = groupDS->GetElements();
11849 while ( elemIt->more() )
11851 const SMDS_MeshElement* elem = elemIt->next();
11854 const SMDS_MeshNode* node = dynamic_cast<const SMDS_MeshNode*>(elem);
11857 SMDS_MeshElement* vol = 0;
11858 SMDS_ElemIteratorPtr volItr = node->GetInverseElementIterator(SMDSAbs_Volume);
11859 while (volItr->more())
11861 vol = (SMDS_MeshElement*)volItr->next();
11862 setOfInsideVol.insert(vol->getVtkId());
11863 sgrp->Add(vol->GetID());
11867 else if (isNodeCoords)
11869 MESSAGE("list of nodes coordinates provided");
11872 while ( i < nodesCoords.size()-2 )
11874 double x = nodesCoords[i++];
11875 double y = nodesCoords[i++];
11876 double z = nodesCoords[i++];
11877 gp_Pnt p = gp_Pnt(x, y ,z);
11878 gpnts.push_back(p);
11879 MESSAGE("TopoDS_Vertex " << k << " " << p.X() << " " << p.Y() << " " << p.Z());
11883 else // --- no group, no coordinates : use the vertices of the geom shape provided, and radius
11885 MESSAGE("no group of nodes provided, using vertices from geom shape, and radius");
11886 TopTools_IndexedMapOfShape vertexMap;
11887 TopExp::MapShapes( theShape, TopAbs_VERTEX, vertexMap );
11888 gp_Pnt p = gp_Pnt(0,0,0);
11889 if (vertexMap.Extent() < 1)
11892 for ( int i = 1; i <= vertexMap.Extent(); ++i )
11894 const TopoDS_Vertex& vertex = TopoDS::Vertex( vertexMap( i ));
11895 p = BRep_Tool::Pnt(vertex);
11896 gpnts.push_back(p);
11897 MESSAGE("TopoDS_Vertex " << i << " " << p.X() << " " << p.Y() << " " << p.Z());
11901 if (gpnts.size() > 0)
11904 const SMDS_MeshNode* startNode = theNodeSearcher->FindClosestTo(gpnts[0]);
11906 nodeId = startNode->GetID();
11907 MESSAGE("nodeId " << nodeId);
11909 double radius2 = radius*radius;
11910 MESSAGE("radius2 " << radius2);
11912 // --- volumes on start node
11914 setOfVolToCheck.clear();
11915 SMDS_MeshElement* startVol = 0;
11916 SMDS_ElemIteratorPtr volItr = startNode->GetInverseElementIterator(SMDSAbs_Volume);
11917 while (volItr->more())
11919 startVol = (SMDS_MeshElement*)volItr->next();
11920 setOfVolToCheck.insert(startVol->getVtkId());
11922 if (setOfVolToCheck.empty())
11924 MESSAGE("No volumes found");
11928 // --- starting with central volumes then their neighbors, check if they are inside
11929 // or outside the domain, until no more new neighbor volume is inside.
11930 // Fill the group of inside volumes
11932 std::map<int, double> mapOfNodeDistance2;
11933 mapOfNodeDistance2.clear();
11934 std::set<int> setOfOutsideVol;
11935 while (!setOfVolToCheck.empty())
11937 std::set<int>::iterator it = setOfVolToCheck.begin();
11939 MESSAGE("volume to check, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
11940 bool volInside = false;
11941 vtkIdType npts = 0;
11942 vtkIdType* pts = 0;
11943 grid->GetCellPoints(vtkId, npts, pts);
11944 for (int i=0; i<npts; i++)
11946 double distance2 = 0;
11947 if (mapOfNodeDistance2.count(pts[i]))
11949 distance2 = mapOfNodeDistance2[pts[i]];
11950 MESSAGE("point " << pts[i] << " distance2 " << distance2);
11954 double *coords = grid->GetPoint(pts[i]);
11955 gp_Pnt aPoint = gp_Pnt(coords[0], coords[1], coords[2]);
11957 for ( size_t j = 0; j < gpnts.size(); j++ )
11959 double d2 = aPoint.SquareDistance( gpnts[ j ]);
11960 if (d2 < distance2)
11963 if (distance2 < radius2)
11967 mapOfNodeDistance2[pts[i]] = distance2;
11968 MESSAGE(" point " << pts[i] << " distance2 " << distance2 << " coords " << coords[0] << " " << coords[1] << " " << coords[2]);
11970 if (distance2 < radius2)
11972 volInside = true; // one or more nodes inside the domain
11973 sgrp->Add(meshDS->fromVtkToSmds(vtkId));
11979 setOfInsideVol.insert(vtkId);
11980 MESSAGE(" volume inside, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
11981 int neighborsVtkIds[NBMAXNEIGHBORS];
11982 int downIds[NBMAXNEIGHBORS];
11983 unsigned char downTypes[NBMAXNEIGHBORS];
11984 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
11985 for (int n = 0; n < nbNeighbors; n++)
11986 if (!setOfInsideVol.count(neighborsVtkIds[n]) ||setOfOutsideVol.count(neighborsVtkIds[n]))
11987 setOfVolToCheck.insert(neighborsVtkIds[n]);
11991 setOfOutsideVol.insert(vtkId);
11992 MESSAGE(" volume outside, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
11994 setOfVolToCheck.erase(vtkId);
11998 // --- for outside hexahedrons, check if they have more than one neighbor volume inside
11999 // If yes, add the volume to the inside set
12001 bool addedInside = true;
12002 std::set<int> setOfVolToReCheck;
12003 while (addedInside)
12005 MESSAGE(" --------------------------- re check");
12006 addedInside = false;
12007 std::set<int>::iterator itv = setOfInsideVol.begin();
12008 for (; itv != setOfInsideVol.end(); ++itv)
12011 int neighborsVtkIds[NBMAXNEIGHBORS];
12012 int downIds[NBMAXNEIGHBORS];
12013 unsigned char downTypes[NBMAXNEIGHBORS];
12014 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12015 for (int n = 0; n < nbNeighbors; n++)
12016 if (!setOfInsideVol.count(neighborsVtkIds[n]))
12017 setOfVolToReCheck.insert(neighborsVtkIds[n]);
12019 setOfVolToCheck = setOfVolToReCheck;
12020 setOfVolToReCheck.clear();
12021 while (!setOfVolToCheck.empty())
12023 std::set<int>::iterator it = setOfVolToCheck.begin();
12025 if (grid->GetCellType(vtkId) == VTK_HEXAHEDRON)
12027 MESSAGE("volume to recheck, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12028 int countInside = 0;
12029 int neighborsVtkIds[NBMAXNEIGHBORS];
12030 int downIds[NBMAXNEIGHBORS];
12031 unsigned char downTypes[NBMAXNEIGHBORS];
12032 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12033 for (int n = 0; n < nbNeighbors; n++)
12034 if (setOfInsideVol.count(neighborsVtkIds[n]))
12036 MESSAGE("countInside " << countInside);
12037 if (countInside > 1)
12039 MESSAGE(" volume inside, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12040 setOfInsideVol.insert(vtkId);
12041 sgrp->Add(meshDS->fromVtkToSmds(vtkId));
12042 addedInside = true;
12045 setOfVolToReCheck.insert(vtkId);
12047 setOfVolToCheck.erase(vtkId);
12051 // --- map of Downward faces at the boundary, inside the global volume
12052 // map of Downward faces on the skin of the global volume (equivalent to SMDS faces on the skin)
12053 // fill group of SMDS faces inside the volume (when several volume shapes)
12054 // fill group of SMDS faces on the skin of the global volume (if skin)
12056 std::map<DownIdType, int, DownIdCompare> boundaryFaces; // boundary faces inside the volume --> corresponding cell
12057 std::map<DownIdType, int, DownIdCompare> skinFaces; // faces on the skin of the global volume --> corresponding cell
12058 std::set<int>::iterator it = setOfInsideVol.begin();
12059 for (; it != setOfInsideVol.end(); ++it)
12062 //MESSAGE(" vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12063 int neighborsVtkIds[NBMAXNEIGHBORS];
12064 int downIds[NBMAXNEIGHBORS];
12065 unsigned char downTypes[NBMAXNEIGHBORS];
12066 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId, true);
12067 for (int n = 0; n < nbNeighbors; n++)
12069 int neighborDim = SMDS_Downward::getCellDimension(grid->GetCellType(neighborsVtkIds[n]));
12070 if (neighborDim == 3)
12072 if (! setOfInsideVol.count(neighborsVtkIds[n])) // neighbor volume is not inside : face is boundary
12074 DownIdType face(downIds[n], downTypes[n]);
12075 boundaryFaces[face] = vtkId;
12077 // if the face between to volumes is in the mesh, get it (internal face between shapes)
12078 int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12079 if (vtkFaceId >= 0)
12081 sgrpi->Add(meshDS->fromVtkToSmds(vtkFaceId));
12082 // find also the smds edges on this face
12083 int nbEdges = grid->getDownArray(downTypes[n])->getNumberOfDownCells(downIds[n]);
12084 const int* dEdges = grid->getDownArray(downTypes[n])->getDownCells(downIds[n]);
12085 const unsigned char* dTypes = grid->getDownArray(downTypes[n])->getDownTypes(downIds[n]);
12086 for (int i = 0; i < nbEdges; i++)
12088 int vtkEdgeId = grid->getDownArray(dTypes[i])->getVtkCellId(dEdges[i]);
12089 if (vtkEdgeId >= 0)
12090 sgrpei->Add(meshDS->fromVtkToSmds(vtkEdgeId));
12094 else if (neighborDim == 2) // skin of the volume
12096 DownIdType face(downIds[n], downTypes[n]);
12097 skinFaces[face] = vtkId;
12098 int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12099 if (vtkFaceId >= 0)
12100 sgrps->Add(meshDS->fromVtkToSmds(vtkFaceId));
12105 // --- identify the edges constituting the wire of each subshape on the skin
12106 // define polylines with the nodes of edges, equivalent to wires
12107 // project polylines on subshapes, and partition, to get geom faces
12109 std::map<int, std::set<int> > shapeIdToVtkIdSet; // shapeId --> set of vtkId on skin
12110 std::set<int> emptySet;
12112 std::set<int> shapeIds;
12114 SMDS_ElemIteratorPtr itelem = sgrps->GetElements();
12115 while (itelem->more())
12117 const SMDS_MeshElement *elem = itelem->next();
12118 int shapeId = elem->getshapeId();
12119 int vtkId = elem->getVtkId();
12120 if (!shapeIdToVtkIdSet.count(shapeId))
12122 shapeIdToVtkIdSet[shapeId] = emptySet;
12123 shapeIds.insert(shapeId);
12125 shapeIdToVtkIdSet[shapeId].insert(vtkId);
12128 std::map<int, std::set<DownIdType, DownIdCompare> > shapeIdToEdges; // shapeId --> set of downward edges
12129 std::set<DownIdType, DownIdCompare> emptyEdges;
12130 emptyEdges.clear();
12132 std::map<int, std::set<int> >::iterator itShape = shapeIdToVtkIdSet.begin();
12133 for (; itShape != shapeIdToVtkIdSet.end(); ++itShape)
12135 int shapeId = itShape->first;
12136 MESSAGE(" --- Shape ID --- "<< shapeId);
12137 shapeIdToEdges[shapeId] = emptyEdges;
12139 std::vector<int> nodesEdges;
12141 std::set<int>::iterator its = itShape->second.begin();
12142 for (; its != itShape->second.end(); ++its)
12145 MESSAGE(" " << vtkId);
12146 int neighborsVtkIds[NBMAXNEIGHBORS];
12147 int downIds[NBMAXNEIGHBORS];
12148 unsigned char downTypes[NBMAXNEIGHBORS];
12149 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12150 for (int n = 0; n < nbNeighbors; n++)
12152 if (neighborsVtkIds[n]<0) // only smds faces are considered as neighbors here
12154 int smdsId = meshDS->fromVtkToSmds(neighborsVtkIds[n]);
12155 const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
12156 if ( shapeIds.count(elem->getshapeId()) && !sgrps->Contains(elem)) // edge : neighbor in the set of shape, not in the group
12158 DownIdType edge(downIds[n], downTypes[n]);
12159 if (!shapeIdToEdges[shapeId].count(edge))
12161 shapeIdToEdges[shapeId].insert(edge);
12163 int nbNodes = grid->getDownArray(downTypes[n])->getNodes(downIds[n],vtkNodeId);
12164 nodesEdges.push_back(vtkNodeId[0]);
12165 nodesEdges.push_back(vtkNodeId[nbNodes-1]);
12166 MESSAGE(" --- nodes " << vtkNodeId[0]+1 << " " << vtkNodeId[nbNodes-1]+1);
12172 std::list<int> order;
12174 if (nodesEdges.size() > 0)
12176 order.push_back(nodesEdges[0]); MESSAGE(" --- back " << order.back()+1); // SMDS id = VTK id + 1;
12177 nodesEdges[0] = -1;
12178 order.push_back(nodesEdges[1]); MESSAGE(" --- back " << order.back()+1);
12179 nodesEdges[1] = -1; // do not reuse this edge
12183 int nodeTofind = order.back(); // try first to push back
12185 for ( i = 0; i < (int)nodesEdges.size(); i++ )
12186 if (nodesEdges[i] == nodeTofind)
12188 if ( i == (int) nodesEdges.size() )
12189 found = false; // no follower found on back
12192 if (i%2) // odd ==> use the previous one
12193 if (nodesEdges[i-1] < 0)
12197 order.push_back(nodesEdges[i-1]); MESSAGE(" --- back " << order.back()+1);
12198 nodesEdges[i-1] = -1;
12200 else // even ==> use the next one
12201 if (nodesEdges[i+1] < 0)
12205 order.push_back(nodesEdges[i+1]); MESSAGE(" --- back " << order.back()+1);
12206 nodesEdges[i+1] = -1;
12211 // try to push front
12213 nodeTofind = order.front(); // try to push front
12214 for ( i = 0; i < (int)nodesEdges.size(); i++ )
12215 if ( nodesEdges[i] == nodeTofind )
12217 if ( i == (int)nodesEdges.size() )
12219 found = false; // no predecessor found on front
12222 if (i%2) // odd ==> use the previous one
12223 if (nodesEdges[i-1] < 0)
12227 order.push_front(nodesEdges[i-1]); MESSAGE(" --- front " << order.front()+1);
12228 nodesEdges[i-1] = -1;
12230 else // even ==> use the next one
12231 if (nodesEdges[i+1] < 0)
12235 order.push_front(nodesEdges[i+1]); MESSAGE(" --- front " << order.front()+1);
12236 nodesEdges[i+1] = -1;
12242 std::vector<int> nodes;
12243 nodes.push_back(shapeId);
12244 std::list<int>::iterator itl = order.begin();
12245 for (; itl != order.end(); itl++)
12247 nodes.push_back((*itl) + 1); // SMDS id = VTK id + 1;
12248 MESSAGE(" ordered node " << nodes[nodes.size()-1]);
12250 listOfListOfNodes.push_back(nodes);
12253 // partition geom faces with blocFissure
12254 // mesh blocFissure and geom faces of the skin (external wires given, triangle algo to choose)
12255 // mesh volume around blocFissure (skin triangles and quadrangle given, tetra algo to choose)
12261 //================================================================================
12263 * \brief Generates skin mesh (containing 2D cells) from 3D mesh
12264 * The created 2D mesh elements based on nodes of free faces of boundary volumes
12265 * \return TRUE if operation has been completed successfully, FALSE otherwise
12267 //================================================================================
12269 bool SMESH_MeshEditor::Make2DMeshFrom3D()
12271 // iterates on volume elements and detect all free faces on them
12272 SMESHDS_Mesh* aMesh = GetMeshDS();
12276 ElemFeatures faceType( SMDSAbs_Face );
12277 int nbFree = 0, nbExisted = 0, nbCreated = 0;
12278 SMDS_VolumeIteratorPtr vIt = aMesh->volumesIterator();
12281 const SMDS_MeshVolume* volume = vIt->next();
12282 SMDS_VolumeTool vTool( volume, /*ignoreCentralNodes=*/false );
12283 vTool.SetExternalNormal();
12284 const int iQuad = volume->IsQuadratic();
12285 faceType.SetQuad( iQuad );
12286 for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12288 if (!vTool.IsFreeFace(iface))
12291 vector<const SMDS_MeshNode *> nodes;
12292 int nbFaceNodes = vTool.NbFaceNodes(iface);
12293 const SMDS_MeshNode** faceNodes = vTool.GetFaceNodes(iface);
12295 for ( ; inode < nbFaceNodes; inode += iQuad+1)
12296 nodes.push_back(faceNodes[inode]);
12298 if (iQuad) // add medium nodes
12300 for ( inode = 1; inode < nbFaceNodes; inode += 2)
12301 nodes.push_back(faceNodes[inode]);
12302 if ( nbFaceNodes == 9 ) // bi-quadratic quad
12303 nodes.push_back(faceNodes[8]);
12305 // add new face based on volume nodes
12306 if (aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false) )
12308 nbExisted++; // face already exsist
12312 AddElement( nodes, faceType.SetPoly( nbFaceNodes/(iQuad+1) > 4 ));
12317 return ( nbFree == ( nbExisted + nbCreated ));
12322 inline const SMDS_MeshNode* getNodeWithSameID(SMESHDS_Mesh* mesh, const SMDS_MeshNode* node)
12324 if ( const SMDS_MeshNode* n = mesh->FindNode( node->GetID() ))
12326 return mesh->AddNodeWithID( node->X(),node->Y(),node->Z(), node->GetID() );
12329 //================================================================================
12331 * \brief Creates missing boundary elements
12332 * \param elements - elements whose boundary is to be checked
12333 * \param dimension - defines type of boundary elements to create
12334 * \param group - a group to store created boundary elements in
12335 * \param targetMesh - a mesh to store created boundary elements in
12336 * \param toCopyElements - if true, the checked elements will be copied into the targetMesh
12337 * \param toCopyExistingBoundary - if true, not only new but also pre-existing
12338 * boundary elements will be copied into the targetMesh
12339 * \param toAddExistingBondary - if true, not only new but also pre-existing
12340 * boundary elements will be added into the new group
12341 * \param aroundElements - if true, elements will be created on boundary of given
12342 * elements else, on boundary of the whole mesh.
12343 * \return nb of added boundary elements
12345 //================================================================================
12347 int SMESH_MeshEditor::MakeBoundaryMesh(const TIDSortedElemSet& elements,
12348 Bnd_Dimension dimension,
12349 SMESH_Group* group/*=0*/,
12350 SMESH_Mesh* targetMesh/*=0*/,
12351 bool toCopyElements/*=false*/,
12352 bool toCopyExistingBoundary/*=false*/,
12353 bool toAddExistingBondary/*= false*/,
12354 bool aroundElements/*= false*/)
12356 SMDSAbs_ElementType missType = (dimension == BND_2DFROM3D) ? SMDSAbs_Face : SMDSAbs_Edge;
12357 SMDSAbs_ElementType elemType = (dimension == BND_1DFROM2D) ? SMDSAbs_Face : SMDSAbs_Volume;
12358 // hope that all elements are of the same type, do not check them all
12359 if ( !elements.empty() && (*elements.begin())->GetType() != elemType )
12360 throw SALOME_Exception(LOCALIZED("wrong element type"));
12363 toCopyElements = toCopyExistingBoundary = false;
12365 SMESH_MeshEditor tgtEditor( targetMesh ? targetMesh : myMesh );
12366 SMESHDS_Mesh* aMesh = GetMeshDS(), *tgtMeshDS = tgtEditor.GetMeshDS();
12367 int nbAddedBnd = 0;
12369 // editor adding present bnd elements and optionally holding elements to add to the group
12370 SMESH_MeshEditor* presentEditor;
12371 SMESH_MeshEditor tgtEditor2( tgtEditor.GetMesh() );
12372 presentEditor = toAddExistingBondary ? &tgtEditor : &tgtEditor2;
12374 SMESH_MesherHelper helper( *myMesh );
12375 const TopAbs_ShapeEnum missShapeType = ( missType==SMDSAbs_Face ? TopAbs_FACE : TopAbs_EDGE );
12376 SMDS_VolumeTool vTool;
12377 TIDSortedElemSet avoidSet;
12378 const TIDSortedElemSet emptySet, *elemSet = aroundElements ? &elements : &emptySet;
12381 typedef vector<const SMDS_MeshNode*> TConnectivity;
12382 TConnectivity tgtNodes;
12383 ElemFeatures elemKind( missType ), elemToCopy;
12385 vector<const SMDS_MeshElement*> presentBndElems;
12386 vector<TConnectivity> missingBndElems;
12387 vector<int> freeFacets;
12388 TConnectivity nodes, elemNodes;
12390 SMDS_ElemIteratorPtr eIt;
12391 if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
12392 else eIt = elemSetIterator( elements );
12394 while (eIt->more())
12396 const SMDS_MeshElement* elem = eIt->next();
12397 const int iQuad = elem->IsQuadratic();
12398 elemKind.SetQuad( iQuad );
12400 // ------------------------------------------------------------------------------------
12401 // 1. For an elem, get present bnd elements and connectivities of missing bnd elements
12402 // ------------------------------------------------------------------------------------
12403 presentBndElems.clear();
12404 missingBndElems.clear();
12405 freeFacets.clear(); nodes.clear(); elemNodes.clear();
12406 if ( vTool.Set(elem, /*ignoreCentralNodes=*/true) ) // elem is a volume --------------
12408 const SMDS_MeshElement* otherVol = 0;
12409 for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12411 if ( !vTool.IsFreeFace(iface, &otherVol) &&
12412 ( !aroundElements || elements.count( otherVol )))
12414 freeFacets.push_back( iface );
12416 if ( missType == SMDSAbs_Face )
12417 vTool.SetExternalNormal();
12418 for ( size_t i = 0; i < freeFacets.size(); ++i )
12420 int iface = freeFacets[i];
12421 const SMDS_MeshNode** nn = vTool.GetFaceNodes(iface);
12422 const size_t nbFaceNodes = vTool.NbFaceNodes (iface);
12423 if ( missType == SMDSAbs_Edge ) // boundary edges
12425 nodes.resize( 2+iQuad );
12426 for ( size_t i = 0; i < nbFaceNodes; i += 1+iQuad )
12428 for ( size_t j = 0; j < nodes.size(); ++j )
12429 nodes[ j ] = nn[ i+j ];
12430 if ( const SMDS_MeshElement* edge =
12431 aMesh->FindElement( nodes, SMDSAbs_Edge, /*noMedium=*/false ))
12432 presentBndElems.push_back( edge );
12434 missingBndElems.push_back( nodes );
12437 else // boundary face
12440 for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12441 nodes.push_back( nn[inode] ); // add corner nodes
12443 for ( inode = 1; inode < nbFaceNodes; inode += 2)
12444 nodes.push_back( nn[inode] ); // add medium nodes
12445 int iCenter = vTool.GetCenterNodeIndex(iface); // for HEX27
12447 nodes.push_back( vTool.GetNodes()[ iCenter ] );
12449 if (const SMDS_MeshElement * f = aMesh->FindElement( nodes,
12450 SMDSAbs_Face, /*noMedium=*/false ))
12451 presentBndElems.push_back( f );
12453 missingBndElems.push_back( nodes );
12455 if ( targetMesh != myMesh )
12457 // add 1D elements on face boundary to be added to a new mesh
12458 const SMDS_MeshElement* edge;
12459 for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12462 edge = aMesh->FindEdge( nn[inode], nn[inode+1], nn[inode+2]);
12464 edge = aMesh->FindEdge( nn[inode], nn[inode+1]);
12465 if ( edge && avoidSet.insert( edge ).second )
12466 presentBndElems.push_back( edge );
12472 else if ( elem->GetType() == SMDSAbs_Face ) // elem is a face ------------------------
12474 avoidSet.clear(), avoidSet.insert( elem );
12475 elemNodes.assign( SMDS_MeshElement::iterator( elem->interlacedNodesElemIterator() ),
12476 SMDS_MeshElement::iterator() );
12477 elemNodes.push_back( elemNodes[0] );
12478 nodes.resize( 2 + iQuad );
12479 const int nbLinks = elem->NbCornerNodes();
12480 for ( int i = 0, iN = 0; i < nbLinks; i++, iN += 1+iQuad )
12482 nodes[0] = elemNodes[iN];
12483 nodes[1] = elemNodes[iN+1+iQuad];
12484 if ( SMESH_MeshAlgos::FindFaceInSet( nodes[0], nodes[1], *elemSet, avoidSet))
12485 continue; // not free link
12487 if ( iQuad ) nodes[2] = elemNodes[iN+1];
12488 if ( const SMDS_MeshElement* edge =
12489 aMesh->FindElement(nodes,SMDSAbs_Edge,/*noMedium=*/false))
12490 presentBndElems.push_back( edge );
12492 missingBndElems.push_back( nodes );
12496 // ---------------------------------
12497 // 2. Add missing boundary elements
12498 // ---------------------------------
12499 if ( targetMesh != myMesh )
12500 // instead of making a map of nodes in this mesh and targetMesh,
12501 // we create nodes with same IDs.
12502 for ( size_t i = 0; i < missingBndElems.size(); ++i )
12504 TConnectivity& srcNodes = missingBndElems[i];
12505 tgtNodes.resize( srcNodes.size() );
12506 for ( inode = 0; inode < srcNodes.size(); ++inode )
12507 tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, srcNodes[inode] );
12508 if ( aroundElements && tgtEditor.GetMeshDS()->FindElement( tgtNodes,
12510 /*noMedium=*/false))
12512 tgtEditor.AddElement( tgtNodes, elemKind.SetPoly( tgtNodes.size()/(iQuad+1) > 4 ));
12516 for ( size_t i = 0; i < missingBndElems.size(); ++i )
12518 TConnectivity& nodes = missingBndElems[ i ];
12519 if ( aroundElements && tgtEditor.GetMeshDS()->FindElement( nodes,
12521 /*noMedium=*/false))
12523 SMDS_MeshElement* newElem =
12524 tgtEditor.AddElement( nodes, elemKind.SetPoly( nodes.size()/(iQuad+1) > 4 ));
12525 nbAddedBnd += bool( newElem );
12527 // try to set a new element to a shape
12528 if ( myMesh->HasShapeToMesh() )
12531 set< pair<TopAbs_ShapeEnum, int > > mediumShapes;
12532 const size_t nbN = nodes.size() / (iQuad+1 );
12533 for ( inode = 0; inode < nbN && ok; ++inode )
12535 pair<int, TopAbs_ShapeEnum> i_stype =
12536 helper.GetMediumPos( nodes[inode], nodes[(inode+1)%nbN]);
12537 if (( ok = ( i_stype.first > 0 && i_stype.second >= TopAbs_FACE )))
12538 mediumShapes.insert( make_pair ( i_stype.second, i_stype.first ));
12540 if ( ok && mediumShapes.size() > 1 )
12542 set< pair<TopAbs_ShapeEnum, int > >::iterator stype_i = mediumShapes.begin();
12543 pair<TopAbs_ShapeEnum, int> stype_i_0 = *stype_i;
12544 for ( ++stype_i; stype_i != mediumShapes.end() && ok; ++stype_i )
12546 if (( ok = ( stype_i->first != stype_i_0.first )))
12547 ok = helper.IsSubShape( aMesh->IndexToShape( stype_i->second ),
12548 aMesh->IndexToShape( stype_i_0.second ));
12551 if ( ok && mediumShapes.begin()->first == missShapeType )
12552 aMesh->SetMeshElementOnShape( newElem, mediumShapes.begin()->second );
12556 // ----------------------------------
12557 // 3. Copy present boundary elements
12558 // ----------------------------------
12559 if ( toCopyExistingBoundary )
12560 for ( size_t i = 0 ; i < presentBndElems.size(); ++i )
12562 const SMDS_MeshElement* e = presentBndElems[i];
12563 tgtNodes.resize( e->NbNodes() );
12564 for ( inode = 0; inode < tgtNodes.size(); ++inode )
12565 tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, e->GetNode(inode) );
12566 presentEditor->AddElement( tgtNodes, elemToCopy.Init( e ));
12568 else // store present elements to add them to a group
12569 for ( size_t i = 0 ; i < presentBndElems.size(); ++i )
12571 presentEditor->myLastCreatedElems.Append( presentBndElems[ i ]);
12574 } // loop on given elements
12576 // ---------------------------------------------
12577 // 4. Fill group with boundary elements
12578 // ---------------------------------------------
12581 if ( SMESHDS_Group* g = dynamic_cast<SMESHDS_Group*>( group->GetGroupDS() ))
12582 for ( int i = 0; i < tgtEditor.myLastCreatedElems.Size(); ++i )
12583 g->SMDSGroup().Add( tgtEditor.myLastCreatedElems( i+1 ));
12585 tgtEditor.myLastCreatedElems.Clear();
12586 tgtEditor2.myLastCreatedElems.Clear();
12588 // -----------------------
12589 // 5. Copy given elements
12590 // -----------------------
12591 if ( toCopyElements && targetMesh != myMesh )
12593 if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
12594 else eIt = elemSetIterator( elements );
12595 while (eIt->more())
12597 const SMDS_MeshElement* elem = eIt->next();
12598 tgtNodes.resize( elem->NbNodes() );
12599 for ( inode = 0; inode < tgtNodes.size(); ++inode )
12600 tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, elem->GetNode(inode) );
12601 tgtEditor.AddElement( tgtNodes, elemToCopy.Init( elem ));
12603 tgtEditor.myLastCreatedElems.Clear();
12609 //================================================================================
12611 * \brief Copy node position and set \a to node on the same geometry
12613 //================================================================================
12615 void SMESH_MeshEditor::copyPosition( const SMDS_MeshNode* from,
12616 const SMDS_MeshNode* to )
12618 if ( !from || !to ) return;
12620 SMDS_PositionPtr pos = from->GetPosition();
12621 if ( !pos || from->getshapeId() < 1 ) return;
12623 switch ( pos->GetTypeOfPosition() )
12625 case SMDS_TOP_3DSPACE: break;
12627 case SMDS_TOP_FACE:
12629 const SMDS_FacePosition* fPos = static_cast< const SMDS_FacePosition* >( pos );
12630 GetMeshDS()->SetNodeOnFace( to, from->getshapeId(),
12631 fPos->GetUParameter(), fPos->GetVParameter() );
12634 case SMDS_TOP_EDGE:
12636 // WARNING: it is dangerous to set equal nodes on one EDGE!!!!!!!!
12637 const SMDS_EdgePosition* ePos = static_cast< const SMDS_EdgePosition* >( pos );
12638 GetMeshDS()->SetNodeOnEdge( to, from->getshapeId(), ePos->GetUParameter() );
12641 case SMDS_TOP_VERTEX:
12643 GetMeshDS()->SetNodeOnVertex( to, from->getshapeId() );
12646 case SMDS_TOP_UNSPEC: