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();
10754 for ( ; elemItr != theElems.end(); ++elemItr )
10756 SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
10757 if (!anElem || (anElem->GetType() != SMDSAbs_Face))
10760 SMESH_MeshAlgos::FaceNormal( anElem, normal, /*normalized=*/true );
10761 std::set<const SMDS_MeshNode*> nodesElem;
10763 SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
10764 while ( nodeItr->more() )
10766 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10767 nodesElem.insert(aNode);
10769 std::set<const SMDS_MeshNode*>::iterator nodit = nodesElem.begin();
10770 for (; nodit != nodesElem.end(); nodit++)
10772 const SMDS_MeshNode* aNode = *nodit;
10773 if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
10775 if (alreadyCheckedNodes.find(aNode) != alreadyCheckedNodes.end())
10777 alreadyCheckedNodes.insert(aNode);
10778 SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
10779 while ( backElemItr->more() )
10781 const SMDS_MeshElement* curElem = backElemItr->next();
10782 if (alreadyCheckedElems.find(curElem) != alreadyCheckedElems.end())
10784 if (theElems.find(curElem) != theElems.end())
10786 alreadyCheckedElems.insert(curElem);
10787 double x=0, y=0, z=0;
10789 SMDS_ElemIteratorPtr nodeItr2 = curElem->nodesIterator();
10790 while ( nodeItr2->more() )
10792 const SMDS_MeshNode* anotherNode = cast2Node(nodeItr2->next());
10793 x += anotherNode->X();
10794 y += anotherNode->Y();
10795 z += anotherNode->Z();
10799 p.SetCoord( x/nb -aNode->X(),
10801 z/nb -aNode->Z() );
10804 theAffectedElems.insert( curElem );
10806 else if (curElem->GetType() == SMDSAbs_Edge)
10807 edgesToCheck.insert(curElem);
10811 // --- add also edges lying on the set of faces (all nodes in alreadyCheckedNodes)
10812 std::set<const SMDS_MeshElement*>::iterator eit = edgesToCheck.begin();
10813 for( ; eit != edgesToCheck.end(); eit++)
10815 bool onside = true;
10816 const SMDS_MeshElement* anEdge = *eit;
10817 SMDS_ElemIteratorPtr nodeItr = anEdge->nodesIterator();
10818 while ( nodeItr->more() )
10820 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10821 if (alreadyCheckedNodes.find(aNode) == alreadyCheckedNodes.end())
10829 theAffectedElems.insert(anEdge);
10835 const double aTol = Precision::Confusion();
10836 auto_ptr< BRepClass3d_SolidClassifier> bsc3d;
10837 auto_ptr<_FaceClassifier> aFaceClassifier;
10838 if ( theShape.ShapeType() == TopAbs_SOLID )
10840 bsc3d.reset( new BRepClass3d_SolidClassifier(theShape));;
10841 bsc3d->PerformInfinitePoint(aTol);
10843 else if (theShape.ShapeType() == TopAbs_FACE )
10845 aFaceClassifier.reset( new _FaceClassifier(TopoDS::Face(theShape)));
10848 // iterates on indicated elements and get elements by back references from their nodes
10849 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10850 for ( ; elemItr != theElems.end(); ++elemItr )
10852 SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
10855 SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
10856 while ( nodeItr->more() )
10858 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10859 if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
10861 SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
10862 while ( backElemItr->more() )
10864 const SMDS_MeshElement* curElem = backElemItr->next();
10865 if ( curElem && theElems.find(curElem) == theElems.end() &&
10867 isInside( curElem, *bsc3d, aTol ) :
10868 isInside( curElem, *aFaceClassifier, aTol )))
10869 theAffectedElems.insert( curElem );
10877 //================================================================================
10879 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10880 \param theElems - group of of elements (edges or faces) to be replicated
10881 \param theNodesNot - group of nodes not to replicate
10882 \param theShape - shape to detect affected elements (element which geometric center
10883 located on or inside shape).
10884 The replicated nodes should be associated to affected elements.
10885 \return TRUE if operation has been completed successfully, FALSE otherwise
10887 //================================================================================
10889 bool SMESH_MeshEditor::DoubleNodesInRegion( const TIDSortedElemSet& theElems,
10890 const TIDSortedElemSet& theNodesNot,
10891 const TopoDS_Shape& theShape )
10893 if ( theShape.IsNull() )
10896 const double aTol = Precision::Confusion();
10897 SMESHUtils::Deleter< BRepClass3d_SolidClassifier> bsc3d;
10898 SMESHUtils::Deleter<_FaceClassifier> aFaceClassifier;
10899 if ( theShape.ShapeType() == TopAbs_SOLID )
10901 bsc3d._obj = new BRepClass3d_SolidClassifier( theShape );
10902 bsc3d->PerformInfinitePoint(aTol);
10904 else if (theShape.ShapeType() == TopAbs_FACE )
10906 aFaceClassifier._obj = new _FaceClassifier( TopoDS::Face( theShape ));
10909 // iterates on indicated elements and get elements by back references from their nodes
10910 TIDSortedElemSet anAffected;
10911 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10912 for ( ; elemItr != theElems.end(); ++elemItr )
10914 SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
10918 SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
10919 while ( nodeItr->more() )
10921 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10922 if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
10924 SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
10925 while ( backElemItr->more() )
10927 const SMDS_MeshElement* curElem = backElemItr->next();
10928 if ( curElem && theElems.find(curElem) == theElems.end() &&
10930 isInside( curElem, *bsc3d, aTol ) :
10931 isInside( curElem, *aFaceClassifier, aTol )))
10932 anAffected.insert( curElem );
10936 return DoubleNodes( theElems, theNodesNot, anAffected );
10940 * \brief compute an oriented angle between two planes defined by four points.
10941 * The vector (p0,p1) defines the intersection of the 2 planes (p0,p1,g1) and (p0,p1,g2)
10942 * @param p0 base of the rotation axe
10943 * @param p1 extremity of the rotation axe
10944 * @param g1 belongs to the first plane
10945 * @param g2 belongs to the second plane
10947 double SMESH_MeshEditor::OrientedAngle(const gp_Pnt& p0, const gp_Pnt& p1, const gp_Pnt& g1, const gp_Pnt& g2)
10949 gp_Vec vref(p0, p1);
10952 gp_Vec n1 = vref.Crossed(v1);
10953 gp_Vec n2 = vref.Crossed(v2);
10955 return n2.AngleWithRef(n1, vref);
10957 catch ( Standard_Failure ) {
10959 return Max( v1.Magnitude(), v2.Magnitude() );
10963 * \brief Double nodes on shared faces between groups of volumes and create flat elements on demand.
10964 * The list of groups must contain at least two groups. The groups have to be disjoint: no common element into two different groups.
10965 * The nodes of the internal faces at the boundaries of the groups are doubled. Optionally, the internal faces are replaced by flat elements.
10966 * Triangles are transformed into prisms, and quadrangles into hexahedrons.
10967 * The flat elements are stored in groups of volumes. These groups are named according to the position of the group in the list:
10968 * 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.
10969 * 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.
10970 * All the flat elements are gathered into the group named "joints3D" (or "joints2D" in 2D situation).
10971 * The flat element of the multiple junctions between the simple junction are stored in a group named "jointsMultiples".
10972 * \param theElems - list of groups of volumes, where a group of volume is a set of
10973 * SMDS_MeshElements sorted by Id.
10974 * \param createJointElems - if TRUE, create the elements
10975 * \param onAllBoundaries - if TRUE, the nodes and elements are also created on
10976 * the boundary between \a theDomains and the rest mesh
10977 * \return TRUE if operation has been completed successfully, FALSE otherwise
10979 bool SMESH_MeshEditor::DoubleNodesOnGroupBoundaries( const std::vector<TIDSortedElemSet>& theElems,
10980 bool createJointElems,
10981 bool onAllBoundaries)
10983 MESSAGE("----------------------------------------------");
10984 MESSAGE("SMESH_MeshEditor::doubleNodesOnGroupBoundaries");
10985 MESSAGE("----------------------------------------------");
10987 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
10988 meshDS->BuildDownWardConnectivity(true);
10990 SMDS_UnstructuredGrid *grid = meshDS->getGrid();
10992 // --- build the list of faces shared by 2 domains (group of elements), with their domain and volume indexes
10993 // build the list of cells with only a node or an edge on the border, with their domain and volume indexes
10994 // build the list of nodes shared by 2 or more domains, with their domain indexes
10996 std::map<DownIdType, std::map<int,int>, DownIdCompare> faceDomains; // face --> (id domain --> id volume)
10997 std::map<int,int>celldom; // cell vtkId --> domain
10998 std::map<DownIdType, std::map<int,int>, DownIdCompare> cellDomains; // oldNode --> (id domain --> id cell)
10999 std::map<int, std::map<int,int> > nodeDomains; // oldId --> (domainId --> newId)
11000 faceDomains.clear();
11002 cellDomains.clear();
11003 nodeDomains.clear();
11004 std::map<int,int> emptyMap;
11005 std::set<int> emptySet;
11008 MESSAGE(".. Number of domains :"<<theElems.size());
11010 TIDSortedElemSet theRestDomElems;
11011 const int iRestDom = -1;
11012 const int idom0 = onAllBoundaries ? iRestDom : 0;
11013 const int nbDomains = theElems.size();
11015 // Check if the domains do not share an element
11016 for (int idom = 0; idom < nbDomains-1; idom++)
11018 // MESSAGE("... Check of domain #" << idom);
11019 const TIDSortedElemSet& domain = theElems[idom];
11020 TIDSortedElemSet::const_iterator elemItr = domain.begin();
11021 for (; elemItr != domain.end(); ++elemItr)
11023 const SMDS_MeshElement* anElem = *elemItr;
11024 int idombisdeb = idom + 1 ;
11025 // check if the element belongs to a domain further in the list
11026 for ( size_t idombis = idombisdeb; idombis < theElems.size(); idombis++ )
11028 const TIDSortedElemSet& domainbis = theElems[idombis];
11029 if ( domainbis.count( anElem ))
11031 MESSAGE(".... Domain #" << idom);
11032 MESSAGE(".... Domain #" << idombis);
11033 throw SALOME_Exception("The domains are not disjoint.");
11040 for (int idom = 0; idom < nbDomains; idom++)
11043 // --- build a map (face to duplicate --> volume to modify)
11044 // with all the faces shared by 2 domains (group of elements)
11045 // and corresponding volume of this domain, for each shared face.
11046 // a volume has a face shared by 2 domains if it has a neighbor which is not in his domain.
11048 MESSAGE("... Neighbors of domain #" << idom);
11049 const TIDSortedElemSet& domain = theElems[idom];
11050 TIDSortedElemSet::const_iterator elemItr = domain.begin();
11051 for (; elemItr != domain.end(); ++elemItr)
11053 const SMDS_MeshElement* anElem = *elemItr;
11056 int vtkId = anElem->getVtkId();
11057 //MESSAGE(" vtkId " << vtkId << " smdsId " << anElem->GetID());
11058 int neighborsVtkIds[NBMAXNEIGHBORS];
11059 int downIds[NBMAXNEIGHBORS];
11060 unsigned char downTypes[NBMAXNEIGHBORS];
11061 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
11062 for (int n = 0; n < nbNeighbors; n++)
11064 int smdsId = meshDS->fromVtkToSmds(neighborsVtkIds[n]);
11065 const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
11066 if (elem && ! domain.count(elem)) // neighbor is in another domain : face is shared
11069 for ( size_t idombis = 0; idombis < theElems.size() && !ok; idombis++) // check if the neighbor belongs to another domain of the list
11071 // MESSAGE("Domain " << idombis);
11072 const TIDSortedElemSet& domainbis = theElems[idombis];
11073 if ( domainbis.count(elem)) ok = true ; // neighbor is in a correct domain : face is kept
11075 if ( ok || onAllBoundaries ) // the characteristics of the face is stored
11077 DownIdType face(downIds[n], downTypes[n]);
11078 if (!faceDomains[face].count(idom))
11080 faceDomains[face][idom] = vtkId; // volume associated to face in this domain
11081 celldom[vtkId] = idom;
11082 //MESSAGE(" cell with a border " << vtkId << " domain " << idom);
11086 theRestDomElems.insert( elem );
11087 faceDomains[face][iRestDom] = neighborsVtkIds[n];
11088 celldom[neighborsVtkIds[n]] = iRestDom;
11096 //MESSAGE("Number of shared faces " << faceDomains.size());
11097 std::map<DownIdType, std::map<int, int>, DownIdCompare>::iterator itface;
11099 // --- explore the shared faces domain by domain,
11100 // explore the nodes of the face and see if they belong to a cell in the domain,
11101 // which has only a node or an edge on the border (not a shared face)
11103 for (int idomain = idom0; idomain < nbDomains; idomain++)
11105 //MESSAGE("Domain " << idomain);
11106 const TIDSortedElemSet& domain = (idomain == iRestDom) ? theRestDomElems : theElems[idomain];
11107 itface = faceDomains.begin();
11108 for (; itface != faceDomains.end(); ++itface)
11110 const std::map<int, int>& domvol = itface->second;
11111 if (!domvol.count(idomain))
11113 DownIdType face = itface->first;
11114 //MESSAGE(" --- face " << face.cellId);
11115 std::set<int> oldNodes;
11117 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11118 std::set<int>::iterator itn = oldNodes.begin();
11119 for (; itn != oldNodes.end(); ++itn)
11122 //MESSAGE(" node " << oldId);
11123 vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
11124 for (int i=0; i<l.ncells; i++)
11126 int vtkId = l.cells[i];
11127 const SMDS_MeshElement* anElem = GetMeshDS()->FindElement(GetMeshDS()->fromVtkToSmds(vtkId));
11128 if (!domain.count(anElem))
11130 int vtkType = grid->GetCellType(vtkId);
11131 int downId = grid->CellIdToDownId(vtkId);
11134 MESSAGE("doubleNodesOnGroupBoundaries: internal algorithm problem");
11135 continue; // not OK at this stage of the algorithm:
11136 //no cells created after BuildDownWardConnectivity
11138 DownIdType aCell(downId, vtkType);
11139 cellDomains[aCell][idomain] = vtkId;
11140 celldom[vtkId] = idomain;
11141 //MESSAGE(" cell " << vtkId << " domain " << idomain);
11147 // --- explore the shared faces domain by domain, to duplicate the nodes in a coherent way
11148 // for each shared face, get the nodes
11149 // for each node, for each domain of the face, create a clone of the node
11151 // --- edges at the intersection of 3 or 4 domains, with the order of domains to build
11152 // junction elements of type prism or hexa. the key is the pair of nodesId (lower first)
11153 // the value is the ordered domain ids. (more than 4 domains not taken into account)
11155 std::map<std::vector<int>, std::vector<int> > edgesMultiDomains; // nodes of edge --> ordered domains
11156 std::map<int, std::vector<int> > mutipleNodes; // nodes multi domains with domain order
11157 std::map<int, std::vector<int> > mutipleNodesToFace; // nodes multi domains with domain order to transform in Face (junction between 3 or more 2D domains)
11159 MESSAGE(".. Duplication of the nodes");
11160 for (int idomain = idom0; idomain < nbDomains; idomain++)
11162 itface = faceDomains.begin();
11163 for (; itface != faceDomains.end(); ++itface)
11165 const std::map<int, int>& domvol = itface->second;
11166 if (!domvol.count(idomain))
11168 DownIdType face = itface->first;
11169 //MESSAGE(" --- face " << face.cellId);
11170 std::set<int> oldNodes;
11172 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11173 std::set<int>::iterator itn = oldNodes.begin();
11174 for (; itn != oldNodes.end(); ++itn)
11177 if (nodeDomains[oldId].empty())
11179 nodeDomains[oldId][idomain] = oldId; // keep the old node in the first domain
11180 //MESSAGE("-+-+-b oldNode " << oldId << " domain " << idomain);
11182 std::map<int, int>::const_iterator itdom = domvol.begin();
11183 for (; itdom != domvol.end(); ++itdom)
11185 int idom = itdom->first;
11186 //MESSAGE(" domain " << idom);
11187 if (!nodeDomains[oldId].count(idom)) // --- node to clone
11189 if (nodeDomains[oldId].size() >= 2) // a multiple node
11191 vector<int> orderedDoms;
11192 //MESSAGE("multiple node " << oldId);
11193 if (mutipleNodes.count(oldId))
11194 orderedDoms = mutipleNodes[oldId];
11197 map<int,int>::iterator it = nodeDomains[oldId].begin();
11198 for (; it != nodeDomains[oldId].end(); ++it)
11199 orderedDoms.push_back(it->first);
11201 orderedDoms.push_back(idom); // TODO order ==> push_front or back
11202 //stringstream txt;
11203 //for (int i=0; i<orderedDoms.size(); i++)
11204 // txt << orderedDoms[i] << " ";
11205 //MESSAGE("orderedDoms " << txt.str());
11206 mutipleNodes[oldId] = orderedDoms;
11208 double *coords = grid->GetPoint(oldId);
11209 SMDS_MeshNode *newNode = meshDS->AddNode(coords[0], coords[1], coords[2]);
11210 copyPosition( meshDS->FindNodeVtk( oldId ), newNode );
11211 int newId = newNode->getVtkId();
11212 nodeDomains[oldId][idom] = newId; // cloned node for other domains
11213 //MESSAGE("-+-+-c oldNode " << oldId << " domain " << idomain << " newNode " << newId << " domain " << idom << " size=" <<nodeDomains[oldId].size());
11220 MESSAGE(".. Creation of elements");
11221 for (int idomain = idom0; idomain < nbDomains; idomain++)
11223 itface = faceDomains.begin();
11224 for (; itface != faceDomains.end(); ++itface)
11226 std::map<int, int> domvol = itface->second;
11227 if (!domvol.count(idomain))
11229 DownIdType face = itface->first;
11230 //MESSAGE(" --- face " << face.cellId);
11231 std::set<int> oldNodes;
11233 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11234 int nbMultipleNodes = 0;
11235 std::set<int>::iterator itn = oldNodes.begin();
11236 for (; itn != oldNodes.end(); ++itn)
11239 if (mutipleNodes.count(oldId))
11242 if (nbMultipleNodes > 1) // check if an edge of the face is shared between 3 or more domains
11244 //MESSAGE("multiple Nodes detected on a shared face");
11245 int downId = itface->first.cellId;
11246 unsigned char cellType = itface->first.cellType;
11247 // --- shared edge or shared face ?
11248 if ((cellType == VTK_LINE) || (cellType == VTK_QUADRATIC_EDGE)) // shared edge (between two faces)
11251 int nbNodes = grid->getDownArray(cellType)->getNodes(downId, nodes);
11252 for (int i=0; i< nbNodes; i=i+nbNodes-1) // i=0 , i=nbNodes-1
11253 if (mutipleNodes.count(nodes[i]))
11254 if (!mutipleNodesToFace.count(nodes[i]))
11255 mutipleNodesToFace[nodes[i]] = mutipleNodes[nodes[i]];
11257 else // shared face (between two volumes)
11259 int nbEdges = grid->getDownArray(cellType)->getNumberOfDownCells(downId);
11260 const int* downEdgeIds = grid->getDownArray(cellType)->getDownCells(downId);
11261 const unsigned char* edgeType = grid->getDownArray(cellType)->getDownTypes(downId);
11262 for (int ie =0; ie < nbEdges; ie++)
11265 int nbNodes = grid->getDownArray(edgeType[ie])->getNodes(downEdgeIds[ie], nodes);
11266 if ( mutipleNodes.count(nodes[0]) && mutipleNodes.count( nodes[ nbNodes-1 ]))
11268 vector<int> vn0 = mutipleNodes[nodes[0]];
11269 vector<int> vn1 = mutipleNodes[nodes[nbNodes - 1]];
11271 for ( size_t i0 = 0; i0 < vn0.size(); i0++ )
11272 for ( size_t i1 = 0; i1 < vn1.size(); i1++ )
11273 if ( vn0[i0] == vn1[i1] )
11274 doms.push_back( vn0[ i0 ]);
11275 if ( doms.size() > 2 )
11277 //MESSAGE(" detect edgesMultiDomains " << nodes[0] << " " << nodes[nbNodes - 1]);
11278 double *coords = grid->GetPoint(nodes[0]);
11279 gp_Pnt p0(coords[0], coords[1], coords[2]);
11280 coords = grid->GetPoint(nodes[nbNodes - 1]);
11281 gp_Pnt p1(coords[0], coords[1], coords[2]);
11283 int vtkVolIds[1000]; // an edge can belong to a lot of volumes
11284 map<int, SMDS_VtkVolume*> domvol; // domain --> a volume with the edge
11285 map<int, double> angleDom; // oriented angles between planes defined by edge and volume centers
11286 int nbvol = grid->GetParentVolumes(vtkVolIds, downEdgeIds[ie], edgeType[ie]);
11287 for ( size_t id = 0; id < doms.size(); id++ )
11289 int idom = doms[id];
11290 const TIDSortedElemSet& domain = (idom == iRestDom) ? theRestDomElems : theElems[idom];
11291 for ( int ivol = 0; ivol < nbvol; ivol++ )
11293 int smdsId = meshDS->fromVtkToSmds(vtkVolIds[ivol]);
11294 SMDS_MeshElement* elem = (SMDS_MeshElement*)meshDS->FindElement(smdsId);
11295 if (domain.count(elem))
11297 SMDS_VtkVolume* svol = dynamic_cast<SMDS_VtkVolume*>(elem);
11298 domvol[idom] = svol;
11299 //MESSAGE(" domain " << idom << " volume " << elem->GetID());
11301 vtkIdType npts = 0;
11302 vtkIdType* pts = 0;
11303 grid->GetCellPoints(vtkVolIds[ivol], npts, pts);
11304 SMDS_VtkVolume::gravityCenter(grid, pts, npts, values);
11307 gref.SetXYZ(gp_XYZ(values[0], values[1], values[2]));
11308 angleDom[idom] = 0;
11312 gp_Pnt g(values[0], values[1], values[2]);
11313 angleDom[idom] = OrientedAngle(p0, p1, gref, g); // -pi<angle<+pi
11314 //MESSAGE(" angle=" << angleDom[idom]);
11320 map<double, int> sortedDom; // sort domains by angle
11321 for (map<int, double>::iterator ia = angleDom.begin(); ia != angleDom.end(); ++ia)
11322 sortedDom[ia->second] = ia->first;
11323 vector<int> vnodes;
11325 for (map<double, int>::iterator ib = sortedDom.begin(); ib != sortedDom.end(); ++ib)
11327 vdom.push_back(ib->second);
11328 //MESSAGE(" ordered domain " << ib->second << " angle " << ib->first);
11330 for (int ino = 0; ino < nbNodes; ino++)
11331 vnodes.push_back(nodes[ino]);
11332 edgesMultiDomains[vnodes] = vdom; // nodes vector --> ordered domains
11341 // --- iterate on shared faces (volumes to modify, face to extrude)
11342 // get node id's of the face (id SMDS = id VTK)
11343 // create flat element with old and new nodes if requested
11345 // --- new quad nodes on flat quad elements: oldId --> ((domain1 X domain2) --> newId)
11346 // (domain1 X domain2) = domain1 + MAXINT*domain2
11348 std::map<int, std::map<long,int> > nodeQuadDomains;
11349 std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11351 MESSAGE(".. Creation of elements: simple junction");
11352 if (createJointElems)
11355 string joints2DName = "joints2D";
11356 mapOfJunctionGroups[joints2DName] = this->myMesh->AddGroup(SMDSAbs_Face, joints2DName.c_str(), idg);
11357 SMESHDS_Group *joints2DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints2DName]->GetGroupDS());
11358 string joints3DName = "joints3D";
11359 mapOfJunctionGroups[joints3DName] = this->myMesh->AddGroup(SMDSAbs_Volume, joints3DName.c_str(), idg);
11360 SMESHDS_Group *joints3DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints3DName]->GetGroupDS());
11362 itface = faceDomains.begin();
11363 for (; itface != faceDomains.end(); ++itface)
11365 DownIdType face = itface->first;
11366 std::set<int> oldNodes;
11367 std::set<int>::iterator itn;
11369 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11371 std::map<int, int> domvol = itface->second;
11372 std::map<int, int>::iterator itdom = domvol.begin();
11373 int dom1 = itdom->first;
11374 int vtkVolId = itdom->second;
11376 int dom2 = itdom->first;
11377 SMDS_MeshCell *vol = grid->extrudeVolumeFromFace(vtkVolId, dom1, dom2, oldNodes, nodeDomains,
11379 stringstream grpname;
11382 grpname << dom1 << "_" << dom2;
11384 grpname << dom2 << "_" << dom1;
11385 string namegrp = grpname.str();
11386 if (!mapOfJunctionGroups.count(namegrp))
11387 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(vol->GetType(), namegrp.c_str(), idg);
11388 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11390 sgrp->Add(vol->GetID());
11391 if (vol->GetType() == SMDSAbs_Volume)
11392 joints3DGrp->Add(vol->GetID());
11393 else if (vol->GetType() == SMDSAbs_Face)
11394 joints2DGrp->Add(vol->GetID());
11398 // --- create volumes on multiple domain intersection if requested
11399 // iterate on mutipleNodesToFace
11400 // iterate on edgesMultiDomains
11402 MESSAGE(".. Creation of elements: multiple junction");
11403 if (createJointElems)
11405 // --- iterate on mutipleNodesToFace
11407 std::map<int, std::vector<int> >::iterator itn = mutipleNodesToFace.begin();
11408 for (; itn != mutipleNodesToFace.end(); ++itn)
11410 int node = itn->first;
11411 vector<int> orderDom = itn->second;
11412 vector<vtkIdType> orderedNodes;
11413 for ( size_t idom = 0; idom < orderDom.size(); idom++ )
11414 orderedNodes.push_back( nodeDomains[ node ][ orderDom[ idom ]]);
11415 SMDS_MeshFace* face = this->GetMeshDS()->AddFaceFromVtkIds(orderedNodes);
11417 stringstream grpname;
11419 grpname << 0 << "_" << 0;
11421 string namegrp = grpname.str();
11422 if (!mapOfJunctionGroups.count(namegrp))
11423 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Face, namegrp.c_str(), idg);
11424 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11426 sgrp->Add(face->GetID());
11429 // --- iterate on edgesMultiDomains
11431 std::map<std::vector<int>, std::vector<int> >::iterator ite = edgesMultiDomains.begin();
11432 for (; ite != edgesMultiDomains.end(); ++ite)
11434 vector<int> nodes = ite->first;
11435 vector<int> orderDom = ite->second;
11436 vector<vtkIdType> orderedNodes;
11437 if (nodes.size() == 2)
11439 //MESSAGE(" use edgesMultiDomains " << nodes[0] << " " << nodes[1]);
11440 for ( size_t ino = 0; ino < nodes.size(); ino++ )
11441 if ( orderDom.size() == 3 )
11442 for ( size_t idom = 0; idom < orderDom.size(); idom++ )
11443 orderedNodes.push_back( nodeDomains[ nodes[ ino ]][ orderDom[ idom ]]);
11445 for (int idom = orderDom.size()-1; idom >=0; idom--)
11446 orderedNodes.push_back( nodeDomains[ nodes[ ino ]][ orderDom[ idom ]]);
11447 SMDS_MeshVolume* vol = this->GetMeshDS()->AddVolumeFromVtkIds(orderedNodes);
11450 string namegrp = "jointsMultiples";
11451 if (!mapOfJunctionGroups.count(namegrp))
11452 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
11453 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11455 sgrp->Add(vol->GetID());
11459 //INFOS("Quadratic multiple joints not implemented");
11460 // TODO quadratic nodes
11465 // --- list the explicit faces and edges of the mesh that need to be modified,
11466 // i.e. faces and edges built with one or more duplicated nodes.
11467 // associate these faces or edges to their corresponding domain.
11468 // only the first domain found is kept when a face or edge is shared
11470 std::map<DownIdType, std::map<int,int>, DownIdCompare> faceOrEdgeDom; // cellToModify --> (id domain --> id cell)
11471 std::map<int,int> feDom; // vtk id of cell to modify --> id domain
11472 faceOrEdgeDom.clear();
11475 MESSAGE(".. Modification of elements");
11476 for (int idomain = idom0; idomain < nbDomains; idomain++)
11478 std::map<int, std::map<int, int> >::const_iterator itnod = nodeDomains.begin();
11479 for (; itnod != nodeDomains.end(); ++itnod)
11481 int oldId = itnod->first;
11482 //MESSAGE(" node " << oldId);
11483 vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
11484 for (int i = 0; i < l.ncells; i++)
11486 int vtkId = l.cells[i];
11487 int vtkType = grid->GetCellType(vtkId);
11488 int downId = grid->CellIdToDownId(vtkId);
11490 continue; // new cells: not to be modified
11491 DownIdType aCell(downId, vtkType);
11492 int volParents[1000];
11493 int nbvol = grid->GetParentVolumes(volParents, vtkId);
11494 for (int j = 0; j < nbvol; j++)
11495 if (celldom.count(volParents[j]) && (celldom[volParents[j]] == idomain))
11496 if (!feDom.count(vtkId))
11498 feDom[vtkId] = idomain;
11499 faceOrEdgeDom[aCell] = emptyMap;
11500 faceOrEdgeDom[aCell][idomain] = vtkId; // affect face or edge to the first domain only
11501 //MESSAGE("affect cell " << this->GetMeshDS()->fromVtkToSmds(vtkId) << " domain " << idomain
11502 // << " type " << vtkType << " downId " << downId);
11508 // --- iterate on shared faces (volumes to modify, face to extrude)
11509 // get node id's of the face
11510 // replace old nodes by new nodes in volumes, and update inverse connectivity
11512 std::map<DownIdType, std::map<int,int>, DownIdCompare>* maps[3] = {&faceDomains, &cellDomains, &faceOrEdgeDom};
11513 for (int m=0; m<3; m++)
11515 std::map<DownIdType, std::map<int,int>, DownIdCompare>* amap = maps[m];
11516 itface = (*amap).begin();
11517 for (; itface != (*amap).end(); ++itface)
11519 DownIdType face = itface->first;
11520 std::set<int> oldNodes;
11521 std::set<int>::iterator itn;
11523 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11524 //MESSAGE("examine cell, downId " << face.cellId << " type " << int(face.cellType));
11525 std::map<int, int> localClonedNodeIds;
11527 std::map<int, int> domvol = itface->second;
11528 std::map<int, int>::iterator itdom = domvol.begin();
11529 for (; itdom != domvol.end(); ++itdom)
11531 int idom = itdom->first;
11532 int vtkVolId = itdom->second;
11533 //MESSAGE("modify nodes of cell " << this->GetMeshDS()->fromVtkToSmds(vtkVolId) << " domain " << idom);
11534 localClonedNodeIds.clear();
11535 for (itn = oldNodes.begin(); itn != oldNodes.end(); ++itn)
11538 if (nodeDomains[oldId].count(idom))
11540 localClonedNodeIds[oldId] = nodeDomains[oldId][idom];
11541 //MESSAGE(" node " << oldId << " --> " << localClonedNodeIds[oldId]);
11544 meshDS->ModifyCellNodes(vtkVolId, localClonedNodeIds);
11549 // Remove empty groups (issue 0022812)
11550 std::map<std::string, SMESH_Group*>::iterator name_group = mapOfJunctionGroups.begin();
11551 for ( ; name_group != mapOfJunctionGroups.end(); ++name_group )
11553 if ( name_group->second && name_group->second->GetGroupDS()->IsEmpty() )
11554 myMesh->RemoveGroup( name_group->second->GetGroupDS()->GetID() );
11557 meshDS->CleanDownWardConnectivity(); // Mesh has been modified, downward connectivity is no more usable, free memory
11558 grid->BuildLinks();
11566 * \brief Double nodes on some external faces and create flat elements.
11567 * Flat elements are mainly used by some types of mechanic calculations.
11569 * Each group of the list must be constituted of faces.
11570 * Triangles are transformed in prisms, and quadrangles in hexahedrons.
11571 * @param theElems - list of groups of faces, where a group of faces is a set of
11572 * SMDS_MeshElements sorted by Id.
11573 * @return TRUE if operation has been completed successfully, FALSE otherwise
11575 bool SMESH_MeshEditor::CreateFlatElementsOnFacesGroups(const std::vector<TIDSortedElemSet>& theElems)
11577 MESSAGE("-------------------------------------------------");
11578 MESSAGE("SMESH_MeshEditor::CreateFlatElementsOnFacesGroups");
11579 MESSAGE("-------------------------------------------------");
11581 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11583 // --- For each group of faces
11584 // duplicate the nodes, create a flat element based on the face
11585 // replace the nodes of the faces by their clones
11587 std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> clonedNodes;
11588 std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> intermediateNodes;
11589 clonedNodes.clear();
11590 intermediateNodes.clear();
11591 std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11592 mapOfJunctionGroups.clear();
11594 for ( size_t idom = 0; idom < theElems.size(); idom++ )
11596 const TIDSortedElemSet& domain = theElems[idom];
11597 TIDSortedElemSet::const_iterator elemItr = domain.begin();
11598 for ( ; elemItr != domain.end(); ++elemItr )
11600 SMDS_MeshElement* anElem = (SMDS_MeshElement*) *elemItr;
11601 SMDS_MeshFace* aFace = dynamic_cast<SMDS_MeshFace*> (anElem);
11604 // MESSAGE("aFace=" << aFace->GetID());
11605 bool isQuad = aFace->IsQuadratic();
11606 vector<const SMDS_MeshNode*> ln0, ln1, ln2, ln3, ln4;
11608 // --- clone the nodes, create intermediate nodes for non medium nodes of a quad face
11610 SMDS_ElemIteratorPtr nodeIt = aFace->nodesIterator();
11611 while (nodeIt->more())
11613 const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*> (nodeIt->next());
11614 bool isMedium = isQuad && (aFace->IsMediumNode(node));
11616 ln2.push_back(node);
11618 ln0.push_back(node);
11620 const SMDS_MeshNode* clone = 0;
11621 if (!clonedNodes.count(node))
11623 clone = meshDS->AddNode(node->X(), node->Y(), node->Z());
11624 copyPosition( node, clone );
11625 clonedNodes[node] = clone;
11628 clone = clonedNodes[node];
11631 ln3.push_back(clone);
11633 ln1.push_back(clone);
11635 const SMDS_MeshNode* inter = 0;
11636 if (isQuad && (!isMedium))
11638 if (!intermediateNodes.count(node))
11640 inter = meshDS->AddNode(node->X(), node->Y(), node->Z());
11641 copyPosition( node, inter );
11642 intermediateNodes[node] = inter;
11645 inter = intermediateNodes[node];
11646 ln4.push_back(inter);
11650 // --- extrude the face
11652 vector<const SMDS_MeshNode*> ln;
11653 SMDS_MeshVolume* vol = 0;
11654 vtkIdType aType = aFace->GetVtkType();
11658 vol = meshDS->AddVolume(ln0[2], ln0[1], ln0[0], ln1[2], ln1[1], ln1[0]);
11659 // MESSAGE("vol prism " << vol->GetID());
11660 ln.push_back(ln1[0]);
11661 ln.push_back(ln1[1]);
11662 ln.push_back(ln1[2]);
11665 vol = meshDS->AddVolume(ln0[3], ln0[2], ln0[1], ln0[0], ln1[3], ln1[2], ln1[1], ln1[0]);
11666 // MESSAGE("vol hexa " << vol->GetID());
11667 ln.push_back(ln1[0]);
11668 ln.push_back(ln1[1]);
11669 ln.push_back(ln1[2]);
11670 ln.push_back(ln1[3]);
11672 case VTK_QUADRATIC_TRIANGLE:
11673 vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln0[0], ln0[1], ln0[2], ln3[0], ln3[1], ln3[2],
11674 ln2[0], ln2[1], ln2[2], ln4[0], ln4[1], ln4[2]);
11675 // MESSAGE("vol quad prism " << vol->GetID());
11676 ln.push_back(ln1[0]);
11677 ln.push_back(ln1[1]);
11678 ln.push_back(ln1[2]);
11679 ln.push_back(ln3[0]);
11680 ln.push_back(ln3[1]);
11681 ln.push_back(ln3[2]);
11683 case VTK_QUADRATIC_QUAD:
11684 // vol = meshDS->AddVolume(ln0[0], ln0[1], ln0[2], ln0[3], ln1[0], ln1[1], ln1[2], ln1[3],
11685 // ln2[0], ln2[1], ln2[2], ln2[3], ln3[0], ln3[1], ln3[2], ln3[3],
11686 // ln4[0], ln4[1], ln4[2], ln4[3]);
11687 vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln1[3], ln0[0], ln0[1], ln0[2], ln0[3],
11688 ln3[0], ln3[1], ln3[2], ln3[3], ln2[0], ln2[1], ln2[2], ln2[3],
11689 ln4[0], ln4[1], ln4[2], ln4[3]);
11690 // MESSAGE("vol quad hexa " << vol->GetID());
11691 ln.push_back(ln1[0]);
11692 ln.push_back(ln1[1]);
11693 ln.push_back(ln1[2]);
11694 ln.push_back(ln1[3]);
11695 ln.push_back(ln3[0]);
11696 ln.push_back(ln3[1]);
11697 ln.push_back(ln3[2]);
11698 ln.push_back(ln3[3]);
11708 stringstream grpname;
11712 string namegrp = grpname.str();
11713 if (!mapOfJunctionGroups.count(namegrp))
11714 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
11715 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11717 sgrp->Add(vol->GetID());
11720 // --- modify the face
11722 aFace->ChangeNodes(&ln[0], ln.size());
11729 * \brief identify all the elements around a geom shape, get the faces delimiting the hole
11730 * Build groups of volume to remove, groups of faces to replace on the skin of the object,
11731 * groups of faces to remove inside the object, (idem edges).
11732 * Build ordered list of nodes at the border of each group of faces to replace (to be used to build a geom subshape)
11734 void SMESH_MeshEditor::CreateHoleSkin(double radius,
11735 const TopoDS_Shape& theShape,
11736 SMESH_NodeSearcher* theNodeSearcher,
11737 const char* groupName,
11738 std::vector<double>& nodesCoords,
11739 std::vector<std::vector<int> >& listOfListOfNodes)
11741 MESSAGE("--------------------------------");
11742 MESSAGE("SMESH_MeshEditor::CreateHoleSkin");
11743 MESSAGE("--------------------------------");
11745 // --- zone of volumes to remove is given :
11746 // 1 either by a geom shape (one or more vertices) and a radius,
11747 // 2 either by a group of nodes (representative of the shape)to use with the radius,
11748 // 3 either by a group of nodes where all the elements build on one of this nodes are to remove,
11749 // In the case 2, the group of nodes is an external group of nodes from another mesh,
11750 // In the case 3, the group of nodes is an internal group of the mesh (obtained for instance by a filter),
11751 // defined by it's name.
11753 SMESHDS_GroupBase* groupDS = 0;
11754 SMESH_Mesh::GroupIteratorPtr groupIt = this->myMesh->GetGroups();
11755 while ( groupIt->more() )
11758 SMESH_Group * group = groupIt->next();
11759 if ( !group ) continue;
11760 groupDS = group->GetGroupDS();
11761 if ( !groupDS || groupDS->IsEmpty() ) continue;
11762 std::string grpName = group->GetName();
11763 //MESSAGE("grpName=" << grpName);
11764 if (grpName == groupName)
11770 bool isNodeGroup = false;
11771 bool isNodeCoords = false;
11774 if (groupDS->GetType() != SMDSAbs_Node)
11776 isNodeGroup = true; // a group of nodes exists and it is in this mesh
11779 if (nodesCoords.size() > 0)
11780 isNodeCoords = true; // a list o nodes given by their coordinates
11781 //MESSAGE("---" << isNodeGroup << " " << isNodeCoords);
11783 // --- define groups to build
11785 int idg; // --- group of SMDS volumes
11786 string grpvName = groupName;
11787 grpvName += "_vol";
11788 SMESH_Group *grp = this->myMesh->AddGroup(SMDSAbs_Volume, grpvName.c_str(), idg);
11791 MESSAGE("group not created " << grpvName);
11794 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(grp->GetGroupDS());
11796 int idgs; // --- group of SMDS faces on the skin
11797 string grpsName = groupName;
11798 grpsName += "_skin";
11799 SMESH_Group *grps = this->myMesh->AddGroup(SMDSAbs_Face, grpsName.c_str(), idgs);
11802 MESSAGE("group not created " << grpsName);
11805 SMESHDS_Group *sgrps = dynamic_cast<SMESHDS_Group*>(grps->GetGroupDS());
11807 int idgi; // --- group of SMDS faces internal (several shapes)
11808 string grpiName = groupName;
11809 grpiName += "_internalFaces";
11810 SMESH_Group *grpi = this->myMesh->AddGroup(SMDSAbs_Face, grpiName.c_str(), idgi);
11813 MESSAGE("group not created " << grpiName);
11816 SMESHDS_Group *sgrpi = dynamic_cast<SMESHDS_Group*>(grpi->GetGroupDS());
11818 int idgei; // --- group of SMDS faces internal (several shapes)
11819 string grpeiName = groupName;
11820 grpeiName += "_internalEdges";
11821 SMESH_Group *grpei = this->myMesh->AddGroup(SMDSAbs_Edge, grpeiName.c_str(), idgei);
11824 MESSAGE("group not created " << grpeiName);
11827 SMESHDS_Group *sgrpei = dynamic_cast<SMESHDS_Group*>(grpei->GetGroupDS());
11829 // --- build downward connectivity
11831 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11832 meshDS->BuildDownWardConnectivity(true);
11833 SMDS_UnstructuredGrid* grid = meshDS->getGrid();
11835 // --- set of volumes detected inside
11837 std::set<int> setOfInsideVol;
11838 std::set<int> setOfVolToCheck;
11840 std::vector<gp_Pnt> gpnts;
11843 if (isNodeGroup) // --- a group of nodes is provided : find all the volumes using one or more of this nodes
11845 MESSAGE("group of nodes provided");
11846 SMDS_ElemIteratorPtr elemIt = groupDS->GetElements();
11847 while ( elemIt->more() )
11849 const SMDS_MeshElement* elem = elemIt->next();
11852 const SMDS_MeshNode* node = dynamic_cast<const SMDS_MeshNode*>(elem);
11855 SMDS_MeshElement* vol = 0;
11856 SMDS_ElemIteratorPtr volItr = node->GetInverseElementIterator(SMDSAbs_Volume);
11857 while (volItr->more())
11859 vol = (SMDS_MeshElement*)volItr->next();
11860 setOfInsideVol.insert(vol->getVtkId());
11861 sgrp->Add(vol->GetID());
11865 else if (isNodeCoords)
11867 MESSAGE("list of nodes coordinates provided");
11870 while ( i < nodesCoords.size()-2 )
11872 double x = nodesCoords[i++];
11873 double y = nodesCoords[i++];
11874 double z = nodesCoords[i++];
11875 gp_Pnt p = gp_Pnt(x, y ,z);
11876 gpnts.push_back(p);
11877 MESSAGE("TopoDS_Vertex " << k << " " << p.X() << " " << p.Y() << " " << p.Z());
11881 else // --- no group, no coordinates : use the vertices of the geom shape provided, and radius
11883 MESSAGE("no group of nodes provided, using vertices from geom shape, and radius");
11884 TopTools_IndexedMapOfShape vertexMap;
11885 TopExp::MapShapes( theShape, TopAbs_VERTEX, vertexMap );
11886 gp_Pnt p = gp_Pnt(0,0,0);
11887 if (vertexMap.Extent() < 1)
11890 for ( int i = 1; i <= vertexMap.Extent(); ++i )
11892 const TopoDS_Vertex& vertex = TopoDS::Vertex( vertexMap( i ));
11893 p = BRep_Tool::Pnt(vertex);
11894 gpnts.push_back(p);
11895 MESSAGE("TopoDS_Vertex " << i << " " << p.X() << " " << p.Y() << " " << p.Z());
11899 if (gpnts.size() > 0)
11902 const SMDS_MeshNode* startNode = theNodeSearcher->FindClosestTo(gpnts[0]);
11904 nodeId = startNode->GetID();
11905 MESSAGE("nodeId " << nodeId);
11907 double radius2 = radius*radius;
11908 MESSAGE("radius2 " << radius2);
11910 // --- volumes on start node
11912 setOfVolToCheck.clear();
11913 SMDS_MeshElement* startVol = 0;
11914 SMDS_ElemIteratorPtr volItr = startNode->GetInverseElementIterator(SMDSAbs_Volume);
11915 while (volItr->more())
11917 startVol = (SMDS_MeshElement*)volItr->next();
11918 setOfVolToCheck.insert(startVol->getVtkId());
11920 if (setOfVolToCheck.empty())
11922 MESSAGE("No volumes found");
11926 // --- starting with central volumes then their neighbors, check if they are inside
11927 // or outside the domain, until no more new neighbor volume is inside.
11928 // Fill the group of inside volumes
11930 std::map<int, double> mapOfNodeDistance2;
11931 mapOfNodeDistance2.clear();
11932 std::set<int> setOfOutsideVol;
11933 while (!setOfVolToCheck.empty())
11935 std::set<int>::iterator it = setOfVolToCheck.begin();
11937 MESSAGE("volume to check, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
11938 bool volInside = false;
11939 vtkIdType npts = 0;
11940 vtkIdType* pts = 0;
11941 grid->GetCellPoints(vtkId, npts, pts);
11942 for (int i=0; i<npts; i++)
11944 double distance2 = 0;
11945 if (mapOfNodeDistance2.count(pts[i]))
11947 distance2 = mapOfNodeDistance2[pts[i]];
11948 MESSAGE("point " << pts[i] << " distance2 " << distance2);
11952 double *coords = grid->GetPoint(pts[i]);
11953 gp_Pnt aPoint = gp_Pnt(coords[0], coords[1], coords[2]);
11955 for ( size_t j = 0; j < gpnts.size(); j++ )
11957 double d2 = aPoint.SquareDistance( gpnts[ j ]);
11958 if (d2 < distance2)
11961 if (distance2 < radius2)
11965 mapOfNodeDistance2[pts[i]] = distance2;
11966 MESSAGE(" point " << pts[i] << " distance2 " << distance2 << " coords " << coords[0] << " " << coords[1] << " " << coords[2]);
11968 if (distance2 < radius2)
11970 volInside = true; // one or more nodes inside the domain
11971 sgrp->Add(meshDS->fromVtkToSmds(vtkId));
11977 setOfInsideVol.insert(vtkId);
11978 MESSAGE(" volume inside, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
11979 int neighborsVtkIds[NBMAXNEIGHBORS];
11980 int downIds[NBMAXNEIGHBORS];
11981 unsigned char downTypes[NBMAXNEIGHBORS];
11982 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
11983 for (int n = 0; n < nbNeighbors; n++)
11984 if (!setOfInsideVol.count(neighborsVtkIds[n]) ||setOfOutsideVol.count(neighborsVtkIds[n]))
11985 setOfVolToCheck.insert(neighborsVtkIds[n]);
11989 setOfOutsideVol.insert(vtkId);
11990 MESSAGE(" volume outside, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
11992 setOfVolToCheck.erase(vtkId);
11996 // --- for outside hexahedrons, check if they have more than one neighbor volume inside
11997 // If yes, add the volume to the inside set
11999 bool addedInside = true;
12000 std::set<int> setOfVolToReCheck;
12001 while (addedInside)
12003 MESSAGE(" --------------------------- re check");
12004 addedInside = false;
12005 std::set<int>::iterator itv = setOfInsideVol.begin();
12006 for (; itv != setOfInsideVol.end(); ++itv)
12009 int neighborsVtkIds[NBMAXNEIGHBORS];
12010 int downIds[NBMAXNEIGHBORS];
12011 unsigned char downTypes[NBMAXNEIGHBORS];
12012 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12013 for (int n = 0; n < nbNeighbors; n++)
12014 if (!setOfInsideVol.count(neighborsVtkIds[n]))
12015 setOfVolToReCheck.insert(neighborsVtkIds[n]);
12017 setOfVolToCheck = setOfVolToReCheck;
12018 setOfVolToReCheck.clear();
12019 while (!setOfVolToCheck.empty())
12021 std::set<int>::iterator it = setOfVolToCheck.begin();
12023 if (grid->GetCellType(vtkId) == VTK_HEXAHEDRON)
12025 MESSAGE("volume to recheck, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12026 int countInside = 0;
12027 int neighborsVtkIds[NBMAXNEIGHBORS];
12028 int downIds[NBMAXNEIGHBORS];
12029 unsigned char downTypes[NBMAXNEIGHBORS];
12030 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12031 for (int n = 0; n < nbNeighbors; n++)
12032 if (setOfInsideVol.count(neighborsVtkIds[n]))
12034 MESSAGE("countInside " << countInside);
12035 if (countInside > 1)
12037 MESSAGE(" volume inside, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12038 setOfInsideVol.insert(vtkId);
12039 sgrp->Add(meshDS->fromVtkToSmds(vtkId));
12040 addedInside = true;
12043 setOfVolToReCheck.insert(vtkId);
12045 setOfVolToCheck.erase(vtkId);
12049 // --- map of Downward faces at the boundary, inside the global volume
12050 // map of Downward faces on the skin of the global volume (equivalent to SMDS faces on the skin)
12051 // fill group of SMDS faces inside the volume (when several volume shapes)
12052 // fill group of SMDS faces on the skin of the global volume (if skin)
12054 std::map<DownIdType, int, DownIdCompare> boundaryFaces; // boundary faces inside the volume --> corresponding cell
12055 std::map<DownIdType, int, DownIdCompare> skinFaces; // faces on the skin of the global volume --> corresponding cell
12056 std::set<int>::iterator it = setOfInsideVol.begin();
12057 for (; it != setOfInsideVol.end(); ++it)
12060 //MESSAGE(" vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12061 int neighborsVtkIds[NBMAXNEIGHBORS];
12062 int downIds[NBMAXNEIGHBORS];
12063 unsigned char downTypes[NBMAXNEIGHBORS];
12064 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId, true);
12065 for (int n = 0; n < nbNeighbors; n++)
12067 int neighborDim = SMDS_Downward::getCellDimension(grid->GetCellType(neighborsVtkIds[n]));
12068 if (neighborDim == 3)
12070 if (! setOfInsideVol.count(neighborsVtkIds[n])) // neighbor volume is not inside : face is boundary
12072 DownIdType face(downIds[n], downTypes[n]);
12073 boundaryFaces[face] = vtkId;
12075 // if the face between to volumes is in the mesh, get it (internal face between shapes)
12076 int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12077 if (vtkFaceId >= 0)
12079 sgrpi->Add(meshDS->fromVtkToSmds(vtkFaceId));
12080 // find also the smds edges on this face
12081 int nbEdges = grid->getDownArray(downTypes[n])->getNumberOfDownCells(downIds[n]);
12082 const int* dEdges = grid->getDownArray(downTypes[n])->getDownCells(downIds[n]);
12083 const unsigned char* dTypes = grid->getDownArray(downTypes[n])->getDownTypes(downIds[n]);
12084 for (int i = 0; i < nbEdges; i++)
12086 int vtkEdgeId = grid->getDownArray(dTypes[i])->getVtkCellId(dEdges[i]);
12087 if (vtkEdgeId >= 0)
12088 sgrpei->Add(meshDS->fromVtkToSmds(vtkEdgeId));
12092 else if (neighborDim == 2) // skin of the volume
12094 DownIdType face(downIds[n], downTypes[n]);
12095 skinFaces[face] = vtkId;
12096 int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12097 if (vtkFaceId >= 0)
12098 sgrps->Add(meshDS->fromVtkToSmds(vtkFaceId));
12103 // --- identify the edges constituting the wire of each subshape on the skin
12104 // define polylines with the nodes of edges, equivalent to wires
12105 // project polylines on subshapes, and partition, to get geom faces
12107 std::map<int, std::set<int> > shapeIdToVtkIdSet; // shapeId --> set of vtkId on skin
12108 std::set<int> emptySet;
12110 std::set<int> shapeIds;
12112 SMDS_ElemIteratorPtr itelem = sgrps->GetElements();
12113 while (itelem->more())
12115 const SMDS_MeshElement *elem = itelem->next();
12116 int shapeId = elem->getshapeId();
12117 int vtkId = elem->getVtkId();
12118 if (!shapeIdToVtkIdSet.count(shapeId))
12120 shapeIdToVtkIdSet[shapeId] = emptySet;
12121 shapeIds.insert(shapeId);
12123 shapeIdToVtkIdSet[shapeId].insert(vtkId);
12126 std::map<int, std::set<DownIdType, DownIdCompare> > shapeIdToEdges; // shapeId --> set of downward edges
12127 std::set<DownIdType, DownIdCompare> emptyEdges;
12128 emptyEdges.clear();
12130 std::map<int, std::set<int> >::iterator itShape = shapeIdToVtkIdSet.begin();
12131 for (; itShape != shapeIdToVtkIdSet.end(); ++itShape)
12133 int shapeId = itShape->first;
12134 MESSAGE(" --- Shape ID --- "<< shapeId);
12135 shapeIdToEdges[shapeId] = emptyEdges;
12137 std::vector<int> nodesEdges;
12139 std::set<int>::iterator its = itShape->second.begin();
12140 for (; its != itShape->second.end(); ++its)
12143 MESSAGE(" " << vtkId);
12144 int neighborsVtkIds[NBMAXNEIGHBORS];
12145 int downIds[NBMAXNEIGHBORS];
12146 unsigned char downTypes[NBMAXNEIGHBORS];
12147 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12148 for (int n = 0; n < nbNeighbors; n++)
12150 if (neighborsVtkIds[n]<0) // only smds faces are considered as neighbors here
12152 int smdsId = meshDS->fromVtkToSmds(neighborsVtkIds[n]);
12153 const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
12154 if ( shapeIds.count(elem->getshapeId()) && !sgrps->Contains(elem)) // edge : neighbor in the set of shape, not in the group
12156 DownIdType edge(downIds[n], downTypes[n]);
12157 if (!shapeIdToEdges[shapeId].count(edge))
12159 shapeIdToEdges[shapeId].insert(edge);
12161 int nbNodes = grid->getDownArray(downTypes[n])->getNodes(downIds[n],vtkNodeId);
12162 nodesEdges.push_back(vtkNodeId[0]);
12163 nodesEdges.push_back(vtkNodeId[nbNodes-1]);
12164 MESSAGE(" --- nodes " << vtkNodeId[0]+1 << " " << vtkNodeId[nbNodes-1]+1);
12170 std::list<int> order;
12172 if (nodesEdges.size() > 0)
12174 order.push_back(nodesEdges[0]); MESSAGE(" --- back " << order.back()+1); // SMDS id = VTK id + 1;
12175 nodesEdges[0] = -1;
12176 order.push_back(nodesEdges[1]); MESSAGE(" --- back " << order.back()+1);
12177 nodesEdges[1] = -1; // do not reuse this edge
12181 int nodeTofind = order.back(); // try first to push back
12183 for ( i = 0; i < (int)nodesEdges.size(); i++ )
12184 if (nodesEdges[i] == nodeTofind)
12186 if ( i == (int) nodesEdges.size() )
12187 found = false; // no follower found on back
12190 if (i%2) // odd ==> use the previous one
12191 if (nodesEdges[i-1] < 0)
12195 order.push_back(nodesEdges[i-1]); MESSAGE(" --- back " << order.back()+1);
12196 nodesEdges[i-1] = -1;
12198 else // even ==> use the next one
12199 if (nodesEdges[i+1] < 0)
12203 order.push_back(nodesEdges[i+1]); MESSAGE(" --- back " << order.back()+1);
12204 nodesEdges[i+1] = -1;
12209 // try to push front
12211 nodeTofind = order.front(); // try to push front
12212 for ( i = 0; i < (int)nodesEdges.size(); i++ )
12213 if ( nodesEdges[i] == nodeTofind )
12215 if ( i == (int)nodesEdges.size() )
12217 found = false; // no predecessor found on front
12220 if (i%2) // odd ==> use the previous one
12221 if (nodesEdges[i-1] < 0)
12225 order.push_front(nodesEdges[i-1]); MESSAGE(" --- front " << order.front()+1);
12226 nodesEdges[i-1] = -1;
12228 else // even ==> use the next one
12229 if (nodesEdges[i+1] < 0)
12233 order.push_front(nodesEdges[i+1]); MESSAGE(" --- front " << order.front()+1);
12234 nodesEdges[i+1] = -1;
12240 std::vector<int> nodes;
12241 nodes.push_back(shapeId);
12242 std::list<int>::iterator itl = order.begin();
12243 for (; itl != order.end(); itl++)
12245 nodes.push_back((*itl) + 1); // SMDS id = VTK id + 1;
12246 MESSAGE(" ordered node " << nodes[nodes.size()-1]);
12248 listOfListOfNodes.push_back(nodes);
12251 // partition geom faces with blocFissure
12252 // mesh blocFissure and geom faces of the skin (external wires given, triangle algo to choose)
12253 // mesh volume around blocFissure (skin triangles and quadrangle given, tetra algo to choose)
12259 //================================================================================
12261 * \brief Generates skin mesh (containing 2D cells) from 3D mesh
12262 * The created 2D mesh elements based on nodes of free faces of boundary volumes
12263 * \return TRUE if operation has been completed successfully, FALSE otherwise
12265 //================================================================================
12267 bool SMESH_MeshEditor::Make2DMeshFrom3D()
12269 // iterates on volume elements and detect all free faces on them
12270 SMESHDS_Mesh* aMesh = GetMeshDS();
12274 ElemFeatures faceType( SMDSAbs_Face );
12275 int nbFree = 0, nbExisted = 0, nbCreated = 0;
12276 SMDS_VolumeIteratorPtr vIt = aMesh->volumesIterator();
12279 const SMDS_MeshVolume* volume = vIt->next();
12280 SMDS_VolumeTool vTool( volume, /*ignoreCentralNodes=*/false );
12281 vTool.SetExternalNormal();
12282 const int iQuad = volume->IsQuadratic();
12283 faceType.SetQuad( iQuad );
12284 for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12286 if (!vTool.IsFreeFace(iface))
12289 vector<const SMDS_MeshNode *> nodes;
12290 int nbFaceNodes = vTool.NbFaceNodes(iface);
12291 const SMDS_MeshNode** faceNodes = vTool.GetFaceNodes(iface);
12293 for ( ; inode < nbFaceNodes; inode += iQuad+1)
12294 nodes.push_back(faceNodes[inode]);
12296 if (iQuad) // add medium nodes
12298 for ( inode = 1; inode < nbFaceNodes; inode += 2)
12299 nodes.push_back(faceNodes[inode]);
12300 if ( nbFaceNodes == 9 ) // bi-quadratic quad
12301 nodes.push_back(faceNodes[8]);
12303 // add new face based on volume nodes
12304 if (aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false) )
12306 nbExisted++; // face already exsist
12310 AddElement( nodes, faceType.SetPoly( nbFaceNodes/(iQuad+1) > 4 ));
12315 return ( nbFree == ( nbExisted + nbCreated ));
12320 inline const SMDS_MeshNode* getNodeWithSameID(SMESHDS_Mesh* mesh, const SMDS_MeshNode* node)
12322 if ( const SMDS_MeshNode* n = mesh->FindNode( node->GetID() ))
12324 return mesh->AddNodeWithID( node->X(),node->Y(),node->Z(), node->GetID() );
12327 //================================================================================
12329 * \brief Creates missing boundary elements
12330 * \param elements - elements whose boundary is to be checked
12331 * \param dimension - defines type of boundary elements to create
12332 * \param group - a group to store created boundary elements in
12333 * \param targetMesh - a mesh to store created boundary elements in
12334 * \param toCopyElements - if true, the checked elements will be copied into the targetMesh
12335 * \param toCopyExistingBoundary - if true, not only new but also pre-existing
12336 * boundary elements will be copied into the targetMesh
12337 * \param toAddExistingBondary - if true, not only new but also pre-existing
12338 * boundary elements will be added into the new group
12339 * \param aroundElements - if true, elements will be created on boundary of given
12340 * elements else, on boundary of the whole mesh.
12341 * \return nb of added boundary elements
12343 //================================================================================
12345 int SMESH_MeshEditor::MakeBoundaryMesh(const TIDSortedElemSet& elements,
12346 Bnd_Dimension dimension,
12347 SMESH_Group* group/*=0*/,
12348 SMESH_Mesh* targetMesh/*=0*/,
12349 bool toCopyElements/*=false*/,
12350 bool toCopyExistingBoundary/*=false*/,
12351 bool toAddExistingBondary/*= false*/,
12352 bool aroundElements/*= false*/)
12354 SMDSAbs_ElementType missType = (dimension == BND_2DFROM3D) ? SMDSAbs_Face : SMDSAbs_Edge;
12355 SMDSAbs_ElementType elemType = (dimension == BND_1DFROM2D) ? SMDSAbs_Face : SMDSAbs_Volume;
12356 // hope that all elements are of the same type, do not check them all
12357 if ( !elements.empty() && (*elements.begin())->GetType() != elemType )
12358 throw SALOME_Exception(LOCALIZED("wrong element type"));
12361 toCopyElements = toCopyExistingBoundary = false;
12363 SMESH_MeshEditor tgtEditor( targetMesh ? targetMesh : myMesh );
12364 SMESHDS_Mesh* aMesh = GetMeshDS(), *tgtMeshDS = tgtEditor.GetMeshDS();
12365 int nbAddedBnd = 0;
12367 // editor adding present bnd elements and optionally holding elements to add to the group
12368 SMESH_MeshEditor* presentEditor;
12369 SMESH_MeshEditor tgtEditor2( tgtEditor.GetMesh() );
12370 presentEditor = toAddExistingBondary ? &tgtEditor : &tgtEditor2;
12372 SMESH_MesherHelper helper( *myMesh );
12373 const TopAbs_ShapeEnum missShapeType = ( missType==SMDSAbs_Face ? TopAbs_FACE : TopAbs_EDGE );
12374 SMDS_VolumeTool vTool;
12375 TIDSortedElemSet avoidSet;
12376 const TIDSortedElemSet emptySet, *elemSet = aroundElements ? &elements : &emptySet;
12379 typedef vector<const SMDS_MeshNode*> TConnectivity;
12380 TConnectivity tgtNodes;
12381 ElemFeatures elemKind( missType ), elemToCopy;
12383 vector<const SMDS_MeshElement*> presentBndElems;
12384 vector<TConnectivity> missingBndElems;
12385 vector<int> freeFacets;
12386 TConnectivity nodes, elemNodes;
12388 SMDS_ElemIteratorPtr eIt;
12389 if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
12390 else eIt = elemSetIterator( elements );
12392 while (eIt->more())
12394 const SMDS_MeshElement* elem = eIt->next();
12395 const int iQuad = elem->IsQuadratic();
12396 elemKind.SetQuad( iQuad );
12398 // ------------------------------------------------------------------------------------
12399 // 1. For an elem, get present bnd elements and connectivities of missing bnd elements
12400 // ------------------------------------------------------------------------------------
12401 presentBndElems.clear();
12402 missingBndElems.clear();
12403 freeFacets.clear(); nodes.clear(); elemNodes.clear();
12404 if ( vTool.Set(elem, /*ignoreCentralNodes=*/true) ) // elem is a volume --------------
12406 const SMDS_MeshElement* otherVol = 0;
12407 for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12409 if ( !vTool.IsFreeFace(iface, &otherVol) &&
12410 ( !aroundElements || elements.count( otherVol )))
12412 freeFacets.push_back( iface );
12414 if ( missType == SMDSAbs_Face )
12415 vTool.SetExternalNormal();
12416 for ( size_t i = 0; i < freeFacets.size(); ++i )
12418 int iface = freeFacets[i];
12419 const SMDS_MeshNode** nn = vTool.GetFaceNodes(iface);
12420 const size_t nbFaceNodes = vTool.NbFaceNodes (iface);
12421 if ( missType == SMDSAbs_Edge ) // boundary edges
12423 nodes.resize( 2+iQuad );
12424 for ( size_t i = 0; i < nbFaceNodes; i += 1+iQuad )
12426 for ( size_t j = 0; j < nodes.size(); ++j )
12427 nodes[ j ] = nn[ i+j ];
12428 if ( const SMDS_MeshElement* edge =
12429 aMesh->FindElement( nodes, SMDSAbs_Edge, /*noMedium=*/false ))
12430 presentBndElems.push_back( edge );
12432 missingBndElems.push_back( nodes );
12435 else // boundary face
12438 for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12439 nodes.push_back( nn[inode] ); // add corner nodes
12441 for ( inode = 1; inode < nbFaceNodes; inode += 2)
12442 nodes.push_back( nn[inode] ); // add medium nodes
12443 int iCenter = vTool.GetCenterNodeIndex(iface); // for HEX27
12445 nodes.push_back( vTool.GetNodes()[ iCenter ] );
12447 if (const SMDS_MeshElement * f = aMesh->FindElement( nodes,
12448 SMDSAbs_Face, /*noMedium=*/false ))
12449 presentBndElems.push_back( f );
12451 missingBndElems.push_back( nodes );
12453 if ( targetMesh != myMesh )
12455 // add 1D elements on face boundary to be added to a new mesh
12456 const SMDS_MeshElement* edge;
12457 for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12460 edge = aMesh->FindEdge( nn[inode], nn[inode+1], nn[inode+2]);
12462 edge = aMesh->FindEdge( nn[inode], nn[inode+1]);
12463 if ( edge && avoidSet.insert( edge ).second )
12464 presentBndElems.push_back( edge );
12470 else if ( elem->GetType() == SMDSAbs_Face ) // elem is a face ------------------------
12472 avoidSet.clear(), avoidSet.insert( elem );
12473 elemNodes.assign( SMDS_MeshElement::iterator( elem->interlacedNodesElemIterator() ),
12474 SMDS_MeshElement::iterator() );
12475 elemNodes.push_back( elemNodes[0] );
12476 nodes.resize( 2 + iQuad );
12477 const int nbLinks = elem->NbCornerNodes();
12478 for ( int i = 0, iN = 0; i < nbLinks; i++, iN += 1+iQuad )
12480 nodes[0] = elemNodes[iN];
12481 nodes[1] = elemNodes[iN+1+iQuad];
12482 if ( SMESH_MeshAlgos::FindFaceInSet( nodes[0], nodes[1], *elemSet, avoidSet))
12483 continue; // not free link
12485 if ( iQuad ) nodes[2] = elemNodes[iN+1];
12486 if ( const SMDS_MeshElement* edge =
12487 aMesh->FindElement(nodes,SMDSAbs_Edge,/*noMedium=*/false))
12488 presentBndElems.push_back( edge );
12490 missingBndElems.push_back( nodes );
12494 // ---------------------------------
12495 // 2. Add missing boundary elements
12496 // ---------------------------------
12497 if ( targetMesh != myMesh )
12498 // instead of making a map of nodes in this mesh and targetMesh,
12499 // we create nodes with same IDs.
12500 for ( size_t i = 0; i < missingBndElems.size(); ++i )
12502 TConnectivity& srcNodes = missingBndElems[i];
12503 tgtNodes.resize( srcNodes.size() );
12504 for ( inode = 0; inode < srcNodes.size(); ++inode )
12505 tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, srcNodes[inode] );
12506 if ( aroundElements && tgtEditor.GetMeshDS()->FindElement( tgtNodes,
12508 /*noMedium=*/false))
12510 tgtEditor.AddElement( tgtNodes, elemKind.SetPoly( tgtNodes.size()/(iQuad+1) > 4 ));
12514 for ( size_t i = 0; i < missingBndElems.size(); ++i )
12516 TConnectivity& nodes = missingBndElems[ i ];
12517 if ( aroundElements && tgtEditor.GetMeshDS()->FindElement( nodes,
12519 /*noMedium=*/false))
12521 SMDS_MeshElement* newElem =
12522 tgtEditor.AddElement( nodes, elemKind.SetPoly( nodes.size()/(iQuad+1) > 4 ));
12523 nbAddedBnd += bool( newElem );
12525 // try to set a new element to a shape
12526 if ( myMesh->HasShapeToMesh() )
12529 set< pair<TopAbs_ShapeEnum, int > > mediumShapes;
12530 const size_t nbN = nodes.size() / (iQuad+1 );
12531 for ( inode = 0; inode < nbN && ok; ++inode )
12533 pair<int, TopAbs_ShapeEnum> i_stype =
12534 helper.GetMediumPos( nodes[inode], nodes[(inode+1)%nbN]);
12535 if (( ok = ( i_stype.first > 0 && i_stype.second >= TopAbs_FACE )))
12536 mediumShapes.insert( make_pair ( i_stype.second, i_stype.first ));
12538 if ( ok && mediumShapes.size() > 1 )
12540 set< pair<TopAbs_ShapeEnum, int > >::iterator stype_i = mediumShapes.begin();
12541 pair<TopAbs_ShapeEnum, int> stype_i_0 = *stype_i;
12542 for ( ++stype_i; stype_i != mediumShapes.end() && ok; ++stype_i )
12544 if (( ok = ( stype_i->first != stype_i_0.first )))
12545 ok = helper.IsSubShape( aMesh->IndexToShape( stype_i->second ),
12546 aMesh->IndexToShape( stype_i_0.second ));
12549 if ( ok && mediumShapes.begin()->first == missShapeType )
12550 aMesh->SetMeshElementOnShape( newElem, mediumShapes.begin()->second );
12554 // ----------------------------------
12555 // 3. Copy present boundary elements
12556 // ----------------------------------
12557 if ( toCopyExistingBoundary )
12558 for ( size_t i = 0 ; i < presentBndElems.size(); ++i )
12560 const SMDS_MeshElement* e = presentBndElems[i];
12561 tgtNodes.resize( e->NbNodes() );
12562 for ( inode = 0; inode < tgtNodes.size(); ++inode )
12563 tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, e->GetNode(inode) );
12564 presentEditor->AddElement( tgtNodes, elemToCopy.Init( e ));
12566 else // store present elements to add them to a group
12567 for ( size_t i = 0 ; i < presentBndElems.size(); ++i )
12569 presentEditor->myLastCreatedElems.Append( presentBndElems[ i ]);
12572 } // loop on given elements
12574 // ---------------------------------------------
12575 // 4. Fill group with boundary elements
12576 // ---------------------------------------------
12579 if ( SMESHDS_Group* g = dynamic_cast<SMESHDS_Group*>( group->GetGroupDS() ))
12580 for ( int i = 0; i < tgtEditor.myLastCreatedElems.Size(); ++i )
12581 g->SMDSGroup().Add( tgtEditor.myLastCreatedElems( i+1 ));
12583 tgtEditor.myLastCreatedElems.Clear();
12584 tgtEditor2.myLastCreatedElems.Clear();
12586 // -----------------------
12587 // 5. Copy given elements
12588 // -----------------------
12589 if ( toCopyElements && targetMesh != myMesh )
12591 if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
12592 else eIt = elemSetIterator( elements );
12593 while (eIt->more())
12595 const SMDS_MeshElement* elem = eIt->next();
12596 tgtNodes.resize( elem->NbNodes() );
12597 for ( inode = 0; inode < tgtNodes.size(); ++inode )
12598 tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, elem->GetNode(inode) );
12599 tgtEditor.AddElement( tgtNodes, elemToCopy.Init( elem ));
12601 tgtEditor.myLastCreatedElems.Clear();
12607 //================================================================================
12609 * \brief Copy node position and set \a to node on the same geometry
12611 //================================================================================
12613 void SMESH_MeshEditor::copyPosition( const SMDS_MeshNode* from,
12614 const SMDS_MeshNode* to )
12616 if ( !from || !to ) return;
12618 SMDS_PositionPtr pos = from->GetPosition();
12619 if ( !pos || from->getshapeId() < 1 ) return;
12621 switch ( pos->GetTypeOfPosition() )
12623 case SMDS_TOP_3DSPACE: break;
12625 case SMDS_TOP_FACE:
12627 const SMDS_FacePosition* fPos = static_cast< const SMDS_FacePosition* >( pos );
12628 GetMeshDS()->SetNodeOnFace( to, from->getshapeId(),
12629 fPos->GetUParameter(), fPos->GetVParameter() );
12632 case SMDS_TOP_EDGE:
12634 // WARNING: it is dangerous to set equal nodes on one EDGE!!!!!!!!
12635 const SMDS_EdgePosition* ePos = static_cast< const SMDS_EdgePosition* >( pos );
12636 GetMeshDS()->SetNodeOnEdge( to, from->getshapeId(), ePos->GetUParameter() );
12639 case SMDS_TOP_VERTEX:
12641 GetMeshDS()->SetNodeOnVertex( to, from->getshapeId() );
12644 case SMDS_TOP_UNSPEC: