1 // Copyright (C) 2007-2015 CEA/DEN, EDF R&D, OPEN CASCADE
3 // Copyright (C) 2003-2007 OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN,
4 // CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS
6 // This library is free software; you can redistribute it and/or
7 // modify it under the terms of the GNU Lesser General Public
8 // License as published by the Free Software Foundation; either
9 // version 2.1 of the License, or (at your option) any later version.
11 // This library is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 // Lesser General Public License for more details.
16 // You should have received a copy of the GNU Lesser General Public
17 // License along with this library; if not, write to the Free Software
18 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
23 // File : SMESH_MeshEditor.cxx
24 // Created : Mon Apr 12 16:10:22 2004
25 // Author : Edward AGAPOV (eap)
27 #include "SMESH_MeshEditor.hxx"
29 #include "SMDS_FaceOfNodes.hxx"
30 #include "SMDS_VolumeTool.hxx"
31 #include "SMDS_EdgePosition.hxx"
32 #include "SMDS_FacePosition.hxx"
33 #include "SMDS_SpacePosition.hxx"
34 #include "SMDS_MeshGroup.hxx"
35 #include "SMDS_LinearEdge.hxx"
36 #include "SMDS_Downward.hxx"
37 #include "SMDS_SetIterator.hxx"
39 #include "SMESHDS_Group.hxx"
40 #include "SMESHDS_Mesh.hxx"
42 #include "SMESH_Algo.hxx"
43 #include "SMESH_ControlsDef.hxx"
44 #include "SMESH_Group.hxx"
45 #include "SMESH_MeshAlgos.hxx"
46 #include "SMESH_MesherHelper.hxx"
47 #include "SMESH_OctreeNode.hxx"
48 #include "SMESH_subMesh.hxx"
50 #include <Basics_OCCTVersion.hxx>
52 #include "utilities.h"
55 #include <BRepAdaptor_Surface.hxx>
56 #include <BRepBuilderAPI_MakeEdge.hxx>
57 #include <BRepClass3d_SolidClassifier.hxx>
58 #include <BRep_Tool.hxx>
60 #include <Extrema_GenExtPS.hxx>
61 #include <Extrema_POnCurv.hxx>
62 #include <Extrema_POnSurf.hxx>
63 #include <Geom2d_Curve.hxx>
64 #include <GeomAdaptor_Surface.hxx>
65 #include <Geom_Curve.hxx>
66 #include <Geom_Surface.hxx>
67 #include <Precision.hxx>
68 #include <TColStd_ListOfInteger.hxx>
69 #include <TopAbs_State.hxx>
71 #include <TopExp_Explorer.hxx>
72 #include <TopTools_ListIteratorOfListOfShape.hxx>
73 #include <TopTools_ListOfShape.hxx>
74 #include <TopTools_SequenceOfShape.hxx>
76 #include <TopoDS_Face.hxx>
77 #include <TopoDS_Solid.hxx>
83 #include <gp_Trsf.hxx>
97 #include <boost/tuple/tuple.hpp>
99 #include <Standard_Failure.hxx>
100 #include <Standard_ErrorHandler.hxx>
102 #define cast2Node(elem) static_cast<const SMDS_MeshNode*>( elem )
105 using namespace SMESH::Controls;
109 template < class ELEM_SET >
110 SMDS_ElemIteratorPtr elemSetIterator( const ELEM_SET& elements )
112 typedef SMDS_SetIterator
113 < SMDS_pElement, typename ELEM_SET::const_iterator> TSetIterator;
114 return SMDS_ElemIteratorPtr( new TSetIterator( elements.begin(), elements.end() ));
118 //=======================================================================
119 //function : SMESH_MeshEditor
121 //=======================================================================
123 SMESH_MeshEditor::SMESH_MeshEditor( SMESH_Mesh* theMesh )
124 :myMesh( theMesh ) // theMesh may be NULL
128 //================================================================================
130 * \brief Clears myLastCreatedNodes and myLastCreatedElems
132 //================================================================================
134 void SMESH_MeshEditor::ClearLastCreated()
136 myLastCreatedNodes.Clear();
137 myLastCreatedElems.Clear();
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 MESSAGE("InverseDiag");
687 myLastCreatedElems.Clear();
688 myLastCreatedNodes.Clear();
690 if (!theTria1 || !theTria2)
693 const SMDS_VtkFace* F1 = dynamic_cast<const SMDS_VtkFace*>( theTria1 );
694 if (!F1) return false;
695 const SMDS_VtkFace* F2 = dynamic_cast<const SMDS_VtkFace*>( theTria2 );
696 if (!F2) return false;
697 if ((theTria1->GetEntityType() == SMDSEntity_Triangle) &&
698 (theTria2->GetEntityType() == SMDSEntity_Triangle)) {
700 // 1 +--+ A theTria1: ( 1 A B ) A->2 ( 1 2 B ) 1 +--+ A
701 // | /| theTria2: ( B A 2 ) B->1 ( 1 A 2 ) |\ |
705 // put nodes in array and find out indices of the same ones
706 const SMDS_MeshNode* aNodes [6];
707 int sameInd [] = { -1, -1, -1, -1, -1, -1 };
709 SMDS_ElemIteratorPtr it = theTria1->nodesIterator();
710 while ( it->more() ) {
711 aNodes[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
713 if ( i > 2 ) // theTria2
714 // find same node of theTria1
715 for ( int j = 0; j < 3; j++ )
716 if ( aNodes[ i ] == aNodes[ j ]) {
725 return false; // theTria1 is not a triangle
726 it = theTria2->nodesIterator();
728 if ( i == 6 && it->more() )
729 return false; // theTria2 is not a triangle
732 // find indices of 1,2 and of A,B in theTria1
733 int iA = -1, iB = 0, i1 = 0, i2 = 0;
734 for ( i = 0; i < 6; i++ ) {
735 if ( sameInd [ i ] == -1 ) {
740 if ( iA >= 0) iB = i;
744 // nodes 1 and 2 should not be the same
745 if ( aNodes[ i1 ] == aNodes[ i2 ] )
749 aNodes[ iA ] = aNodes[ i2 ];
751 aNodes[ sameInd[ iB ]] = aNodes[ i1 ];
753 GetMeshDS()->ChangeElementNodes( theTria1, aNodes, 3 );
754 GetMeshDS()->ChangeElementNodes( theTria2, &aNodes[ 3 ], 3 );
758 } // end if(F1 && F2)
760 // check case of quadratic faces
761 if (theTria1->GetEntityType() != SMDSEntity_Quad_Triangle &&
762 theTria1->GetEntityType() != SMDSEntity_BiQuad_Triangle)
764 if (theTria2->GetEntityType() != SMDSEntity_Quad_Triangle&&
765 theTria2->GetEntityType() != SMDSEntity_BiQuad_Triangle)
769 // 1 +--+--+ 2 theTria1: (1 2 4 5 9 7) or (2 4 1 9 7 5) or (4 1 2 7 5 9)
770 // | /| theTria2: (2 3 4 6 8 9) or (3 4 2 8 9 6) or (4 2 3 9 6 8)
778 vector< const SMDS_MeshNode* > N1;
779 vector< const SMDS_MeshNode* > N2;
780 if(!getNodesFromTwoTria(theTria1,theTria2,N1,N2))
782 // now we receive following N1 and N2 (using numeration as above image)
783 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
784 // i.e. first nodes from both arrays determ new diagonal
786 vector< const SMDS_MeshNode*> N1new( N1.size() );
787 vector< const SMDS_MeshNode*> N2new( N2.size() );
788 N1new.back() = N1.back(); // central node of biquadratic
789 N2new.back() = N2.back();
790 N1new[0] = N1[0]; N2new[0] = N1[0];
791 N1new[1] = N2[0]; N2new[1] = N1[1];
792 N1new[2] = N2[1]; N2new[2] = N2[0];
793 N1new[3] = N1[4]; N2new[3] = N1[3];
794 N1new[4] = N2[3]; N2new[4] = N2[5];
795 N1new[5] = N1[5]; N2new[5] = N1[4];
796 // change nodes in faces
797 GetMeshDS()->ChangeElementNodes( theTria1, &N1new[0], N1new.size() );
798 GetMeshDS()->ChangeElementNodes( theTria2, &N2new[0], N2new.size() );
800 // move the central node of biquadratic triangle
801 SMESH_MesherHelper helper( *GetMesh() );
802 for ( int is2nd = 0; is2nd < 2; ++is2nd )
804 const SMDS_MeshElement* tria = is2nd ? theTria2 : theTria1;
805 vector< const SMDS_MeshNode*>& nodes = is2nd ? N2new : N1new;
806 if ( nodes.size() < 7 )
808 helper.SetSubShape( tria->getshapeId() );
809 const TopoDS_Face& F = TopoDS::Face( helper.GetSubShape() );
813 xyz = ( SMESH_TNodeXYZ( nodes[3] ) +
814 SMESH_TNodeXYZ( nodes[4] ) +
815 SMESH_TNodeXYZ( nodes[5] )) / 3.;
820 gp_XY uv = ( helper.GetNodeUV( F, nodes[3], nodes[2], &checkUV ) +
821 helper.GetNodeUV( F, nodes[4], nodes[0], &checkUV ) +
822 helper.GetNodeUV( F, nodes[5], nodes[1], &checkUV )) / 3.;
824 Handle(Geom_Surface) S = BRep_Tool::Surface(F,loc);
825 xyz = S->Value( uv.X(), uv.Y() );
826 xyz.Transform( loc );
827 if ( nodes[6]->GetPosition()->GetTypeOfPosition() == SMDS_TOP_FACE && // set UV
828 nodes[6]->getshapeId() > 0 )
829 GetMeshDS()->SetNodeOnFace( nodes[6], nodes[6]->getshapeId(), uv.X(), uv.Y() );
831 GetMeshDS()->MoveNode( nodes[6], xyz.X(), xyz.Y(), xyz.Z() );
836 //=======================================================================
837 //function : findTriangles
838 //purpose : find triangles sharing theNode1-theNode2 link
839 //=======================================================================
841 static bool findTriangles(const SMDS_MeshNode * theNode1,
842 const SMDS_MeshNode * theNode2,
843 const SMDS_MeshElement*& theTria1,
844 const SMDS_MeshElement*& theTria2)
846 if ( !theNode1 || !theNode2 ) return false;
848 theTria1 = theTria2 = 0;
850 set< const SMDS_MeshElement* > emap;
851 SMDS_ElemIteratorPtr it = theNode1->GetInverseElementIterator(SMDSAbs_Face);
853 const SMDS_MeshElement* elem = it->next();
854 if ( elem->NbCornerNodes() == 3 )
857 it = theNode2->GetInverseElementIterator(SMDSAbs_Face);
859 const SMDS_MeshElement* elem = it->next();
860 if ( emap.count( elem )) {
868 // theTria1 must be element with minimum ID
869 if ( theTria2->GetID() < theTria1->GetID() )
870 std::swap( theTria2, theTria1 );
878 //=======================================================================
879 //function : InverseDiag
880 //purpose : Replace two neighbour triangles sharing theNode1-theNode2 link
881 // with ones built on the same 4 nodes but having other common link.
882 // Return false if proper faces not found
883 //=======================================================================
885 bool SMESH_MeshEditor::InverseDiag (const SMDS_MeshNode * theNode1,
886 const SMDS_MeshNode * theNode2)
888 myLastCreatedElems.Clear();
889 myLastCreatedNodes.Clear();
891 MESSAGE( "::InverseDiag()" );
893 const SMDS_MeshElement *tr1, *tr2;
894 if ( !findTriangles( theNode1, theNode2, tr1, tr2 ))
897 const SMDS_VtkFace* F1 = dynamic_cast<const SMDS_VtkFace*>( tr1 );
898 if (!F1) return false;
899 const SMDS_VtkFace* F2 = dynamic_cast<const SMDS_VtkFace*>( tr2 );
900 if (!F2) return false;
901 if ((tr1->GetEntityType() == SMDSEntity_Triangle) &&
902 (tr2->GetEntityType() == SMDSEntity_Triangle)) {
904 // 1 +--+ A tr1: ( 1 A B ) A->2 ( 1 2 B ) 1 +--+ A
905 // | /| tr2: ( B A 2 ) B->1 ( 1 A 2 ) |\ |
909 // put nodes in array
910 // and find indices of 1,2 and of A in tr1 and of B in tr2
911 int i, iA1 = 0, i1 = 0;
912 const SMDS_MeshNode* aNodes1 [3];
913 SMDS_ElemIteratorPtr it;
914 for (i = 0, it = tr1->nodesIterator(); it->more(); i++ ) {
915 aNodes1[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
916 if ( aNodes1[ i ] == theNode1 )
917 iA1 = i; // node A in tr1
918 else if ( aNodes1[ i ] != theNode2 )
922 const SMDS_MeshNode* aNodes2 [3];
923 for (i = 0, it = tr2->nodesIterator(); it->more(); i++ ) {
924 aNodes2[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
925 if ( aNodes2[ i ] == theNode2 )
926 iB2 = i; // node B in tr2
927 else if ( aNodes2[ i ] != theNode1 )
931 // nodes 1 and 2 should not be the same
932 if ( aNodes1[ i1 ] == aNodes2[ i2 ] )
936 aNodes1[ iA1 ] = aNodes2[ i2 ];
938 aNodes2[ iB2 ] = aNodes1[ i1 ];
940 GetMeshDS()->ChangeElementNodes( tr1, aNodes1, 3 );
941 GetMeshDS()->ChangeElementNodes( tr2, aNodes2, 3 );
946 // check case of quadratic faces
947 return InverseDiag(tr1,tr2);
950 //=======================================================================
951 //function : getQuadrangleNodes
952 //purpose : fill theQuadNodes - nodes of a quadrangle resulting from
953 // fusion of triangles tr1 and tr2 having shared link on
954 // theNode1 and theNode2
955 //=======================================================================
957 bool getQuadrangleNodes(const SMDS_MeshNode * theQuadNodes [],
958 const SMDS_MeshNode * theNode1,
959 const SMDS_MeshNode * theNode2,
960 const SMDS_MeshElement * tr1,
961 const SMDS_MeshElement * tr2 )
963 if( tr1->NbNodes() != tr2->NbNodes() )
965 // find the 4-th node to insert into tr1
966 const SMDS_MeshNode* n4 = 0;
967 SMDS_ElemIteratorPtr it = tr2->nodesIterator();
969 while ( !n4 && i<3 ) {
970 const SMDS_MeshNode * n = cast2Node( it->next() );
972 bool isDiag = ( n == theNode1 || n == theNode2 );
976 // Make an array of nodes to be in a quadrangle
977 int iNode = 0, iFirstDiag = -1;
978 it = tr1->nodesIterator();
981 const SMDS_MeshNode * n = cast2Node( it->next() );
983 bool isDiag = ( n == theNode1 || n == theNode2 );
985 if ( iFirstDiag < 0 )
987 else if ( iNode - iFirstDiag == 1 )
988 theQuadNodes[ iNode++ ] = n4; // insert the 4-th node between diagonal nodes
990 else if ( n == n4 ) {
991 return false; // tr1 and tr2 should not have all the same nodes
993 theQuadNodes[ iNode++ ] = n;
995 if ( iNode == 3 ) // diagonal nodes have 0 and 2 indices
996 theQuadNodes[ iNode ] = n4;
1001 //=======================================================================
1002 //function : DeleteDiag
1003 //purpose : Replace two neighbour triangles sharing theNode1-theNode2 link
1004 // with a quadrangle built on the same 4 nodes.
1005 // Return false if proper faces not found
1006 //=======================================================================
1008 bool SMESH_MeshEditor::DeleteDiag (const SMDS_MeshNode * theNode1,
1009 const SMDS_MeshNode * theNode2)
1011 myLastCreatedElems.Clear();
1012 myLastCreatedNodes.Clear();
1014 MESSAGE( "::DeleteDiag()" );
1016 const SMDS_MeshElement *tr1, *tr2;
1017 if ( !findTriangles( theNode1, theNode2, tr1, tr2 ))
1020 const SMDS_VtkFace* F1 = dynamic_cast<const SMDS_VtkFace*>( tr1 );
1021 if (!F1) return false;
1022 const SMDS_VtkFace* F2 = dynamic_cast<const SMDS_VtkFace*>( tr2 );
1023 if (!F2) return false;
1024 SMESHDS_Mesh * aMesh = GetMeshDS();
1026 if ((tr1->GetEntityType() == SMDSEntity_Triangle) &&
1027 (tr2->GetEntityType() == SMDSEntity_Triangle)) {
1029 const SMDS_MeshNode* aNodes [ 4 ];
1030 if ( ! getQuadrangleNodes( aNodes, theNode1, theNode2, tr1, tr2 ))
1033 const SMDS_MeshElement* newElem = 0;
1034 newElem = aMesh->AddFace( aNodes[0], aNodes[1], aNodes[2], aNodes[3] );
1035 myLastCreatedElems.Append(newElem);
1036 AddToSameGroups( newElem, tr1, aMesh );
1037 int aShapeId = tr1->getshapeId();
1040 aMesh->SetMeshElementOnShape( newElem, aShapeId );
1042 aMesh->RemoveElement( tr1 );
1043 aMesh->RemoveElement( tr2 );
1048 // check case of quadratic faces
1049 if (tr1->GetEntityType() != SMDSEntity_Quad_Triangle)
1051 if (tr2->GetEntityType() != SMDSEntity_Quad_Triangle)
1055 // 1 +--+--+ 2 tr1: (1 2 4 5 9 7) or (2 4 1 9 7 5) or (4 1 2 7 5 9)
1056 // | /| tr2: (2 3 4 6 8 9) or (3 4 2 8 9 6) or (4 2 3 9 6 8)
1064 vector< const SMDS_MeshNode* > N1;
1065 vector< const SMDS_MeshNode* > N2;
1066 if(!getNodesFromTwoTria(tr1,tr2,N1,N2))
1068 // now we receive following N1 and N2 (using numeration as above image)
1069 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
1070 // i.e. first nodes from both arrays determ new diagonal
1072 const SMDS_MeshNode* aNodes[8];
1082 const SMDS_MeshElement* newElem = 0;
1083 newElem = aMesh->AddFace( aNodes[0], aNodes[1], aNodes[2], aNodes[3],
1084 aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
1085 myLastCreatedElems.Append(newElem);
1086 AddToSameGroups( newElem, tr1, aMesh );
1087 int aShapeId = tr1->getshapeId();
1090 aMesh->SetMeshElementOnShape( newElem, aShapeId );
1092 aMesh->RemoveElement( tr1 );
1093 aMesh->RemoveElement( tr2 );
1095 // remove middle node (9)
1096 GetMeshDS()->RemoveNode( N1[4] );
1101 //=======================================================================
1102 //function : Reorient
1103 //purpose : Reverse theElement orientation
1104 //=======================================================================
1106 bool SMESH_MeshEditor::Reorient (const SMDS_MeshElement * theElem)
1108 MESSAGE("Reorient");
1109 myLastCreatedElems.Clear();
1110 myLastCreatedNodes.Clear();
1114 SMDS_ElemIteratorPtr it = theElem->nodesIterator();
1115 if ( !it || !it->more() )
1118 const SMDSAbs_ElementType type = theElem->GetType();
1119 if ( type < SMDSAbs_Edge || type > SMDSAbs_Volume )
1122 const SMDSAbs_EntityType geomType = theElem->GetEntityType();
1123 if ( geomType == SMDSEntity_Polyhedra ) // polyhedron
1125 const SMDS_VtkVolume* aPolyedre =
1126 dynamic_cast<const SMDS_VtkVolume*>( theElem );
1128 MESSAGE("Warning: bad volumic element");
1131 const int nbFaces = aPolyedre->NbFaces();
1132 vector<const SMDS_MeshNode *> poly_nodes;
1133 vector<int> quantities (nbFaces);
1135 // reverse each face of the polyedre
1136 for (int iface = 1; iface <= nbFaces; iface++) {
1137 int inode, nbFaceNodes = aPolyedre->NbFaceNodes(iface);
1138 quantities[iface - 1] = nbFaceNodes;
1140 for (inode = nbFaceNodes; inode >= 1; inode--) {
1141 const SMDS_MeshNode* curNode = aPolyedre->GetFaceNode(iface, inode);
1142 poly_nodes.push_back(curNode);
1145 return GetMeshDS()->ChangePolyhedronNodes( theElem, poly_nodes, quantities );
1147 else // other elements
1149 vector<const SMDS_MeshNode*> nodes( theElem->begin_nodes(), theElem->end_nodes() );
1150 const std::vector<int>& interlace = SMDS_MeshCell::reverseSmdsOrder( geomType, nodes.size() );
1151 if ( interlace.empty() )
1153 std::reverse( nodes.begin(), nodes.end() ); // obsolete, just in case
1157 SMDS_MeshCell::applyInterlace( interlace, nodes );
1159 return GetMeshDS()->ChangeElementNodes( theElem, &nodes[0], nodes.size() );
1164 //================================================================================
1166 * \brief Reorient faces.
1167 * \param theFaces - the faces to reorient. If empty the whole mesh is meant
1168 * \param theDirection - desired direction of normal of \a theFace
1169 * \param theFace - one of \a theFaces that sould be oriented according to
1170 * \a theDirection and whose orientation defines orientation of other faces
1171 * \return number of reoriented faces.
1173 //================================================================================
1175 int SMESH_MeshEditor::Reorient2D (TIDSortedElemSet & theFaces,
1176 const gp_Dir& theDirection,
1177 const SMDS_MeshElement * theFace)
1180 if ( !theFace || theFace->GetType() != SMDSAbs_Face ) return nbReori;
1182 if ( theFaces.empty() )
1184 SMDS_FaceIteratorPtr fIt = GetMeshDS()->facesIterator(/*idInceasingOrder=*/true);
1185 while ( fIt->more() )
1186 theFaces.insert( theFaces.end(), fIt->next() );
1189 // orient theFace according to theDirection
1191 SMESH_MeshAlgos::FaceNormal( theFace, normal, /*normalized=*/false );
1192 if ( normal * theDirection.XYZ() < 0 )
1193 nbReori += Reorient( theFace );
1195 // Orient other faces
1197 set< const SMDS_MeshElement* > startFaces, visitedFaces;
1198 TIDSortedElemSet avoidSet;
1199 set< SMESH_TLink > checkedLinks;
1200 pair< set< SMESH_TLink >::iterator, bool > linkIt_isNew;
1202 if ( theFaces.size() > 1 )// leave 1 face to prevent finding not selected faces
1203 theFaces.erase( theFace );
1204 startFaces.insert( theFace );
1206 int nodeInd1, nodeInd2;
1207 const SMDS_MeshElement* otherFace;
1208 vector< const SMDS_MeshElement* > facesNearLink;
1209 vector< std::pair< int, int > > nodeIndsOfFace;
1211 set< const SMDS_MeshElement* >::iterator startFace = startFaces.begin();
1212 while ( !startFaces.empty() )
1214 startFace = startFaces.begin();
1215 theFace = *startFace;
1216 startFaces.erase( startFace );
1217 if ( !visitedFaces.insert( theFace ).second )
1221 avoidSet.insert(theFace);
1223 NLink link( theFace->GetNode( 0 ), (SMDS_MeshNode *) 0 );
1225 const int nbNodes = theFace->NbCornerNodes();
1226 for ( int i = 0; i < nbNodes; ++i ) // loop on links of theFace
1228 link.second = theFace->GetNode(( i+1 ) % nbNodes );
1229 linkIt_isNew = checkedLinks.insert( link );
1230 if ( !linkIt_isNew.second )
1232 // link has already been checked and won't be encountered more
1233 // if the group (theFaces) is manifold
1234 //checkedLinks.erase( linkIt_isNew.first );
1238 facesNearLink.clear();
1239 nodeIndsOfFace.clear();
1240 while (( otherFace = SMESH_MeshAlgos::FindFaceInSet( link.first, link.second,
1242 &nodeInd1, &nodeInd2 )))
1243 if ( otherFace != theFace)
1245 facesNearLink.push_back( otherFace );
1246 nodeIndsOfFace.push_back( make_pair( nodeInd1, nodeInd2 ));
1247 avoidSet.insert( otherFace );
1249 if ( facesNearLink.size() > 1 )
1251 // NON-MANIFOLD mesh shell !
1252 // select a face most co-directed with theFace,
1253 // other faces won't be visited this time
1255 SMESH_MeshAlgos::FaceNormal( theFace, NF, /*normalized=*/false );
1256 double proj, maxProj = -1;
1257 for ( size_t i = 0; i < facesNearLink.size(); ++i ) {
1258 SMESH_MeshAlgos::FaceNormal( facesNearLink[i], NOF, /*normalized=*/false );
1259 if (( proj = Abs( NF * NOF )) > maxProj ) {
1261 otherFace = facesNearLink[i];
1262 nodeInd1 = nodeIndsOfFace[i].first;
1263 nodeInd2 = nodeIndsOfFace[i].second;
1266 // not to visit rejected faces
1267 for ( size_t i = 0; i < facesNearLink.size(); ++i )
1268 if ( facesNearLink[i] != otherFace && theFaces.size() > 1 )
1269 visitedFaces.insert( facesNearLink[i] );
1271 else if ( facesNearLink.size() == 1 )
1273 otherFace = facesNearLink[0];
1274 nodeInd1 = nodeIndsOfFace.back().first;
1275 nodeInd2 = nodeIndsOfFace.back().second;
1277 if ( otherFace && otherFace != theFace)
1279 // link must be reverse in otherFace if orientation ot otherFace
1280 // is same as that of theFace
1281 if ( abs(nodeInd2-nodeInd1) == 1 ? nodeInd2 > nodeInd1 : nodeInd1 > nodeInd2 )
1283 nbReori += Reorient( otherFace );
1285 startFaces.insert( otherFace );
1288 std::swap( link.first, link.second ); // reverse the link
1294 //================================================================================
1296 * \brief Reorient faces basing on orientation of adjacent volumes.
1297 * \param theFaces - faces to reorient. If empty, all mesh faces are treated.
1298 * \param theVolumes - reference volumes.
1299 * \param theOutsideNormal - to orient faces to have their normal
1300 * pointing either \a outside or \a inside the adjacent volumes.
1301 * \return number of reoriented faces.
1303 //================================================================================
1305 int SMESH_MeshEditor::Reorient2DBy3D (TIDSortedElemSet & theFaces,
1306 TIDSortedElemSet & theVolumes,
1307 const bool theOutsideNormal)
1311 SMDS_ElemIteratorPtr faceIt;
1312 if ( theFaces.empty() )
1313 faceIt = GetMeshDS()->elementsIterator( SMDSAbs_Face );
1315 faceIt = elemSetIterator( theFaces );
1317 vector< const SMDS_MeshNode* > faceNodes;
1318 TIDSortedElemSet checkedVolumes;
1319 set< const SMDS_MeshNode* > faceNodesSet;
1320 SMDS_VolumeTool volumeTool;
1322 while ( faceIt->more() ) // loop on given faces
1324 const SMDS_MeshElement* face = faceIt->next();
1325 if ( face->GetType() != SMDSAbs_Face )
1328 const int nbCornersNodes = face->NbCornerNodes();
1329 faceNodes.assign( face->begin_nodes(), face->end_nodes() );
1331 checkedVolumes.clear();
1332 SMDS_ElemIteratorPtr vIt = faceNodes[ 0 ]->GetInverseElementIterator( SMDSAbs_Volume );
1333 while ( vIt->more() )
1335 const SMDS_MeshElement* volume = vIt->next();
1337 if ( !checkedVolumes.insert( volume ).second )
1339 if ( !theVolumes.empty() && !theVolumes.count( volume ))
1342 // is volume adjacent?
1343 bool allNodesCommon = true;
1344 for ( int iN = 1; iN < nbCornersNodes && allNodesCommon; ++iN )
1345 allNodesCommon = ( volume->GetNodeIndex( faceNodes[ iN ]) > -1 );
1346 if ( !allNodesCommon )
1349 // get nodes of a corresponding volume facet
1350 faceNodesSet.clear();
1351 faceNodesSet.insert( faceNodes.begin(), faceNodes.end() );
1352 volumeTool.Set( volume );
1353 int facetID = volumeTool.GetFaceIndex( faceNodesSet );
1354 if ( facetID < 0 ) continue;
1355 volumeTool.SetExternalNormal();
1356 const SMDS_MeshNode** facetNodes = volumeTool.GetFaceNodes( facetID );
1358 // compare order of faceNodes and facetNodes
1359 const int iQ = 1 + ( nbCornersNodes < faceNodes.size() );
1361 for ( int i = 0; i < 2; ++i )
1363 const SMDS_MeshNode* n = facetNodes[ i*iQ ];
1364 for ( int iN = 0; iN < nbCornersNodes; ++iN )
1365 if ( faceNodes[ iN ] == n )
1371 bool isOutside = Abs( iNN[0]-iNN[1] ) == 1 ? iNN[0] < iNN[1] : iNN[0] > iNN[1];
1372 if ( isOutside != theOutsideNormal )
1373 nbReori += Reorient( face );
1375 } // loop on given faces
1380 //=======================================================================
1381 //function : getBadRate
1383 //=======================================================================
1385 static double getBadRate (const SMDS_MeshElement* theElem,
1386 SMESH::Controls::NumericalFunctorPtr& theCrit)
1388 SMESH::Controls::TSequenceOfXYZ P;
1389 if ( !theElem || !theCrit->GetPoints( theElem, P ))
1391 return theCrit->GetBadRate( theCrit->GetValue( P ), theElem->NbNodes() );
1392 //return theCrit->GetBadRate( theCrit->GetValue( theElem->GetID() ), theElem->NbNodes() );
1395 //=======================================================================
1396 //function : QuadToTri
1397 //purpose : Cut quadrangles into triangles.
1398 // theCrit is used to select a diagonal to cut
1399 //=======================================================================
1401 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet & theElems,
1402 SMESH::Controls::NumericalFunctorPtr theCrit)
1404 myLastCreatedElems.Clear();
1405 myLastCreatedNodes.Clear();
1407 if ( !theCrit.get() )
1410 SMESHDS_Mesh * aMesh = GetMeshDS();
1412 Handle(Geom_Surface) surface;
1413 SMESH_MesherHelper helper( *GetMesh() );
1415 TIDSortedElemSet::iterator itElem;
1416 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
1418 const SMDS_MeshElement* elem = *itElem;
1419 if ( !elem || elem->GetType() != SMDSAbs_Face )
1421 if ( elem->NbCornerNodes() != 4 )
1424 // retrieve element nodes
1425 vector< const SMDS_MeshNode* > aNodes( elem->begin_nodes(), elem->end_nodes() );
1427 // compare two sets of possible triangles
1428 double aBadRate1, aBadRate2; // to what extent a set is bad
1429 SMDS_FaceOfNodes tr1 ( aNodes[0], aNodes[1], aNodes[2] );
1430 SMDS_FaceOfNodes tr2 ( aNodes[2], aNodes[3], aNodes[0] );
1431 aBadRate1 = getBadRate( &tr1, theCrit ) + getBadRate( &tr2, theCrit );
1433 SMDS_FaceOfNodes tr3 ( aNodes[1], aNodes[2], aNodes[3] );
1434 SMDS_FaceOfNodes tr4 ( aNodes[3], aNodes[0], aNodes[1] );
1435 aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit );
1437 const int aShapeId = FindShape( elem );
1438 const SMDS_MeshElement* newElem1 = 0;
1439 const SMDS_MeshElement* newElem2 = 0;
1441 if ( !elem->IsQuadratic() ) // split liner quadrangle
1443 // for MaxElementLength2D functor we return minimum diagonal for splitting,
1444 // because aBadRate1=2*len(diagonal 1-3); aBadRate2=2*len(diagonal 2-4)
1445 if ( aBadRate1 <= aBadRate2 ) {
1446 // tr1 + tr2 is better
1447 newElem1 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
1448 newElem2 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
1451 // tr3 + tr4 is better
1452 newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
1453 newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
1456 else // split quadratic quadrangle
1458 helper.SetIsQuadratic( true );
1459 helper.SetIsBiQuadratic( aNodes.size() == 9 );
1461 helper.AddTLinks( static_cast< const SMDS_MeshFace* >( elem ));
1462 if ( aNodes.size() == 9 )
1464 helper.SetIsBiQuadratic( true );
1465 if ( aBadRate1 <= aBadRate2 )
1466 helper.AddTLinkNode( aNodes[0], aNodes[2], aNodes[8] );
1468 helper.AddTLinkNode( aNodes[1], aNodes[3], aNodes[8] );
1470 // create a new element
1471 if ( aBadRate1 <= aBadRate2 ) {
1472 newElem1 = helper.AddFace( aNodes[2], aNodes[3], aNodes[0] );
1473 newElem2 = helper.AddFace( aNodes[2], aNodes[0], aNodes[1] );
1476 newElem1 = helper.AddFace( aNodes[3], aNodes[0], aNodes[1] );
1477 newElem2 = helper.AddFace( aNodes[3], aNodes[1], aNodes[2] );
1481 // care of a new element
1483 myLastCreatedElems.Append(newElem1);
1484 myLastCreatedElems.Append(newElem2);
1485 AddToSameGroups( newElem1, elem, aMesh );
1486 AddToSameGroups( newElem2, elem, aMesh );
1488 // put a new triangle on the same shape
1490 aMesh->SetMeshElementOnShape( newElem1, aShapeId );
1491 aMesh->SetMeshElementOnShape( newElem2, aShapeId );
1493 aMesh->RemoveElement( elem );
1498 //=======================================================================
1500 * \brief Split each of given quadrangles into 4 triangles.
1501 * \param theElems - The faces to be splitted. If empty all faces are split.
1503 //=======================================================================
1505 void SMESH_MeshEditor::QuadTo4Tri (TIDSortedElemSet & theElems)
1507 myLastCreatedElems.Clear();
1508 myLastCreatedNodes.Clear();
1510 SMESH_MesherHelper helper( *GetMesh() );
1511 helper.SetElementsOnShape( true );
1513 SMDS_ElemIteratorPtr faceIt;
1514 if ( theElems.empty() ) faceIt = GetMeshDS()->elementsIterator(SMDSAbs_Face);
1515 else faceIt = elemSetIterator( theElems );
1518 gp_XY uv [9]; uv[8] = gp_XY(0,0);
1520 vector< const SMDS_MeshNode* > nodes;
1521 SMESHDS_SubMesh* subMeshDS;
1523 Handle(Geom_Surface) surface;
1524 TopLoc_Location loc;
1526 while ( faceIt->more() )
1528 const SMDS_MeshElement* quad = faceIt->next();
1529 if ( !quad || quad->NbCornerNodes() != 4 )
1532 // get a surface the quad is on
1534 if ( quad->getshapeId() < 1 )
1537 helper.SetSubShape( 0 );
1540 else if ( quad->getshapeId() != helper.GetSubShapeID() )
1542 helper.SetSubShape( quad->getshapeId() );
1543 if ( !helper.GetSubShape().IsNull() &&
1544 helper.GetSubShape().ShapeType() == TopAbs_FACE )
1546 F = TopoDS::Face( helper.GetSubShape() );
1547 surface = BRep_Tool::Surface( F, loc );
1548 subMeshDS = GetMeshDS()->MeshElements( quad->getshapeId() );
1552 helper.SetSubShape( 0 );
1557 // create a central node
1559 const SMDS_MeshNode* nCentral;
1560 nodes.assign( quad->begin_nodes(), quad->end_nodes() );
1562 if ( nodes.size() == 9 )
1564 nCentral = nodes.back();
1571 for ( ; iN < nodes.size(); ++iN )
1572 xyz[ iN ] = SMESH_TNodeXYZ( nodes[ iN ] );
1574 for ( ; iN < 8; ++iN ) // mid-side points of a linear qudrangle
1575 xyz[ iN ] = 0.5 * ( xyz[ iN - 4 ] + xyz[( iN - 3 )%4 ] );
1577 xyz[ 8 ] = helper.calcTFI( 0.5, 0.5,
1578 xyz[0], xyz[1], xyz[2], xyz[3],
1579 xyz[4], xyz[5], xyz[6], xyz[7] );
1583 for ( ; iN < nodes.size(); ++iN )
1584 uv[ iN ] = helper.GetNodeUV( F, nodes[iN], nodes[(iN+2)%4], &checkUV );
1586 for ( ; iN < 8; ++iN ) // UV of mid-side points of a linear qudrangle
1587 uv[ iN ] = helper.GetMiddleUV( surface, uv[ iN - 4 ], uv[( iN - 3 )%4 ] );
1589 uv[ 8 ] = helper.calcTFI( 0.5, 0.5,
1590 uv[0], uv[1], uv[2], uv[3],
1591 uv[4], uv[5], uv[6], uv[7] );
1593 gp_Pnt p = surface->Value( uv[8].X(), uv[8].Y() ).Transformed( loc );
1597 nCentral = helper.AddNode( xyz[8].X(), xyz[8].Y(), xyz[8].Z(), /*id=*/0,
1598 uv[8].X(), uv[8].Y() );
1599 myLastCreatedNodes.Append( nCentral );
1602 // create 4 triangles
1604 GetMeshDS()->RemoveFreeElement( quad, subMeshDS, /*fromGroups=*/false );
1606 helper.SetIsQuadratic ( nodes.size() > 4 );
1607 helper.SetIsBiQuadratic( nodes.size() == 9 );
1608 if ( helper.GetIsQuadratic() )
1609 helper.AddTLinks( static_cast< const SMDS_MeshFace*>( quad ));
1611 for ( int i = 0; i < 4; ++i )
1613 SMDS_MeshElement* tria = helper.AddFace( nodes[ i ],
1616 ReplaceElemInGroups( tria, quad, GetMeshDS() );
1617 myLastCreatedElems.Append( tria );
1622 //=======================================================================
1623 //function : BestSplit
1624 //purpose : Find better diagonal for cutting.
1625 //=======================================================================
1627 int SMESH_MeshEditor::BestSplit (const SMDS_MeshElement* theQuad,
1628 SMESH::Controls::NumericalFunctorPtr theCrit)
1630 myLastCreatedElems.Clear();
1631 myLastCreatedNodes.Clear();
1636 if (!theQuad || theQuad->GetType() != SMDSAbs_Face )
1639 if( theQuad->NbNodes()==4 ||
1640 (theQuad->NbNodes()==8 && theQuad->IsQuadratic()) ) {
1642 // retrieve element nodes
1643 const SMDS_MeshNode* aNodes [4];
1644 SMDS_ElemIteratorPtr itN = theQuad->nodesIterator();
1646 //while (itN->more())
1648 aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
1650 // compare two sets of possible triangles
1651 double aBadRate1, aBadRate2; // to what extent a set is bad
1652 SMDS_FaceOfNodes tr1 ( aNodes[0], aNodes[1], aNodes[2] );
1653 SMDS_FaceOfNodes tr2 ( aNodes[2], aNodes[3], aNodes[0] );
1654 aBadRate1 = getBadRate( &tr1, theCrit ) + getBadRate( &tr2, theCrit );
1656 SMDS_FaceOfNodes tr3 ( aNodes[1], aNodes[2], aNodes[3] );
1657 SMDS_FaceOfNodes tr4 ( aNodes[3], aNodes[0], aNodes[1] );
1658 aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit );
1659 // for MaxElementLength2D functor we return minimum diagonal for splitting,
1660 // because aBadRate1=2*len(diagonal 1-3); aBadRate2=2*len(diagonal 2-4)
1661 if (aBadRate1 <= aBadRate2) // tr1 + tr2 is better
1662 return 1; // diagonal 1-3
1664 return 2; // diagonal 2-4
1671 // Methods of splitting volumes into tetra
1673 const int theHexTo5_1[5*4+1] =
1675 0, 1, 2, 5, 0, 4, 5, 7, 0, 2, 3, 7, 2, 5, 6, 7, 0, 5, 2, 7, -1
1677 const int theHexTo5_2[5*4+1] =
1679 1, 2, 3, 6, 1, 4, 5, 6, 0, 1, 3, 4, 3, 4, 6, 7, 1, 3, 4, 6, -1
1681 const int* theHexTo5[2] = { theHexTo5_1, theHexTo5_2 };
1683 const int theHexTo6_1[6*4+1] =
1685 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
1687 const int theHexTo6_2[6*4+1] =
1689 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
1691 const int theHexTo6_3[6*4+1] =
1693 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
1695 const int theHexTo6_4[6*4+1] =
1697 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
1699 const int* theHexTo6[4] = { theHexTo6_1, theHexTo6_2, theHexTo6_3, theHexTo6_4 };
1701 const int thePyraTo2_1[2*4+1] =
1703 0, 1, 2, 4, 0, 2, 3, 4, -1
1705 const int thePyraTo2_2[2*4+1] =
1707 1, 2, 3, 4, 1, 3, 0, 4, -1
1709 const int* thePyraTo2[2] = { thePyraTo2_1, thePyraTo2_2 };
1711 const int thePentaTo3_1[3*4+1] =
1713 0, 1, 2, 3, 1, 3, 4, 2, 2, 3, 4, 5, -1
1715 const int thePentaTo3_2[3*4+1] =
1717 1, 2, 0, 4, 2, 4, 5, 0, 0, 4, 5, 3, -1
1719 const int thePentaTo3_3[3*4+1] =
1721 2, 0, 1, 5, 0, 5, 3, 1, 1, 5, 3, 4, -1
1723 const int thePentaTo3_4[3*4+1] =
1725 0, 1, 2, 3, 1, 3, 4, 5, 2, 3, 1, 5, -1
1727 const int thePentaTo3_5[3*4+1] =
1729 1, 2, 0, 4, 2, 4, 5, 3, 0, 4, 2, 3, -1
1731 const int thePentaTo3_6[3*4+1] =
1733 2, 0, 1, 5, 0, 5, 3, 4, 1, 5, 0, 4, -1
1735 const int* thePentaTo3[6] = { thePentaTo3_1, thePentaTo3_2, thePentaTo3_3,
1736 thePentaTo3_4, thePentaTo3_5, thePentaTo3_6 };
1738 // Methods of splitting hexahedron into prisms
1740 const int theHexTo4Prisms_BT[6*4+1] = // bottom-top
1742 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
1744 const int theHexTo4Prisms_LR[6*4+1] = // left-right
1746 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
1748 const int theHexTo4Prisms_FB[6*4+1] = // front-back
1750 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
1753 const int theHexTo2Prisms_BT_1[6*2+1] =
1755 0, 1, 3, 4, 5, 7, 1, 2, 3, 5, 6, 7, -1
1757 const int theHexTo2Prisms_BT_2[6*2+1] =
1759 0, 1, 2, 4, 5, 6, 0, 2, 3, 4, 6, 7, -1
1761 const int* theHexTo2Prisms_BT[2] = { theHexTo2Prisms_BT_1, theHexTo2Prisms_BT_2 };
1763 const int theHexTo2Prisms_LR_1[6*2+1] =
1765 1, 0, 4, 2, 3, 7, 1, 4, 5, 2, 7, 6, -1
1767 const int theHexTo2Prisms_LR_2[6*2+1] =
1769 1, 0, 4, 2, 3, 7, 1, 4, 5, 2, 7, 6, -1
1771 const int* theHexTo2Prisms_LR[2] = { theHexTo2Prisms_LR_1, theHexTo2Prisms_LR_2 };
1773 const int theHexTo2Prisms_FB_1[6*2+1] =
1775 0, 3, 4, 1, 2, 5, 3, 7, 4, 2, 6, 5, -1
1777 const int theHexTo2Prisms_FB_2[6*2+1] =
1779 0, 3, 7, 1, 2, 7, 0, 7, 4, 1, 6, 5, -1
1781 const int* theHexTo2Prisms_FB[2] = { theHexTo2Prisms_FB_1, theHexTo2Prisms_FB_2 };
1784 struct TTriangleFacet //!< stores indices of three nodes of tetra facet
1787 TTriangleFacet(int n1, int n2, int n3): _n1(n1), _n2(n2), _n3(n3) {}
1788 bool contains(int n) const { return ( n == _n1 || n == _n2 || n == _n3 ); }
1789 bool hasAdjacentVol( const SMDS_MeshElement* elem,
1790 const SMDSAbs_GeometryType geom = SMDSGeom_TETRA) const;
1796 const int* _connectivity; //!< foursomes of tetra connectivy finished by -1
1797 bool _baryNode; //!< additional node is to be created at cell barycenter
1798 bool _ownConn; //!< to delete _connectivity in destructor
1799 map<int, const SMDS_MeshNode*> _faceBaryNode; //!< map face index to node at BC of face
1801 TSplitMethod( int nbTet=0, const int* conn=0, bool addNode=false)
1802 : _nbSplits(nbTet), _nbCorners(4), _connectivity(conn), _baryNode(addNode), _ownConn(false) {}
1803 ~TSplitMethod() { if ( _ownConn ) delete [] _connectivity; _connectivity = 0; }
1804 bool hasFacet( const TTriangleFacet& facet ) const
1806 if ( _nbCorners == 4 )
1808 const int* tetConn = _connectivity;
1809 for ( ; tetConn[0] >= 0; tetConn += 4 )
1810 if (( facet.contains( tetConn[0] ) +
1811 facet.contains( tetConn[1] ) +
1812 facet.contains( tetConn[2] ) +
1813 facet.contains( tetConn[3] )) == 3 )
1816 else // prism, _nbCorners == 6
1818 const int* prismConn = _connectivity;
1819 for ( ; prismConn[0] >= 0; prismConn += 6 )
1821 if (( facet.contains( prismConn[0] ) &&
1822 facet.contains( prismConn[1] ) &&
1823 facet.contains( prismConn[2] ))
1825 ( facet.contains( prismConn[3] ) &&
1826 facet.contains( prismConn[4] ) &&
1827 facet.contains( prismConn[5] )))
1835 //=======================================================================
1837 * \brief return TSplitMethod for the given element to split into tetrahedra
1839 //=======================================================================
1841 TSplitMethod getTetraSplitMethod( SMDS_VolumeTool& vol, const int theMethodFlags)
1843 const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
1845 // at HEXA_TO_24 method, each face of volume is split into triangles each based on
1846 // an edge and a face barycenter; tertaherdons are based on triangles and
1847 // a volume barycenter
1848 const bool is24TetMode = ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_24 );
1850 // Find out how adjacent volumes are split
1852 vector < list< TTriangleFacet > > triaSplitsByFace( vol.NbFaces() ); // splits of each side
1853 int hasAdjacentSplits = 0, maxTetConnSize = 0;
1854 for ( int iF = 0; iF < vol.NbFaces(); ++iF )
1856 int nbNodes = vol.NbFaceNodes( iF ) / iQ;
1857 maxTetConnSize += 4 * ( nbNodes - (is24TetMode ? 0 : 2));
1858 if ( nbNodes < 4 ) continue;
1860 list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
1861 const int* nInd = vol.GetFaceNodesIndices( iF );
1864 TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
1865 TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
1866 if ( t012.hasAdjacentVol( vol.Element() )) triaSplits.push_back( t012 );
1867 else if ( t123.hasAdjacentVol( vol.Element() )) triaSplits.push_back( t123 );
1871 int iCom = 0; // common node of triangle faces to split into
1872 for ( int iVar = 0; iVar < nbNodes; ++iVar, ++iCom )
1874 TTriangleFacet t012( nInd[ iQ * ( iCom )],
1875 nInd[ iQ * ( (iCom+1)%nbNodes )],
1876 nInd[ iQ * ( (iCom+2)%nbNodes )]);
1877 TTriangleFacet t023( nInd[ iQ * ( iCom )],
1878 nInd[ iQ * ( (iCom+2)%nbNodes )],
1879 nInd[ iQ * ( (iCom+3)%nbNodes )]);
1880 if ( t012.hasAdjacentVol( vol.Element() ) && t023.hasAdjacentVol( vol.Element() ))
1882 triaSplits.push_back( t012 );
1883 triaSplits.push_back( t023 );
1888 if ( !triaSplits.empty() )
1889 hasAdjacentSplits = true;
1892 // Among variants of split method select one compliant with adjacent volumes
1894 TSplitMethod method;
1895 if ( !vol.Element()->IsPoly() && !is24TetMode )
1897 int nbVariants = 2, nbTet = 0;
1898 const int** connVariants = 0;
1899 switch ( vol.Element()->GetEntityType() )
1901 case SMDSEntity_Hexa:
1902 case SMDSEntity_Quad_Hexa:
1903 case SMDSEntity_TriQuad_Hexa:
1904 if ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_5 )
1905 connVariants = theHexTo5, nbTet = 5;
1907 connVariants = theHexTo6, nbTet = 6, nbVariants = 4;
1909 case SMDSEntity_Pyramid:
1910 case SMDSEntity_Quad_Pyramid:
1911 connVariants = thePyraTo2; nbTet = 2;
1913 case SMDSEntity_Penta:
1914 case SMDSEntity_Quad_Penta:
1915 connVariants = thePentaTo3; nbTet = 3; nbVariants = 6;
1920 for ( int variant = 0; variant < nbVariants && method._nbSplits == 0; ++variant )
1922 // check method compliancy with adjacent tetras,
1923 // all found splits must be among facets of tetras described by this method
1924 method = TSplitMethod( nbTet, connVariants[variant] );
1925 if ( hasAdjacentSplits && method._nbSplits > 0 )
1927 bool facetCreated = true;
1928 for ( int iF = 0; facetCreated && iF < triaSplitsByFace.size(); ++iF )
1930 list< TTriangleFacet >::const_iterator facet = triaSplitsByFace[iF].begin();
1931 for ( ; facetCreated && facet != triaSplitsByFace[iF].end(); ++facet )
1932 facetCreated = method.hasFacet( *facet );
1934 if ( !facetCreated )
1935 method = TSplitMethod(0); // incompatible method
1939 if ( method._nbSplits < 1 )
1941 // No standard method is applicable, use a generic solution:
1942 // each facet of a volume is split into triangles and
1943 // each of triangles and a volume barycenter form a tetrahedron.
1945 const bool isHex27 = ( vol.Element()->GetEntityType() == SMDSEntity_TriQuad_Hexa );
1947 int* connectivity = new int[ maxTetConnSize + 1 ];
1948 method._connectivity = connectivity;
1949 method._ownConn = true;
1950 method._baryNode = !isHex27; // to create central node or not
1953 int baryCenInd = vol.NbNodes() - int( isHex27 );
1954 for ( int iF = 0; iF < vol.NbFaces(); ++iF )
1956 const int nbNodes = vol.NbFaceNodes( iF ) / iQ;
1957 const int* nInd = vol.GetFaceNodesIndices( iF );
1958 // find common node of triangle facets of tetra to create
1959 int iCommon = 0; // index in linear numeration
1960 const list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
1961 if ( !triaSplits.empty() )
1964 const TTriangleFacet* facet = &triaSplits.front();
1965 for ( ; iCommon < nbNodes-1 ; ++iCommon )
1966 if ( facet->contains( nInd[ iQ * iCommon ]) &&
1967 facet->contains( nInd[ iQ * ((iCommon+2)%nbNodes) ]))
1970 else if ( nbNodes > 3 && !is24TetMode )
1972 // find the best method of splitting into triangles by aspect ratio
1973 SMESH::Controls::NumericalFunctorPtr aspectRatio( new SMESH::Controls::AspectRatio);
1974 map< double, int > badness2iCommon;
1975 const SMDS_MeshNode** nodes = vol.GetFaceNodes( iF );
1976 int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
1977 for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCommon )
1980 for ( int iLast = iCommon+2; iLast < iCommon+nbNodes; ++iLast )
1982 SMDS_FaceOfNodes tria ( nodes[ iQ*( iCommon )],
1983 nodes[ iQ*((iLast-1)%nbNodes)],
1984 nodes[ iQ*((iLast )%nbNodes)]);
1985 badness += getBadRate( &tria, aspectRatio );
1987 badness2iCommon.insert( make_pair( badness, iCommon ));
1989 // use iCommon with lowest badness
1990 iCommon = badness2iCommon.begin()->second;
1992 if ( iCommon >= nbNodes )
1993 iCommon = 0; // something wrong
1995 // fill connectivity of tetrahedra based on a current face
1996 int nbTet = nbNodes - 2;
1997 if ( is24TetMode && nbNodes > 3 && triaSplits.empty())
2002 faceBaryCenInd = vol.GetCenterNodeIndex( iF );
2003 method._faceBaryNode[ iF ] = vol.GetNodes()[ faceBaryCenInd ];
2007 method._faceBaryNode[ iF ] = 0;
2008 faceBaryCenInd = baryCenInd + method._faceBaryNode.size();
2011 for ( int i = 0; i < nbTet; ++i )
2013 int i1 = i, i2 = (i+1) % nbNodes;
2014 if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
2015 connectivity[ connSize++ ] = nInd[ iQ * i1 ];
2016 connectivity[ connSize++ ] = nInd[ iQ * i2 ];
2017 connectivity[ connSize++ ] = faceBaryCenInd;
2018 connectivity[ connSize++ ] = baryCenInd;
2023 for ( int i = 0; i < nbTet; ++i )
2025 int i1 = (iCommon+1+i) % nbNodes, i2 = (iCommon+2+i) % nbNodes;
2026 if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
2027 connectivity[ connSize++ ] = nInd[ iQ * iCommon ];
2028 connectivity[ connSize++ ] = nInd[ iQ * i1 ];
2029 connectivity[ connSize++ ] = nInd[ iQ * i2 ];
2030 connectivity[ connSize++ ] = baryCenInd;
2033 method._nbSplits += nbTet;
2035 } // loop on volume faces
2037 connectivity[ connSize++ ] = -1;
2039 } // end of generic solution
2043 //=======================================================================
2045 * \brief return TSplitMethod to split haxhedron into prisms
2047 //=======================================================================
2049 TSplitMethod getPrismSplitMethod( SMDS_VolumeTool& vol,
2050 const int methodFlags,
2051 const int facetToSplit)
2053 // order of facets in HEX according to SMDS_VolumeTool::Hexa_F :
2055 const int iF = ( facetToSplit < 2 ) ? 0 : 1 + ( facetToSplit-2 ) % 2; // [0,1,2]
2057 if ( methodFlags == SMESH_MeshEditor::HEXA_TO_4_PRISMS )
2059 static TSplitMethod to4methods[4]; // order BT, LR, FB
2060 if ( to4methods[iF]._nbSplits == 0 )
2064 to4methods[iF]._connectivity = theHexTo4Prisms_BT;
2065 to4methods[iF]._faceBaryNode[ 0 ] = 0;
2066 to4methods[iF]._faceBaryNode[ 1 ] = 0;
2069 to4methods[iF]._connectivity = theHexTo4Prisms_LR;
2070 to4methods[iF]._faceBaryNode[ 2 ] = 0;
2071 to4methods[iF]._faceBaryNode[ 4 ] = 0;
2074 to4methods[iF]._connectivity = theHexTo4Prisms_FB;
2075 to4methods[iF]._faceBaryNode[ 3 ] = 0;
2076 to4methods[iF]._faceBaryNode[ 5 ] = 0;
2078 default: return to4methods[3];
2080 to4methods[iF]._nbSplits = 4;
2081 to4methods[iF]._nbCorners = 6;
2083 return to4methods[iF];
2085 // else if ( methodFlags == HEXA_TO_2_PRISMS )
2087 TSplitMethod method;
2089 const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
2091 const int nbVariants = 2, nbSplits = 2;
2092 const int** connVariants = 0;
2094 case 0: connVariants = theHexTo2Prisms_BT; break;
2095 case 1: connVariants = theHexTo2Prisms_LR; break;
2096 case 2: connVariants = theHexTo2Prisms_FB; break;
2097 default: return method;
2100 // look for prisms adjacent via facetToSplit and an opposite one
2101 for ( int is2nd = 0; is2nd < 2; ++is2nd )
2103 int iFacet = is2nd ? vol.GetOppFaceIndexOfHex( facetToSplit ) : facetToSplit;
2104 int nbNodes = vol.NbFaceNodes( iFacet ) / iQ;
2105 if ( nbNodes != 4 ) return method;
2107 const int* nInd = vol.GetFaceNodesIndices( iFacet );
2108 TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
2109 TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
2111 if ( t012.hasAdjacentVol( vol.Element(), SMDSGeom_PENTA ))
2113 else if ( t123.hasAdjacentVol( vol.Element(), SMDSGeom_PENTA ))
2118 // there are adjacent prism
2119 for ( int variant = 0; variant < nbVariants; ++variant )
2121 // check method compliancy with adjacent prisms,
2122 // the found prism facets must be among facets of prisms described by current method
2123 method._nbSplits = nbSplits;
2124 method._nbCorners = 6;
2125 method._connectivity = connVariants[ variant ];
2126 if ( method.hasFacet( *t ))
2131 // No adjacent prisms. Select a variant with a best aspect ratio.
2133 double badness[2] = { 0, 0 };
2134 static SMESH::Controls::NumericalFunctorPtr aspectRatio( new SMESH::Controls::AspectRatio);
2135 const SMDS_MeshNode** nodes = vol.GetNodes();
2136 for ( int variant = 0; variant < nbVariants; ++variant )
2137 for ( int is2nd = 0; is2nd < 2; ++is2nd )
2139 int iFacet = is2nd ? vol.GetOppFaceIndexOfHex( facetToSplit ) : facetToSplit;
2140 const int* nInd = vol.GetFaceNodesIndices( iFacet );
2142 method._connectivity = connVariants[ variant ];
2143 TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
2144 TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
2145 TTriangleFacet* t = ( method.hasFacet( t012 )) ? & t012 : & t123;
2147 SMDS_FaceOfNodes tria ( nodes[ t->_n1 ],
2150 badness[ variant ] += getBadRate( &tria, aspectRatio );
2152 const int iBetter = ( badness[1] < badness[0] && badness[0]-badness[1] > 0.1 * badness[0] );
2154 method._nbSplits = nbSplits;
2155 method._nbCorners = 6;
2156 method._connectivity = connVariants[ iBetter ];
2161 //================================================================================
2163 * \brief Check if there is a tetraherdon adjacent to the given element via this facet
2165 //================================================================================
2167 bool TTriangleFacet::hasAdjacentVol( const SMDS_MeshElement* elem,
2168 const SMDSAbs_GeometryType geom ) const
2170 // find the tetrahedron including the three nodes of facet
2171 const SMDS_MeshNode* n1 = elem->GetNode(_n1);
2172 const SMDS_MeshNode* n2 = elem->GetNode(_n2);
2173 const SMDS_MeshNode* n3 = elem->GetNode(_n3);
2174 SMDS_ElemIteratorPtr volIt1 = n1->GetInverseElementIterator(SMDSAbs_Volume);
2175 while ( volIt1->more() )
2177 const SMDS_MeshElement* v = volIt1->next();
2178 if ( v->GetGeomType() != geom )
2180 const int lastCornerInd = v->NbCornerNodes() - 1;
2181 if ( v->IsQuadratic() && v->GetNodeIndex( n1 ) > lastCornerInd )
2182 continue; // medium node not allowed
2183 const int ind2 = v->GetNodeIndex( n2 );
2184 if ( ind2 < 0 || lastCornerInd < ind2 )
2186 const int ind3 = v->GetNodeIndex( n3 );
2187 if ( ind3 < 0 || lastCornerInd < ind3 )
2194 //=======================================================================
2196 * \brief A key of a face of volume
2198 //=======================================================================
2200 struct TVolumeFaceKey: pair< pair< int, int>, pair< int, int> >
2202 TVolumeFaceKey( SMDS_VolumeTool& vol, int iF )
2204 TIDSortedNodeSet sortedNodes;
2205 const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
2206 int nbNodes = vol.NbFaceNodes( iF );
2207 const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iF );
2208 for ( int i = 0; i < nbNodes; i += iQ )
2209 sortedNodes.insert( fNodes[i] );
2210 TIDSortedNodeSet::iterator n = sortedNodes.begin();
2211 first.first = (*(n++))->GetID();
2212 first.second = (*(n++))->GetID();
2213 second.first = (*(n++))->GetID();
2214 second.second = ( sortedNodes.size() > 3 ) ? (*(n++))->GetID() : 0;
2219 //=======================================================================
2220 //function : SplitVolumes
2221 //purpose : Split volume elements into tetrahedra or prisms.
2222 // If facet ID < 0, element is split into tetrahedra,
2223 // else a hexahedron is split into prisms so that the given facet is
2224 // split into triangles
2225 //=======================================================================
2227 void SMESH_MeshEditor::SplitVolumes (const TFacetOfElem & theElems,
2228 const int theMethodFlags)
2230 SMDS_VolumeTool volTool;
2231 SMESH_MesherHelper helper( *GetMesh()), fHelper(*GetMesh());
2232 fHelper.ToFixNodeParameters( true );
2234 SMESHDS_SubMesh* subMesh = 0;//GetMeshDS()->MeshElements(1);
2235 SMESHDS_SubMesh* fSubMesh = 0;//subMesh;
2237 SMESH_SequenceOfElemPtr newNodes, newElems;
2239 // map face of volume to it's baricenrtic node
2240 map< TVolumeFaceKey, const SMDS_MeshNode* > volFace2BaryNode;
2243 TFacetOfElem::const_iterator elem2facet = theElems.begin();
2244 for ( ; elem2facet != theElems.end(); ++elem2facet )
2246 const SMDS_MeshElement* elem = elem2facet->first;
2247 const int facetToSplit = elem2facet->second;
2248 if ( elem->GetType() != SMDSAbs_Volume )
2250 const SMDSAbs_EntityType geomType = elem->GetEntityType();
2251 if ( geomType == SMDSEntity_Tetra || geomType == SMDSEntity_Quad_Tetra )
2254 if ( !volTool.Set( elem, /*ignoreCentralNodes=*/false )) continue; // strange...
2256 TSplitMethod splitMethod = ( facetToSplit < 0 ?
2257 getTetraSplitMethod( volTool, theMethodFlags ) :
2258 getPrismSplitMethod( volTool, theMethodFlags, facetToSplit ));
2259 if ( splitMethod._nbSplits < 1 ) continue;
2261 // find submesh to add new tetras to
2262 if ( !subMesh || !subMesh->Contains( elem ))
2264 int shapeID = FindShape( elem );
2265 helper.SetSubShape( shapeID ); // helper will add tetras to the found submesh
2266 subMesh = GetMeshDS()->MeshElements( shapeID );
2269 if ( elem->IsQuadratic() )
2272 // add quadratic links to the helper
2273 for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
2275 const SMDS_MeshNode** fNodes = volTool.GetFaceNodes( iF );
2276 int nbN = volTool.NbFaceNodes( iF ) - bool( volTool.GetCenterNodeIndex(iF) > 0 );
2277 for ( int iN = 0; iN < nbN; iN += iQ )
2278 helper.AddTLinkNode( fNodes[iN], fNodes[iN+2], fNodes[iN+1] );
2280 helper.SetIsQuadratic( true );
2285 helper.SetIsQuadratic( false );
2287 vector<const SMDS_MeshNode*> nodes( volTool.GetNodes(),
2288 volTool.GetNodes() + elem->NbNodes() );
2289 helper.SetElementsOnShape( true );
2290 if ( splitMethod._baryNode )
2292 // make a node at barycenter
2293 volTool.GetBaryCenter( bc[0], bc[1], bc[2] );
2294 SMDS_MeshNode* gcNode = helper.AddNode( bc[0], bc[1], bc[2] );
2295 nodes.push_back( gcNode );
2296 newNodes.Append( gcNode );
2298 if ( !splitMethod._faceBaryNode.empty() )
2300 // make or find baricentric nodes of faces
2301 map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.begin();
2302 for ( ; iF_n != splitMethod._faceBaryNode.end(); ++iF_n )
2304 map< TVolumeFaceKey, const SMDS_MeshNode* >::iterator f_n =
2305 volFace2BaryNode.insert
2306 ( make_pair( TVolumeFaceKey( volTool,iF_n->first ), iF_n->second )).first;
2309 volTool.GetFaceBaryCenter( iF_n->first, bc[0], bc[1], bc[2] );
2310 newNodes.Append( f_n->second = helper.AddNode( bc[0], bc[1], bc[2] ));
2312 nodes.push_back( iF_n->second = f_n->second );
2317 vector<const SMDS_MeshElement* > splitVols( splitMethod._nbSplits ); // splits of a volume
2318 const int* volConn = splitMethod._connectivity;
2319 if ( splitMethod._nbCorners == 4 ) // tetra
2320 for ( int i = 0; i < splitMethod._nbSplits; ++i, volConn += splitMethod._nbCorners )
2321 newElems.Append( splitVols[ i ] = helper.AddVolume( nodes[ volConn[0] ],
2322 nodes[ volConn[1] ],
2323 nodes[ volConn[2] ],
2324 nodes[ volConn[3] ]));
2326 for ( int i = 0; i < splitMethod._nbSplits; ++i, volConn += splitMethod._nbCorners )
2327 newElems.Append( splitVols[ i ] = helper.AddVolume( nodes[ volConn[0] ],
2328 nodes[ volConn[1] ],
2329 nodes[ volConn[2] ],
2330 nodes[ volConn[3] ],
2331 nodes[ volConn[4] ],
2332 nodes[ volConn[5] ]));
2334 ReplaceElemInGroups( elem, splitVols, GetMeshDS() );
2336 // Split faces on sides of the split volume
2338 const SMDS_MeshNode** volNodes = volTool.GetNodes();
2339 for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
2341 const int nbNodes = volTool.NbFaceNodes( iF ) / iQ;
2342 if ( nbNodes < 4 ) continue;
2344 // find an existing face
2345 vector<const SMDS_MeshNode*> fNodes( volTool.GetFaceNodes( iF ),
2346 volTool.GetFaceNodes( iF ) + volTool.NbFaceNodes( iF ));
2347 while ( const SMDS_MeshElement* face = GetMeshDS()->FindElement( fNodes, SMDSAbs_Face,
2348 /*noMedium=*/false))
2351 helper.SetElementsOnShape( false );
2352 vector< const SMDS_MeshElement* > triangles;
2354 // find submesh to add new triangles in
2355 if ( !fSubMesh || !fSubMesh->Contains( face ))
2357 int shapeID = FindShape( face );
2358 fSubMesh = GetMeshDS()->MeshElements( shapeID );
2360 map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.find(iF);
2361 if ( iF_n != splitMethod._faceBaryNode.end() )
2363 const SMDS_MeshNode *baryNode = iF_n->second;
2364 for ( int iN = 0; iN < nbNodes*iQ; iN += iQ )
2366 const SMDS_MeshNode* n1 = fNodes[iN];
2367 const SMDS_MeshNode *n2 = fNodes[(iN+iQ)%(nbNodes*iQ)];
2368 const SMDS_MeshNode *n3 = baryNode;
2369 if ( !volTool.IsFaceExternal( iF ))
2371 triangles.push_back( helper.AddFace( n1,n2,n3 ));
2373 if ( fSubMesh ) // update position of the bary node on geometry
2376 subMesh->RemoveNode( baryNode, false );
2377 GetMeshDS()->SetNodeOnFace( baryNode, fSubMesh->GetID() );
2378 const TopoDS_Shape& s = GetMeshDS()->IndexToShape( fSubMesh->GetID() );
2379 if ( !s.IsNull() && s.ShapeType() == TopAbs_FACE )
2381 fHelper.SetSubShape( s );
2382 gp_XY uv( 1e100, 1e100 );
2384 if ( !fHelper.CheckNodeUV( TopoDS::Face( s ), baryNode,
2385 uv, /*tol=*/1e-7, /*force=*/true, distXYZ ) &&
2388 // node is too far from the surface
2389 GetMeshDS()->MoveNode( baryNode, distXYZ[1], distXYZ[2], distXYZ[3] );
2390 const_cast<SMDS_MeshNode*>( baryNode )->SetPosition
2391 ( SMDS_PositionPtr( new SMDS_FacePosition( uv.X(), uv.Y() )));
2398 // among possible triangles create ones discribed by split method
2399 const int* nInd = volTool.GetFaceNodesIndices( iF );
2400 int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
2401 int iCom = 0; // common node of triangle faces to split into
2402 list< TTriangleFacet > facets;
2403 for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCom )
2405 TTriangleFacet t012( nInd[ iQ * ( iCom )],
2406 nInd[ iQ * ( (iCom+1)%nbNodes )],
2407 nInd[ iQ * ( (iCom+2)%nbNodes )]);
2408 TTriangleFacet t023( nInd[ iQ * ( iCom )],
2409 nInd[ iQ * ( (iCom+2)%nbNodes )],
2410 nInd[ iQ * ( (iCom+3)%nbNodes )]);
2411 if ( splitMethod.hasFacet( t012 ) && splitMethod.hasFacet( t023 ))
2413 facets.push_back( t012 );
2414 facets.push_back( t023 );
2415 for ( int iLast = iCom+4; iLast < iCom+nbNodes; ++iLast )
2416 facets.push_back( TTriangleFacet( nInd[ iQ * ( iCom )],
2417 nInd[ iQ * ((iLast-1)%nbNodes )],
2418 nInd[ iQ * ((iLast )%nbNodes )]));
2422 list< TTriangleFacet >::iterator facet = facets.begin();
2423 if ( facet == facets.end() )
2425 for ( ; facet != facets.end(); ++facet )
2427 if ( !volTool.IsFaceExternal( iF ))
2428 swap( facet->_n2, facet->_n3 );
2429 triangles.push_back( helper.AddFace( volNodes[ facet->_n1 ],
2430 volNodes[ facet->_n2 ],
2431 volNodes[ facet->_n3 ]));
2434 for ( int i = 0; i < triangles.size(); ++i )
2436 if ( !triangles[i] ) continue;
2438 fSubMesh->AddElement( triangles[i]);
2439 newElems.Append( triangles[i] );
2441 ReplaceElemInGroups( face, triangles, GetMeshDS() );
2442 GetMeshDS()->RemoveFreeElement( face, fSubMesh, /*fromGroups=*/false );
2444 } // while a face based on facet nodes exists
2445 } // loop on volume faces to split them into triangles
2447 GetMeshDS()->RemoveFreeElement( elem, subMesh, /*fromGroups=*/false );
2449 if ( geomType == SMDSEntity_TriQuad_Hexa )
2451 // remove medium nodes that could become free
2452 for ( int i = 20; i < volTool.NbNodes(); ++i )
2453 if ( volNodes[i]->NbInverseElements() == 0 )
2454 GetMeshDS()->RemoveNode( volNodes[i] );
2456 } // loop on volumes to split
2458 myLastCreatedNodes = newNodes;
2459 myLastCreatedElems = newElems;
2462 //=======================================================================
2463 //function : GetHexaFacetsToSplit
2464 //purpose : For hexahedra that will be split into prisms, finds facets to
2465 // split into triangles. Only hexahedra adjacent to the one closest
2466 // to theFacetNormal.Location() are returned.
2467 //param [in,out] theHexas - the hexahedra
2468 //param [in] theFacetNormal - facet normal
2469 //param [out] theFacets - the hexahedra and found facet IDs
2470 //=======================================================================
2472 void SMESH_MeshEditor::GetHexaFacetsToSplit( TIDSortedElemSet& theHexas,
2473 const gp_Ax1& theFacetNormal,
2474 TFacetOfElem & theFacets)
2476 #define THIS_METHOD "SMESH_MeshEditor::GetHexaFacetsToSplit(): "
2478 // Find a hexa closest to the location of theFacetNormal
2480 const SMDS_MeshElement* startHex;
2482 // get SMDS_ElemIteratorPtr on theHexas
2483 typedef const SMDS_MeshElement* TValue;
2484 typedef TIDSortedElemSet::iterator TSetIterator;
2485 typedef SMDS::SimpleAccessor<TValue,TSetIterator> TAccesor;
2486 typedef SMDS_MeshElement::GeomFilter TFilter;
2487 typedef SMDS_SetIterator < TValue, TSetIterator, TAccesor, TFilter > TElemSetIter;
2488 SMDS_ElemIteratorPtr elemIt = SMDS_ElemIteratorPtr
2489 ( new TElemSetIter( theHexas.begin(),
2491 SMDS_MeshElement::GeomFilter( SMDSGeom_HEXA )));
2493 SMESH_ElementSearcher* searcher =
2494 SMESH_MeshAlgos::GetElementSearcher( *myMesh->GetMeshDS(), elemIt );
2496 startHex = searcher->FindClosestTo( theFacetNormal.Location(), SMDSAbs_Volume );
2501 throw SALOME_Exception( THIS_METHOD "startHex not found");
2504 // Select a facet of startHex by theFacetNormal
2506 SMDS_VolumeTool vTool( startHex );
2507 double norm[3], dot, maxDot = 0;
2509 for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2510 if ( vTool.GetFaceNormal( iF, norm[0], norm[1], norm[2] ))
2512 dot = Abs( theFacetNormal.Direction().Dot( gp_Dir( norm[0], norm[1], norm[2] )));
2520 throw SALOME_Exception( THIS_METHOD "facet of startHex not found");
2522 // Fill theFacets starting from facetID of startHex
2524 // facets used for seach of volumes adjacent to already treated ones
2525 typedef pair< TFacetOfElem::iterator, int > TElemFacets;
2526 typedef map< TVolumeFaceKey, TElemFacets > TFacetMap;
2527 TFacetMap facetsToCheck;
2529 set<const SMDS_MeshNode*> facetNodes;
2530 const SMDS_MeshElement* curHex;
2532 const bool allHex = ( theHexas.size() == myMesh->NbHexas() );
2536 // move in two directions from startHex via facetID
2537 for ( int is2nd = 0; is2nd < 2; ++is2nd )
2540 int curFacet = facetID;
2541 if ( is2nd ) // do not treat startHex twice
2543 vTool.Set( curHex );
2544 if ( vTool.IsFreeFace( curFacet, &curHex ))
2550 vTool.GetFaceNodes( curFacet, facetNodes );
2551 vTool.Set( curHex );
2552 curFacet = vTool.GetFaceIndex( facetNodes );
2557 // store a facet to split
2558 if ( curHex->GetGeomType() != SMDSGeom_HEXA )
2560 theFacets.insert( make_pair( curHex, -1 ));
2563 if ( !allHex && !theHexas.count( curHex ))
2566 pair< TFacetOfElem::iterator, bool > facetIt2isNew =
2567 theFacets.insert( make_pair( curHex, curFacet ));
2568 if ( !facetIt2isNew.second )
2571 // remember not-to-split facets in facetsToCheck
2572 int oppFacet = vTool.GetOppFaceIndexOfHex( curFacet );
2573 for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2575 if ( iF == curFacet && iF == oppFacet )
2577 TVolumeFaceKey facetKey ( vTool, iF );
2578 TElemFacets elemFacet( facetIt2isNew.first, iF );
2579 pair< TFacetMap::iterator, bool > it2isnew =
2580 facetsToCheck.insert( make_pair( facetKey, elemFacet ));
2581 if ( !it2isnew.second )
2582 facetsToCheck.erase( it2isnew.first ); // adjacent hex already checked
2584 // pass to a volume adjacent via oppFacet
2585 if ( vTool.IsFreeFace( oppFacet, &curHex ))
2591 // get a new curFacet
2592 vTool.GetFaceNodes( oppFacet, facetNodes );
2593 vTool.Set( curHex );
2594 curFacet = vTool.GetFaceIndex( facetNodes, /*hint=*/curFacet );
2597 } // move in two directions from startHex via facetID
2599 // Find a new startHex by facetsToCheck
2603 TFacetMap::iterator fIt = facetsToCheck.begin();
2604 while ( !startHex && fIt != facetsToCheck.end() )
2606 const TElemFacets& elemFacets = fIt->second;
2607 const SMDS_MeshElement* hex = elemFacets.first->first;
2608 int splitFacet = elemFacets.first->second;
2609 int lateralFacet = elemFacets.second;
2610 facetsToCheck.erase( fIt );
2611 fIt = facetsToCheck.begin();
2614 if ( vTool.IsFreeFace( lateralFacet, &curHex ) ||
2615 curHex->GetGeomType() != SMDSGeom_HEXA )
2617 if ( !allHex && !theHexas.count( curHex ))
2622 // find a facet of startHex to split
2624 set<const SMDS_MeshNode*> lateralNodes;
2625 vTool.GetFaceNodes( lateralFacet, lateralNodes );
2626 vTool.GetFaceNodes( splitFacet, facetNodes );
2627 int oppLateralFacet = vTool.GetOppFaceIndexOfHex( lateralFacet );
2628 vTool.Set( startHex );
2629 lateralFacet = vTool.GetFaceIndex( lateralNodes, oppLateralFacet );
2631 // look for a facet of startHex having common nodes with facetNodes
2632 // but not lateralFacet
2633 for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2635 if ( iF == lateralFacet )
2637 int nbCommonNodes = 0;
2638 const SMDS_MeshNode** nn = vTool.GetFaceNodes( iF );
2639 for ( int iN = 0, nbN = vTool.NbFaceNodes( iF ); iN < nbN; ++iN )
2640 nbCommonNodes += facetNodes.count( nn[ iN ]);
2642 if ( nbCommonNodes >= 2 )
2649 throw SALOME_Exception( THIS_METHOD "facet of a new startHex not found");
2651 } // while ( startHex )
2654 //=======================================================================
2655 //function : AddToSameGroups
2656 //purpose : add elemToAdd to the groups the elemInGroups belongs to
2657 //=======================================================================
2659 void SMESH_MeshEditor::AddToSameGroups (const SMDS_MeshElement* elemToAdd,
2660 const SMDS_MeshElement* elemInGroups,
2661 SMESHDS_Mesh * aMesh)
2663 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2664 if (!groups.empty()) {
2665 set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2666 for ( ; grIt != groups.end(); grIt++ ) {
2667 SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2668 if ( group && group->Contains( elemInGroups ))
2669 group->SMDSGroup().Add( elemToAdd );
2675 //=======================================================================
2676 //function : RemoveElemFromGroups
2677 //purpose : Remove removeelem to the groups the elemInGroups belongs to
2678 //=======================================================================
2679 void SMESH_MeshEditor::RemoveElemFromGroups (const SMDS_MeshElement* removeelem,
2680 SMESHDS_Mesh * aMesh)
2682 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2683 if (!groups.empty())
2685 set<SMESHDS_GroupBase*>::const_iterator GrIt = groups.begin();
2686 for (; GrIt != groups.end(); GrIt++)
2688 SMESHDS_Group* grp = dynamic_cast<SMESHDS_Group*>(*GrIt);
2689 if (!grp || grp->IsEmpty()) continue;
2690 grp->SMDSGroup().Remove(removeelem);
2695 //================================================================================
2697 * \brief Replace elemToRm by elemToAdd in the all groups
2699 //================================================================================
2701 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement* elemToRm,
2702 const SMDS_MeshElement* elemToAdd,
2703 SMESHDS_Mesh * aMesh)
2705 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2706 if (!groups.empty()) {
2707 set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2708 for ( ; grIt != groups.end(); grIt++ ) {
2709 SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2710 if ( group && group->SMDSGroup().Remove( elemToRm ) && elemToAdd )
2711 group->SMDSGroup().Add( elemToAdd );
2716 //================================================================================
2718 * \brief Replace elemToRm by elemToAdd in the all groups
2720 //================================================================================
2722 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement* elemToRm,
2723 const vector<const SMDS_MeshElement*>& elemToAdd,
2724 SMESHDS_Mesh * aMesh)
2726 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2727 if (!groups.empty())
2729 set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2730 for ( ; grIt != groups.end(); grIt++ ) {
2731 SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2732 if ( group && group->SMDSGroup().Remove( elemToRm ) )
2733 for ( int i = 0; i < elemToAdd.size(); ++i )
2734 group->SMDSGroup().Add( elemToAdd[ i ] );
2739 //=======================================================================
2740 //function : QuadToTri
2741 //purpose : Cut quadrangles into triangles.
2742 // theCrit is used to select a diagonal to cut
2743 //=======================================================================
2745 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet & theElems,
2746 const bool the13Diag)
2748 myLastCreatedElems.Clear();
2749 myLastCreatedNodes.Clear();
2751 MESSAGE( "::QuadToTri()" );
2753 SMESHDS_Mesh * aMesh = GetMeshDS();
2755 Handle(Geom_Surface) surface;
2756 SMESH_MesherHelper helper( *GetMesh() );
2758 TIDSortedElemSet::iterator itElem;
2759 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
2760 const SMDS_MeshElement* elem = *itElem;
2761 if ( !elem || elem->GetType() != SMDSAbs_Face )
2763 bool isquad = elem->NbNodes()==4 || elem->NbNodes()==8;
2764 if(!isquad) continue;
2766 if(elem->NbNodes()==4) {
2767 // retrieve element nodes
2768 const SMDS_MeshNode* aNodes [4];
2769 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
2771 while ( itN->more() )
2772 aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
2774 int aShapeId = FindShape( elem );
2775 const SMDS_MeshElement* newElem1 = 0;
2776 const SMDS_MeshElement* newElem2 = 0;
2778 newElem1 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
2779 newElem2 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
2782 newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
2783 newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
2785 myLastCreatedElems.Append(newElem1);
2786 myLastCreatedElems.Append(newElem2);
2787 // put a new triangle on the same shape and add to the same groups
2790 aMesh->SetMeshElementOnShape( newElem1, aShapeId );
2791 aMesh->SetMeshElementOnShape( newElem2, aShapeId );
2793 AddToSameGroups( newElem1, elem, aMesh );
2794 AddToSameGroups( newElem2, elem, aMesh );
2795 //aMesh->RemoveFreeElement(elem, aMesh->MeshElements(aShapeId), true);
2796 aMesh->RemoveElement( elem );
2799 // Quadratic quadrangle
2801 if( elem->NbNodes()==8 && elem->IsQuadratic() ) {
2803 // get surface elem is on
2804 int aShapeId = FindShape( elem );
2805 if ( aShapeId != helper.GetSubShapeID() ) {
2809 shape = aMesh->IndexToShape( aShapeId );
2810 if ( !shape.IsNull() && shape.ShapeType() == TopAbs_FACE ) {
2811 TopoDS_Face face = TopoDS::Face( shape );
2812 surface = BRep_Tool::Surface( face );
2813 if ( !surface.IsNull() )
2814 helper.SetSubShape( shape );
2818 const SMDS_MeshNode* aNodes [8];
2819 const SMDS_MeshNode* inFaceNode = 0;
2820 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
2822 while ( itN->more() ) {
2823 aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
2824 if ( !inFaceNode && helper.GetNodeUVneedInFaceNode() &&
2825 aNodes[ i-1 ]->GetPosition()->GetTypeOfPosition() == SMDS_TOP_FACE )
2827 inFaceNode = aNodes[ i-1 ];
2831 // find middle point for (0,1,2,3)
2832 // and create a node in this point;
2834 if ( surface.IsNull() ) {
2836 p += gp_XYZ(aNodes[i]->X(), aNodes[i]->Y(), aNodes[i]->Z() );
2840 TopoDS_Face geomFace = TopoDS::Face( helper.GetSubShape() );
2843 uv += helper.GetNodeUV( geomFace, aNodes[i], inFaceNode );
2845 p = surface->Value( uv.X(), uv.Y() ).XYZ();
2847 const SMDS_MeshNode* newN = aMesh->AddNode( p.X(), p.Y(), p.Z() );
2848 myLastCreatedNodes.Append(newN);
2850 // create a new element
2851 const SMDS_MeshElement* newElem1 = 0;
2852 const SMDS_MeshElement* newElem2 = 0;
2854 newElem1 = aMesh->AddFace(aNodes[2], aNodes[3], aNodes[0],
2855 aNodes[6], aNodes[7], newN );
2856 newElem2 = aMesh->AddFace(aNodes[2], aNodes[0], aNodes[1],
2857 newN, aNodes[4], aNodes[5] );
2860 newElem1 = aMesh->AddFace(aNodes[3], aNodes[0], aNodes[1],
2861 aNodes[7], aNodes[4], newN );
2862 newElem2 = aMesh->AddFace(aNodes[3], aNodes[1], aNodes[2],
2863 newN, aNodes[5], aNodes[6] );
2865 myLastCreatedElems.Append(newElem1);
2866 myLastCreatedElems.Append(newElem2);
2867 // put a new triangle on the same shape and add to the same groups
2870 aMesh->SetMeshElementOnShape( newElem1, aShapeId );
2871 aMesh->SetMeshElementOnShape( newElem2, aShapeId );
2873 AddToSameGroups( newElem1, elem, aMesh );
2874 AddToSameGroups( newElem2, elem, aMesh );
2875 aMesh->RemoveElement( elem );
2882 //=======================================================================
2883 //function : getAngle
2885 //=======================================================================
2887 double getAngle(const SMDS_MeshElement * tr1,
2888 const SMDS_MeshElement * tr2,
2889 const SMDS_MeshNode * n1,
2890 const SMDS_MeshNode * n2)
2892 double angle = 2. * M_PI; // bad angle
2895 SMESH::Controls::TSequenceOfXYZ P1, P2;
2896 if ( !SMESH::Controls::NumericalFunctor::GetPoints( tr1, P1 ) ||
2897 !SMESH::Controls::NumericalFunctor::GetPoints( tr2, P2 ))
2900 if(!tr1->IsQuadratic())
2901 N1 = gp_Vec( P1(2) - P1(1) ) ^ gp_Vec( P1(3) - P1(1) );
2903 N1 = gp_Vec( P1(3) - P1(1) ) ^ gp_Vec( P1(5) - P1(1) );
2904 if ( N1.SquareMagnitude() <= gp::Resolution() )
2906 if(!tr2->IsQuadratic())
2907 N2 = gp_Vec( P2(2) - P2(1) ) ^ gp_Vec( P2(3) - P2(1) );
2909 N2 = gp_Vec( P2(3) - P2(1) ) ^ gp_Vec( P2(5) - P2(1) );
2910 if ( N2.SquareMagnitude() <= gp::Resolution() )
2913 // find the first diagonal node n1 in the triangles:
2914 // take in account a diagonal link orientation
2915 const SMDS_MeshElement *nFirst[2], *tr[] = { tr1, tr2 };
2916 for ( int t = 0; t < 2; t++ ) {
2917 SMDS_ElemIteratorPtr it = tr[ t ]->nodesIterator();
2918 int i = 0, iDiag = -1;
2919 while ( it->more()) {
2920 const SMDS_MeshElement *n = it->next();
2921 if ( n == n1 || n == n2 ) {
2925 if ( i - iDiag == 1 )
2926 nFirst[ t ] = ( n == n1 ? n2 : n1 );
2935 if ( nFirst[ 0 ] == nFirst[ 1 ] )
2938 angle = N1.Angle( N2 );
2943 // =================================================
2944 // class generating a unique ID for a pair of nodes
2945 // and able to return nodes by that ID
2946 // =================================================
2950 LinkID_Gen( const SMESHDS_Mesh* theMesh )
2951 :myMesh( theMesh ), myMaxID( theMesh->MaxNodeID() + 1)
2954 long GetLinkID (const SMDS_MeshNode * n1,
2955 const SMDS_MeshNode * n2) const
2957 return ( Min(n1->GetID(),n2->GetID()) * myMaxID + Max(n1->GetID(),n2->GetID()));
2960 bool GetNodes (const long theLinkID,
2961 const SMDS_MeshNode* & theNode1,
2962 const SMDS_MeshNode* & theNode2) const
2964 theNode1 = myMesh->FindNode( theLinkID / myMaxID );
2965 if ( !theNode1 ) return false;
2966 theNode2 = myMesh->FindNode( theLinkID % myMaxID );
2967 if ( !theNode2 ) return false;
2973 const SMESHDS_Mesh* myMesh;
2978 //=======================================================================
2979 //function : TriToQuad
2980 //purpose : Fuse neighbour triangles into quadrangles.
2981 // theCrit is used to select a neighbour to fuse with.
2982 // theMaxAngle is a max angle between element normals at which
2983 // fusion is still performed.
2984 //=======================================================================
2986 bool SMESH_MeshEditor::TriToQuad (TIDSortedElemSet & theElems,
2987 SMESH::Controls::NumericalFunctorPtr theCrit,
2988 const double theMaxAngle)
2990 myLastCreatedElems.Clear();
2991 myLastCreatedNodes.Clear();
2993 MESSAGE( "::TriToQuad()" );
2995 if ( !theCrit.get() )
2998 SMESHDS_Mesh * aMesh = GetMeshDS();
3000 // Prepare data for algo: build
3001 // 1. map of elements with their linkIDs
3002 // 2. map of linkIDs with their elements
3004 map< SMESH_TLink, list< const SMDS_MeshElement* > > mapLi_listEl;
3005 map< SMESH_TLink, list< const SMDS_MeshElement* > >::iterator itLE;
3006 map< const SMDS_MeshElement*, set< SMESH_TLink > > mapEl_setLi;
3007 map< const SMDS_MeshElement*, set< SMESH_TLink > >::iterator itEL;
3009 TIDSortedElemSet::iterator itElem;
3010 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
3012 const SMDS_MeshElement* elem = *itElem;
3013 if(!elem || elem->GetType() != SMDSAbs_Face ) continue;
3014 bool IsTria = ( elem->NbCornerNodes()==3 );
3015 if (!IsTria) continue;
3017 // retrieve element nodes
3018 const SMDS_MeshNode* aNodes [4];
3019 SMDS_NodeIteratorPtr itN = elem->nodeIterator();
3022 aNodes[ i++ ] = itN->next();
3023 aNodes[ 3 ] = aNodes[ 0 ];
3026 for ( i = 0; i < 3; i++ ) {
3027 SMESH_TLink link( aNodes[i], aNodes[i+1] );
3028 // check if elements sharing a link can be fused
3029 itLE = mapLi_listEl.find( link );
3030 if ( itLE != mapLi_listEl.end() ) {
3031 if ((*itLE).second.size() > 1 ) // consider only 2 elems adjacent by a link
3033 const SMDS_MeshElement* elem2 = (*itLE).second.front();
3034 //if ( FindShape( elem ) != FindShape( elem2 ))
3035 // continue; // do not fuse triangles laying on different shapes
3036 if ( getAngle( elem, elem2, aNodes[i], aNodes[i+1] ) > theMaxAngle )
3037 continue; // avoid making badly shaped quads
3038 (*itLE).second.push_back( elem );
3041 mapLi_listEl[ link ].push_back( elem );
3043 mapEl_setLi [ elem ].insert( link );
3046 // Clean the maps from the links shared by a sole element, ie
3047 // links to which only one element is bound in mapLi_listEl
3049 for ( itLE = mapLi_listEl.begin(); itLE != mapLi_listEl.end(); itLE++ ) {
3050 int nbElems = (*itLE).second.size();
3051 if ( nbElems < 2 ) {
3052 const SMDS_MeshElement* elem = (*itLE).second.front();
3053 SMESH_TLink link = (*itLE).first;
3054 mapEl_setLi[ elem ].erase( link );
3055 if ( mapEl_setLi[ elem ].empty() )
3056 mapEl_setLi.erase( elem );
3060 // Algo: fuse triangles into quadrangles
3062 while ( ! mapEl_setLi.empty() ) {
3063 // Look for the start element:
3064 // the element having the least nb of shared links
3065 const SMDS_MeshElement* startElem = 0;
3067 for ( itEL = mapEl_setLi.begin(); itEL != mapEl_setLi.end(); itEL++ ) {
3068 int nbLinks = (*itEL).second.size();
3069 if ( nbLinks < minNbLinks ) {
3070 startElem = (*itEL).first;
3071 minNbLinks = nbLinks;
3072 if ( minNbLinks == 1 )
3077 // search elements to fuse starting from startElem or links of elements
3078 // fused earlyer - startLinks
3079 list< SMESH_TLink > startLinks;
3080 while ( startElem || !startLinks.empty() ) {
3081 while ( !startElem && !startLinks.empty() ) {
3082 // Get an element to start, by a link
3083 SMESH_TLink linkId = startLinks.front();
3084 startLinks.pop_front();
3085 itLE = mapLi_listEl.find( linkId );
3086 if ( itLE != mapLi_listEl.end() ) {
3087 list< const SMDS_MeshElement* > & listElem = (*itLE).second;
3088 list< const SMDS_MeshElement* >::iterator itE = listElem.begin();
3089 for ( ; itE != listElem.end() ; itE++ )
3090 if ( mapEl_setLi.find( (*itE) ) != mapEl_setLi.end() )
3092 mapLi_listEl.erase( itLE );
3097 // Get candidates to be fused
3098 const SMDS_MeshElement *tr1 = startElem, *tr2 = 0, *tr3 = 0;
3099 const SMESH_TLink *link12, *link13;
3101 ASSERT( mapEl_setLi.find( tr1 ) != mapEl_setLi.end() );
3102 set< SMESH_TLink >& setLi = mapEl_setLi[ tr1 ];
3103 ASSERT( !setLi.empty() );
3104 set< SMESH_TLink >::iterator itLi;
3105 for ( itLi = setLi.begin(); itLi != setLi.end(); itLi++ )
3107 const SMESH_TLink & link = (*itLi);
3108 itLE = mapLi_listEl.find( link );
3109 if ( itLE == mapLi_listEl.end() )
3112 const SMDS_MeshElement* elem = (*itLE).second.front();
3114 elem = (*itLE).second.back();
3115 mapLi_listEl.erase( itLE );
3116 if ( mapEl_setLi.find( elem ) == mapEl_setLi.end())
3127 // add other links of elem to list of links to re-start from
3128 set< SMESH_TLink >& links = mapEl_setLi[ elem ];
3129 set< SMESH_TLink >::iterator it;
3130 for ( it = links.begin(); it != links.end(); it++ ) {
3131 const SMESH_TLink& link2 = (*it);
3132 if ( link2 != link )
3133 startLinks.push_back( link2 );
3137 // Get nodes of possible quadrangles
3138 const SMDS_MeshNode *n12 [4], *n13 [4];
3139 bool Ok12 = false, Ok13 = false;
3140 const SMDS_MeshNode *linkNode1, *linkNode2;
3142 linkNode1 = link12->first;
3143 linkNode2 = link12->second;
3144 if ( tr2 && getQuadrangleNodes( n12, linkNode1, linkNode2, tr1, tr2 ))
3148 linkNode1 = link13->first;
3149 linkNode2 = link13->second;
3150 if ( tr3 && getQuadrangleNodes( n13, linkNode1, linkNode2, tr1, tr3 ))
3154 // Choose a pair to fuse
3155 if ( Ok12 && Ok13 ) {
3156 SMDS_FaceOfNodes quad12 ( n12[ 0 ], n12[ 1 ], n12[ 2 ], n12[ 3 ] );
3157 SMDS_FaceOfNodes quad13 ( n13[ 0 ], n13[ 1 ], n13[ 2 ], n13[ 3 ] );
3158 double aBadRate12 = getBadRate( &quad12, theCrit );
3159 double aBadRate13 = getBadRate( &quad13, theCrit );
3160 if ( aBadRate13 < aBadRate12 )
3167 // and remove fused elems and remove links from the maps
3168 mapEl_setLi.erase( tr1 );
3171 mapEl_setLi.erase( tr2 );
3172 mapLi_listEl.erase( *link12 );
3173 if ( tr1->NbNodes() == 3 )
3175 const SMDS_MeshElement* newElem = 0;
3176 newElem = aMesh->AddFace(n12[0], n12[1], n12[2], n12[3] );
3177 myLastCreatedElems.Append(newElem);
3178 AddToSameGroups( newElem, tr1, aMesh );
3179 int aShapeId = tr1->getshapeId();
3181 aMesh->SetMeshElementOnShape( newElem, aShapeId );
3182 aMesh->RemoveElement( tr1 );
3183 aMesh->RemoveElement( tr2 );
3186 vector< const SMDS_MeshNode* > N1;
3187 vector< const SMDS_MeshNode* > N2;
3188 getNodesFromTwoTria(tr1,tr2,N1,N2);
3189 // now we receive following N1 and N2 (using numeration as in image in InverseDiag())
3190 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
3191 // i.e. first nodes from both arrays form a new diagonal
3192 const SMDS_MeshNode* aNodes[8];
3201 const SMDS_MeshElement* newElem = 0;
3202 if ( N1.size() == 7 || N2.size() == 7 ) // biquadratic
3203 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3204 aNodes[4], aNodes[5], aNodes[6], aNodes[7], N1[4]);
3206 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3207 aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
3208 myLastCreatedElems.Append(newElem);
3209 AddToSameGroups( newElem, tr1, aMesh );
3210 int aShapeId = tr1->getshapeId();
3212 aMesh->SetMeshElementOnShape( newElem, aShapeId );
3213 aMesh->RemoveElement( tr1 );
3214 aMesh->RemoveElement( tr2 );
3215 // remove middle node (9)
3216 if ( N1[4]->NbInverseElements() == 0 )
3217 aMesh->RemoveNode( N1[4] );
3218 if ( N1.size() == 7 && N1[6]->NbInverseElements() == 0 )
3219 aMesh->RemoveNode( N1[6] );
3220 if ( N2.size() == 7 && N2[6]->NbInverseElements() == 0 )
3221 aMesh->RemoveNode( N2[6] );
3226 mapEl_setLi.erase( tr3 );
3227 mapLi_listEl.erase( *link13 );
3228 if ( tr1->NbNodes() == 3 ) {
3229 const SMDS_MeshElement* newElem = 0;
3230 newElem = aMesh->AddFace(n13[0], n13[1], n13[2], n13[3] );
3231 myLastCreatedElems.Append(newElem);
3232 AddToSameGroups( newElem, tr1, aMesh );
3233 int aShapeId = tr1->getshapeId();
3235 aMesh->SetMeshElementOnShape( newElem, aShapeId );
3236 aMesh->RemoveElement( tr1 );
3237 aMesh->RemoveElement( tr3 );
3240 vector< const SMDS_MeshNode* > N1;
3241 vector< const SMDS_MeshNode* > N2;
3242 getNodesFromTwoTria(tr1,tr3,N1,N2);
3243 // now we receive following N1 and N2 (using numeration as above image)
3244 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
3245 // i.e. first nodes from both arrays form a new diagonal
3246 const SMDS_MeshNode* aNodes[8];
3255 const SMDS_MeshElement* newElem = 0;
3256 if ( N1.size() == 7 || N2.size() == 7 ) // biquadratic
3257 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3258 aNodes[4], aNodes[5], aNodes[6], aNodes[7], N1[4]);
3260 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3261 aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
3262 myLastCreatedElems.Append(newElem);
3263 AddToSameGroups( newElem, tr1, aMesh );
3264 int aShapeId = tr1->getshapeId();
3266 aMesh->SetMeshElementOnShape( newElem, aShapeId );
3267 aMesh->RemoveElement( tr1 );
3268 aMesh->RemoveElement( tr3 );
3269 // remove middle node (9)
3270 if ( N1[4]->NbInverseElements() == 0 )
3271 aMesh->RemoveNode( N1[4] );
3272 if ( N1.size() == 7 && N1[6]->NbInverseElements() == 0 )
3273 aMesh->RemoveNode( N1[6] );
3274 if ( N2.size() == 7 && N2[6]->NbInverseElements() == 0 )
3275 aMesh->RemoveNode( N2[6] );
3279 // Next element to fuse: the rejected one
3281 startElem = Ok12 ? tr3 : tr2;
3283 } // if ( startElem )
3284 } // while ( startElem || !startLinks.empty() )
3285 } // while ( ! mapEl_setLi.empty() )
3291 /*#define DUMPSO(txt) \
3292 // cout << txt << endl;
3293 //=============================================================================
3297 //=============================================================================
3298 static void swap( int i1, int i2, int idNodes[], gp_Pnt P[] )
3302 int tmp = idNodes[ i1 ];
3303 idNodes[ i1 ] = idNodes[ i2 ];
3304 idNodes[ i2 ] = tmp;
3305 gp_Pnt Ptmp = P[ i1 ];
3308 DUMPSO( i1 << "(" << idNodes[ i2 ] << ") <-> " << i2 << "(" << idNodes[ i1 ] << ")");
3311 //=======================================================================
3312 //function : SortQuadNodes
3313 //purpose : Set 4 nodes of a quadrangle face in a good order.
3314 // Swap 1<->2 or 2<->3 nodes and correspondingly return
3316 //=======================================================================
3318 int SMESH_MeshEditor::SortQuadNodes (const SMDS_Mesh * theMesh,
3323 for ( i = 0; i < 4; i++ ) {
3324 const SMDS_MeshNode *n = theMesh->FindNode( idNodes[i] );
3326 P[ i ].SetCoord( n->X(), n->Y(), n->Z() );
3329 gp_Vec V1(P[0], P[1]);
3330 gp_Vec V2(P[0], P[2]);
3331 gp_Vec V3(P[0], P[3]);
3333 gp_Vec Cross1 = V1 ^ V2;
3334 gp_Vec Cross2 = V2 ^ V3;
3337 if (Cross1.Dot(Cross2) < 0)
3342 if (Cross1.Dot(Cross2) < 0)
3346 swap ( i, i + 1, idNodes, P );
3348 // for ( int ii = 0; ii < 4; ii++ ) {
3349 // const SMDS_MeshNode *n = theMesh->FindNode( idNodes[ii] );
3350 // DUMPSO( ii << "(" << idNodes[ii] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
3356 //=======================================================================
3357 //function : SortHexaNodes
3358 //purpose : Set 8 nodes of a hexahedron in a good order.
3359 // Return success status
3360 //=======================================================================
3362 bool SMESH_MeshEditor::SortHexaNodes (const SMDS_Mesh * theMesh,
3367 DUMPSO( "INPUT: ========================================");
3368 for ( i = 0; i < 8; i++ ) {
3369 const SMDS_MeshNode *n = theMesh->FindNode( idNodes[i] );
3370 if ( !n ) return false;
3371 P[ i ].SetCoord( n->X(), n->Y(), n->Z() );
3372 DUMPSO( i << "(" << idNodes[i] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
3374 DUMPSO( "========================================");
3377 set<int> faceNodes; // ids of bottom face nodes, to be found
3378 set<int> checkedId1; // ids of tried 2-nd nodes
3379 Standard_Real leastDist = DBL_MAX; // dist of the 4-th node from 123 plane
3380 const Standard_Real tol = 1.e-6; // tolerance to find nodes in plane
3381 int iMin, iLoop1 = 0;
3383 // Loop to try the 2-nd nodes
3385 while ( leastDist > DBL_MIN && ++iLoop1 < 8 )
3387 // Find not checked 2-nd node
3388 for ( i = 1; i < 8; i++ )
3389 if ( checkedId1.find( idNodes[i] ) == checkedId1.end() ) {
3390 int id1 = idNodes[i];
3391 swap ( 1, i, idNodes, P );
3392 checkedId1.insert ( id1 );
3396 // Find the 3-d node so that 1-2-3 triangle to be on a hexa face,
3397 // ie that all but meybe one (id3 which is on the same face) nodes
3398 // lay on the same side from the triangle plane.
3400 bool manyInPlane = false; // more than 4 nodes lay in plane
3402 while ( ++iLoop2 < 6 ) {
3404 // get 1-2-3 plane coeffs
3405 Standard_Real A, B, C, D;
3406 gp_Vec N = gp_Vec (P[0], P[1]).Crossed( gp_Vec (P[0], P[2]) );
3407 if ( N.SquareMagnitude() > gp::Resolution() )
3409 gp_Pln pln ( P[0], N );
3410 pln.Coefficients( A, B, C, D );
3412 // find the node (iMin) closest to pln
3413 Standard_Real dist[ 8 ], minDist = DBL_MAX;
3415 for ( i = 3; i < 8; i++ ) {
3416 dist[i] = A * P[i].X() + B * P[i].Y() + C * P[i].Z() + D;
3417 if ( fabs( dist[i] ) < minDist ) {
3418 minDist = fabs( dist[i] );
3421 if ( fabs( dist[i] ) <= tol )
3422 idInPln.insert( idNodes[i] );
3425 // there should not be more than 4 nodes in bottom plane
3426 if ( idInPln.size() > 1 )
3428 DUMPSO( "### idInPln.size() = " << idInPln.size());
3429 // idInPlane does not contain the first 3 nodes
3430 if ( manyInPlane || idInPln.size() == 5)
3431 return false; // all nodes in one plane
3434 // set the 1-st node to be not in plane
3435 for ( i = 3; i < 8; i++ ) {
3436 if ( idInPln.find( idNodes[ i ] ) == idInPln.end() ) {
3437 DUMPSO( "### Reset 0-th node");
3438 swap( 0, i, idNodes, P );
3443 // reset to re-check second nodes
3444 leastDist = DBL_MAX;
3448 break; // from iLoop2;
3451 // check that the other 4 nodes are on the same side
3452 bool sameSide = true;
3453 bool isNeg = dist[ iMin == 3 ? 4 : 3 ] <= 0.;
3454 for ( i = 3; sameSide && i < 8; i++ ) {
3456 sameSide = ( isNeg == dist[i] <= 0.);
3459 // keep best solution
3460 if ( sameSide && minDist < leastDist ) {
3461 leastDist = minDist;
3463 faceNodes.insert( idNodes[ 1 ] );
3464 faceNodes.insert( idNodes[ 2 ] );
3465 faceNodes.insert( idNodes[ iMin ] );
3466 DUMPSO( "loop " << iLoop2 << " id2 " << idNodes[ 1 ] << " id3 " << idNodes[ 2 ]
3467 << " leastDist = " << leastDist);
3468 if ( leastDist <= DBL_MIN )
3473 // set next 3-d node to check
3474 int iNext = 2 + iLoop2;
3476 DUMPSO( "Try 2-nd");
3477 swap ( 2, iNext, idNodes, P );
3479 } // while ( iLoop2 < 6 )
3482 if ( faceNodes.empty() ) return false;
3484 // Put the faceNodes in proper places
3485 for ( i = 4; i < 8; i++ ) {
3486 if ( faceNodes.find( idNodes[ i ] ) != faceNodes.end() ) {
3487 // find a place to put
3489 while ( faceNodes.find( idNodes[ iTo ] ) != faceNodes.end() )
3491 DUMPSO( "Set faceNodes");
3492 swap ( iTo, i, idNodes, P );
3497 // Set nodes of the found bottom face in good order
3498 DUMPSO( " Found bottom face: ");
3499 i = SortQuadNodes( theMesh, idNodes );
3501 gp_Pnt Ptmp = P[ i ];
3506 // for ( int ii = 0; ii < 4; ii++ ) {
3507 // const SMDS_MeshNode *n = theMesh->FindNode( idNodes[ii] );
3508 // DUMPSO( ii << "(" << idNodes[ii] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
3511 // Gravity center of the top and bottom faces
3512 gp_Pnt aGCb = ( P[0].XYZ() + P[1].XYZ() + P[2].XYZ() + P[3].XYZ() ) / 4.;
3513 gp_Pnt aGCt = ( P[4].XYZ() + P[5].XYZ() + P[6].XYZ() + P[7].XYZ() ) / 4.;
3515 // Get direction from the bottom to the top face
3516 gp_Vec upDir ( aGCb, aGCt );
3517 Standard_Real upDirSize = upDir.Magnitude();
3518 if ( upDirSize <= gp::Resolution() ) return false;
3521 // Assure that the bottom face normal points up
3522 gp_Vec Nb = gp_Vec (P[0], P[1]).Crossed( gp_Vec (P[0], P[2]) );
3523 Nb += gp_Vec (P[0], P[2]).Crossed( gp_Vec (P[0], P[3]) );
3524 if ( Nb.Dot( upDir ) < 0 ) {
3525 DUMPSO( "Reverse bottom face");
3526 swap( 1, 3, idNodes, P );
3529 // Find 5-th node - the one closest to the 1-st among the last 4 nodes.
3530 Standard_Real minDist = DBL_MAX;
3531 for ( i = 4; i < 8; i++ ) {
3532 // projection of P[i] to the plane defined by P[0] and upDir
3533 gp_Pnt Pp = P[i].Translated( upDir * ( upDir.Dot( gp_Vec( P[i], P[0] ))));
3534 Standard_Real sqDist = P[0].SquareDistance( Pp );
3535 if ( sqDist < minDist ) {
3540 DUMPSO( "Set 4-th");
3541 swap ( 4, iMin, idNodes, P );
3543 // Set nodes of the top face in good order
3544 DUMPSO( "Sort top face");
3545 i = SortQuadNodes( theMesh, &idNodes[4] );
3548 gp_Pnt Ptmp = P[ i ];
3553 // Assure that direction of the top face normal is from the bottom face
3554 gp_Vec Nt = gp_Vec (P[4], P[5]).Crossed( gp_Vec (P[4], P[6]) );
3555 Nt += gp_Vec (P[4], P[6]).Crossed( gp_Vec (P[4], P[7]) );
3556 if ( Nt.Dot( upDir ) < 0 ) {
3557 DUMPSO( "Reverse top face");
3558 swap( 5, 7, idNodes, P );
3561 // DUMPSO( "OUTPUT: ========================================");
3562 // for ( i = 0; i < 8; i++ ) {
3563 // float *p = ugrid->GetPoint(idNodes[i]);
3564 // DUMPSO( i << "(" << idNodes[i] << ") : " << p[0] << " " << p[1] << " " << p[2]);
3570 //================================================================================
3572 * \brief Return nodes linked to the given one
3573 * \param theNode - the node
3574 * \param linkedNodes - the found nodes
3575 * \param type - the type of elements to check
3577 * Medium nodes are ignored
3579 //================================================================================
3581 void SMESH_MeshEditor::GetLinkedNodes( const SMDS_MeshNode* theNode,
3582 TIDSortedElemSet & linkedNodes,
3583 SMDSAbs_ElementType type )
3585 SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(type);
3586 while ( elemIt->more() )
3588 const SMDS_MeshElement* elem = elemIt->next();
3589 if(elem->GetType() == SMDSAbs_0DElement)
3592 SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
3593 if ( elem->GetType() == SMDSAbs_Volume )
3595 SMDS_VolumeTool vol( elem );
3596 while ( nodeIt->more() ) {
3597 const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
3598 if ( theNode != n && vol.IsLinked( theNode, n ))
3599 linkedNodes.insert( n );
3604 for ( int i = 0; nodeIt->more(); ++i ) {
3605 const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
3606 if ( n == theNode ) {
3607 int iBefore = i - 1;
3609 if ( elem->IsQuadratic() ) {
3610 int nb = elem->NbNodes() / 2;
3611 iAfter = SMESH_MesherHelper::WrapIndex( iAfter, nb );
3612 iBefore = SMESH_MesherHelper::WrapIndex( iBefore, nb );
3614 linkedNodes.insert( elem->GetNodeWrap( iAfter ));
3615 linkedNodes.insert( elem->GetNodeWrap( iBefore ));
3622 //=======================================================================
3623 //function : laplacianSmooth
3624 //purpose : pulls theNode toward the center of surrounding nodes directly
3625 // connected to that node along an element edge
3626 //=======================================================================
3628 void laplacianSmooth(const SMDS_MeshNode* theNode,
3629 const Handle(Geom_Surface)& theSurface,
3630 map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
3632 // find surrounding nodes
3634 TIDSortedElemSet nodeSet;
3635 SMESH_MeshEditor::GetLinkedNodes( theNode, nodeSet, SMDSAbs_Face );
3637 // compute new coodrs
3639 double coord[] = { 0., 0., 0. };
3640 TIDSortedElemSet::iterator nodeSetIt = nodeSet.begin();
3641 for ( ; nodeSetIt != nodeSet.end(); nodeSetIt++ ) {
3642 const SMDS_MeshNode* node = cast2Node(*nodeSetIt);
3643 if ( theSurface.IsNull() ) { // smooth in 3D
3644 coord[0] += node->X();
3645 coord[1] += node->Y();
3646 coord[2] += node->Z();
3648 else { // smooth in 2D
3649 ASSERT( theUVMap.find( node ) != theUVMap.end() );
3650 gp_XY* uv = theUVMap[ node ];
3651 coord[0] += uv->X();
3652 coord[1] += uv->Y();
3655 int nbNodes = nodeSet.size();
3658 coord[0] /= nbNodes;
3659 coord[1] /= nbNodes;
3661 if ( !theSurface.IsNull() ) {
3662 ASSERT( theUVMap.find( theNode ) != theUVMap.end() );
3663 theUVMap[ theNode ]->SetCoord( coord[0], coord[1] );
3664 gp_Pnt p3d = theSurface->Value( coord[0], coord[1] );
3670 coord[2] /= nbNodes;
3674 const_cast< SMDS_MeshNode* >( theNode )->setXYZ(coord[0],coord[1],coord[2]);
3677 //=======================================================================
3678 //function : centroidalSmooth
3679 //purpose : pulls theNode toward the element-area-weighted centroid of the
3680 // surrounding elements
3681 //=======================================================================
3683 void centroidalSmooth(const SMDS_MeshNode* theNode,
3684 const Handle(Geom_Surface)& theSurface,
3685 map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
3687 gp_XYZ aNewXYZ(0.,0.,0.);
3688 SMESH::Controls::Area anAreaFunc;
3689 double totalArea = 0.;
3694 SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(SMDSAbs_Face);
3695 while ( elemIt->more() )
3697 const SMDS_MeshElement* elem = elemIt->next();
3700 gp_XYZ elemCenter(0.,0.,0.);
3701 SMESH::Controls::TSequenceOfXYZ aNodePoints;
3702 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3703 int nn = elem->NbNodes();
3704 if(elem->IsQuadratic()) nn = nn/2;
3706 //while ( itN->more() ) {
3708 const SMDS_MeshNode* aNode = static_cast<const SMDS_MeshNode*>( itN->next() );
3710 gp_XYZ aP( aNode->X(), aNode->Y(), aNode->Z() );
3711 aNodePoints.push_back( aP );
3712 if ( !theSurface.IsNull() ) { // smooth in 2D
3713 ASSERT( theUVMap.find( aNode ) != theUVMap.end() );
3714 gp_XY* uv = theUVMap[ aNode ];
3715 aP.SetCoord( uv->X(), uv->Y(), 0. );
3719 double elemArea = anAreaFunc.GetValue( aNodePoints );
3720 totalArea += elemArea;
3722 aNewXYZ += elemCenter * elemArea;
3724 aNewXYZ /= totalArea;
3725 if ( !theSurface.IsNull() ) {
3726 theUVMap[ theNode ]->SetCoord( aNewXYZ.X(), aNewXYZ.Y() );
3727 aNewXYZ = theSurface->Value( aNewXYZ.X(), aNewXYZ.Y() ).XYZ();
3732 const_cast< SMDS_MeshNode* >( theNode )->setXYZ(aNewXYZ.X(),aNewXYZ.Y(),aNewXYZ.Z());
3735 //=======================================================================
3736 //function : getClosestUV
3737 //purpose : return UV of closest projection
3738 //=======================================================================
3740 static bool getClosestUV (Extrema_GenExtPS& projector,
3741 const gp_Pnt& point,
3744 projector.Perform( point );
3745 if ( projector.IsDone() ) {
3746 double u, v, minVal = DBL_MAX;
3747 for ( int i = projector.NbExt(); i > 0; i-- )
3748 if ( projector.SquareDistance( i ) < minVal ) {
3749 minVal = projector.SquareDistance( i );
3750 projector.Point( i ).Parameter( u, v );
3752 result.SetCoord( u, v );
3758 //=======================================================================
3760 //purpose : Smooth theElements during theNbIterations or until a worst
3761 // element has aspect ratio <= theTgtAspectRatio.
3762 // Aspect Ratio varies in range [1.0, inf].
3763 // If theElements is empty, the whole mesh is smoothed.
3764 // theFixedNodes contains additionally fixed nodes. Nodes built
3765 // on edges and boundary nodes are always fixed.
3766 //=======================================================================
3768 void SMESH_MeshEditor::Smooth (TIDSortedElemSet & theElems,
3769 set<const SMDS_MeshNode*> & theFixedNodes,
3770 const SmoothMethod theSmoothMethod,
3771 const int theNbIterations,
3772 double theTgtAspectRatio,
3775 myLastCreatedElems.Clear();
3776 myLastCreatedNodes.Clear();
3778 MESSAGE((theSmoothMethod==LAPLACIAN ? "LAPLACIAN" : "CENTROIDAL") << "--::Smooth()");
3780 if ( theTgtAspectRatio < 1.0 )
3781 theTgtAspectRatio = 1.0;
3783 const double disttol = 1.e-16;
3785 SMESH::Controls::AspectRatio aQualityFunc;
3787 SMESHDS_Mesh* aMesh = GetMeshDS();
3789 if ( theElems.empty() ) {
3790 // add all faces to theElems
3791 SMDS_FaceIteratorPtr fIt = aMesh->facesIterator();
3792 while ( fIt->more() ) {
3793 const SMDS_MeshElement* face = fIt->next();
3794 theElems.insert( theElems.end(), face );
3797 // get all face ids theElems are on
3798 set< int > faceIdSet;
3799 TIDSortedElemSet::iterator itElem;
3801 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
3802 int fId = FindShape( *itElem );
3803 // check that corresponding submesh exists and a shape is face
3805 faceIdSet.find( fId ) == faceIdSet.end() &&
3806 aMesh->MeshElements( fId )) {
3807 TopoDS_Shape F = aMesh->IndexToShape( fId );
3808 if ( !F.IsNull() && F.ShapeType() == TopAbs_FACE )
3809 faceIdSet.insert( fId );
3812 faceIdSet.insert( 0 ); // to smooth elements that are not on any TopoDS_Face
3814 // ===============================================
3815 // smooth elements on each TopoDS_Face separately
3816 // ===============================================
3818 SMESH_MesherHelper helper( *GetMesh() );
3820 set< int >::reverse_iterator fId = faceIdSet.rbegin(); // treat 0 fId at the end
3821 for ( ; fId != faceIdSet.rend(); ++fId )
3823 // get face surface and submesh
3824 Handle(Geom_Surface) surface;
3825 SMESHDS_SubMesh* faceSubMesh = 0;
3827 double fToler2 = 0, f,l;
3828 double u1 = 0, u2 = 0, v1 = 0, v2 = 0;
3829 bool isUPeriodic = false, isVPeriodic = false;
3832 face = TopoDS::Face( aMesh->IndexToShape( *fId ));
3833 surface = BRep_Tool::Surface( face );
3834 faceSubMesh = aMesh->MeshElements( *fId );
3835 fToler2 = BRep_Tool::Tolerance( face );
3836 fToler2 *= fToler2 * 10.;
3837 isUPeriodic = surface->IsUPeriodic();
3840 isVPeriodic = surface->IsVPeriodic();
3843 surface->Bounds( u1, u2, v1, v2 );
3844 helper.SetSubShape( face );
3846 // ---------------------------------------------------------
3847 // for elements on a face, find movable and fixed nodes and
3848 // compute UV for them
3849 // ---------------------------------------------------------
3850 bool checkBoundaryNodes = false;
3851 bool isQuadratic = false;
3852 set<const SMDS_MeshNode*> setMovableNodes;
3853 map< const SMDS_MeshNode*, gp_XY* > uvMap, uvMap2;
3854 list< gp_XY > listUV; // uvs the 2 uvMaps refer to
3855 list< const SMDS_MeshElement* > elemsOnFace;
3857 Extrema_GenExtPS projector;
3858 GeomAdaptor_Surface surfAdaptor;
3859 if ( !surface.IsNull() ) {
3860 surfAdaptor.Load( surface );
3861 projector.Initialize( surfAdaptor, 20,20, 1e-5,1e-5 );
3863 int nbElemOnFace = 0;
3864 itElem = theElems.begin();
3865 // loop on not yet smoothed elements: look for elems on a face
3866 while ( itElem != theElems.end() )
3868 if ( faceSubMesh && nbElemOnFace == faceSubMesh->NbElements() )
3869 break; // all elements found
3871 const SMDS_MeshElement* elem = *itElem;
3872 if ( !elem || elem->GetType() != SMDSAbs_Face || elem->NbNodes() < 3 ||
3873 ( faceSubMesh && !faceSubMesh->Contains( elem ))) {
3877 elemsOnFace.push_back( elem );
3878 theElems.erase( itElem++ );
3882 isQuadratic = elem->IsQuadratic();
3884 // get movable nodes of elem
3885 const SMDS_MeshNode* node;
3886 SMDS_TypeOfPosition posType;
3887 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3888 int nn = 0, nbn = elem->NbNodes();
3889 if(elem->IsQuadratic())
3891 while ( nn++ < nbn ) {
3892 node = static_cast<const SMDS_MeshNode*>( itN->next() );
3893 const SMDS_PositionPtr& pos = node->GetPosition();
3894 posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
3895 if (posType != SMDS_TOP_EDGE &&
3896 posType != SMDS_TOP_VERTEX &&
3897 theFixedNodes.find( node ) == theFixedNodes.end())
3899 // check if all faces around the node are on faceSubMesh
3900 // because a node on edge may be bound to face
3901 SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
3903 if ( faceSubMesh ) {
3904 while ( eIt->more() && all ) {
3905 const SMDS_MeshElement* e = eIt->next();
3906 all = faceSubMesh->Contains( e );
3910 setMovableNodes.insert( node );
3912 checkBoundaryNodes = true;
3914 if ( posType == SMDS_TOP_3DSPACE )
3915 checkBoundaryNodes = true;
3918 if ( surface.IsNull() )
3921 // get nodes to check UV
3922 list< const SMDS_MeshNode* > uvCheckNodes;
3923 const SMDS_MeshNode* nodeInFace = 0;
3924 itN = elem->nodesIterator();
3925 nn = 0; nbn = elem->NbNodes();
3926 if(elem->IsQuadratic())
3928 while ( nn++ < nbn ) {
3929 node = static_cast<const SMDS_MeshNode*>( itN->next() );
3930 if ( node->GetPosition()->GetDim() == 2 )
3932 if ( uvMap.find( node ) == uvMap.end() )
3933 uvCheckNodes.push_back( node );
3934 // add nodes of elems sharing node
3935 // SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
3936 // while ( eIt->more() ) {
3937 // const SMDS_MeshElement* e = eIt->next();
3938 // if ( e != elem ) {
3939 // SMDS_ElemIteratorPtr nIt = e->nodesIterator();
3940 // while ( nIt->more() ) {
3941 // const SMDS_MeshNode* n =
3942 // static_cast<const SMDS_MeshNode*>( nIt->next() );
3943 // if ( uvMap.find( n ) == uvMap.end() )
3944 // uvCheckNodes.push_back( n );
3950 list< const SMDS_MeshNode* >::iterator n = uvCheckNodes.begin();
3951 for ( ; n != uvCheckNodes.end(); ++n ) {
3954 const SMDS_PositionPtr& pos = node->GetPosition();
3955 posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
3959 bool toCheck = true;
3960 uv = helper.GetNodeUV( face, node, nodeInFace, &toCheck );
3962 // compute not existing UV
3963 bool project = ( posType == SMDS_TOP_3DSPACE );
3964 // double dist1 = DBL_MAX, dist2 = 0;
3965 // if ( posType != SMDS_TOP_3DSPACE ) {
3966 // dist1 = pNode.SquareDistance( surface->Value( uv.X(), uv.Y() ));
3967 // project = dist1 > fToler2;
3969 if ( project ) { // compute new UV
3971 gp_Pnt pNode = SMESH_TNodeXYZ( node );
3972 if ( !getClosestUV( projector, pNode, newUV )) {
3973 MESSAGE("Node Projection Failed " << node);
3977 newUV.SetX( ElCLib::InPeriod( newUV.X(), u1, u2 ));
3979 newUV.SetY( ElCLib::InPeriod( newUV.Y(), v1, v2 ));
3981 // if ( posType != SMDS_TOP_3DSPACE )
3982 // dist2 = pNode.SquareDistance( surface->Value( newUV.X(), newUV.Y() ));
3983 // if ( dist2 < dist1 )
3987 // store UV in the map
3988 listUV.push_back( uv );
3989 uvMap.insert( make_pair( node, &listUV.back() ));
3991 } // loop on not yet smoothed elements
3993 if ( !faceSubMesh || nbElemOnFace != faceSubMesh->NbElements() )
3994 checkBoundaryNodes = true;
3996 // fix nodes on mesh boundary
3998 if ( checkBoundaryNodes ) {
3999 map< SMESH_TLink, int > linkNbMap; // how many times a link encounters in elemsOnFace
4000 map< SMESH_TLink, int >::iterator link_nb;
4001 // put all elements links to linkNbMap
4002 list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4003 for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
4004 const SMDS_MeshElement* elem = (*elemIt);
4005 int nbn = elem->NbCornerNodes();
4006 // loop on elem links: insert them in linkNbMap
4007 for ( int iN = 0; iN < nbn; ++iN ) {
4008 const SMDS_MeshNode* n1 = elem->GetNode( iN );
4009 const SMDS_MeshNode* n2 = elem->GetNode(( iN+1 ) % nbn);
4010 SMESH_TLink link( n1, n2 );
4011 link_nb = linkNbMap.insert( make_pair( link, 0 )).first;
4015 // remove nodes that are in links encountered only once from setMovableNodes
4016 for ( link_nb = linkNbMap.begin(); link_nb != linkNbMap.end(); ++link_nb ) {
4017 if ( link_nb->second == 1 ) {
4018 setMovableNodes.erase( link_nb->first.node1() );
4019 setMovableNodes.erase( link_nb->first.node2() );
4024 // -----------------------------------------------------
4025 // for nodes on seam edge, compute one more UV ( uvMap2 );
4026 // find movable nodes linked to nodes on seam and which
4027 // are to be smoothed using the second UV ( uvMap2 )
4028 // -----------------------------------------------------
4030 set<const SMDS_MeshNode*> nodesNearSeam; // to smooth using uvMap2
4031 if ( !surface.IsNull() ) {
4032 TopExp_Explorer eExp( face, TopAbs_EDGE );
4033 for ( ; eExp.More(); eExp.Next() ) {
4034 TopoDS_Edge edge = TopoDS::Edge( eExp.Current() );
4035 if ( !BRep_Tool::IsClosed( edge, face ))
4037 SMESHDS_SubMesh* sm = aMesh->MeshElements( edge );
4038 if ( !sm ) continue;
4039 // find out which parameter varies for a node on seam
4042 Handle(Geom2d_Curve) pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
4043 if ( pcurve.IsNull() ) continue;
4044 uv1 = pcurve->Value( f );
4046 pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
4047 if ( pcurve.IsNull() ) continue;
4048 uv2 = pcurve->Value( f );
4049 int iPar = Abs( uv1.X() - uv2.X() ) > Abs( uv1.Y() - uv2.Y() ) ? 1 : 2;
4051 if ( uv1.Coord( iPar ) > uv2.Coord( iPar ))
4052 std::swap( uv1, uv2 );
4053 // get nodes on seam and its vertices
4054 list< const SMDS_MeshNode* > seamNodes;
4055 SMDS_NodeIteratorPtr nSeamIt = sm->GetNodes();
4056 while ( nSeamIt->more() ) {
4057 const SMDS_MeshNode* node = nSeamIt->next();
4058 if ( !isQuadratic || !IsMedium( node ))
4059 seamNodes.push_back( node );
4061 TopExp_Explorer vExp( edge, TopAbs_VERTEX );
4062 for ( ; vExp.More(); vExp.Next() ) {
4063 sm = aMesh->MeshElements( vExp.Current() );
4065 nSeamIt = sm->GetNodes();
4066 while ( nSeamIt->more() )
4067 seamNodes.push_back( nSeamIt->next() );
4070 // loop on nodes on seam
4071 list< const SMDS_MeshNode* >::iterator noSeIt = seamNodes.begin();
4072 for ( ; noSeIt != seamNodes.end(); ++noSeIt ) {
4073 const SMDS_MeshNode* nSeam = *noSeIt;
4074 map< const SMDS_MeshNode*, gp_XY* >::iterator n_uv = uvMap.find( nSeam );
4075 if ( n_uv == uvMap.end() )
4078 n_uv->second->SetCoord( iPar, uv1.Coord( iPar ));
4079 // set the second UV
4080 listUV.push_back( *n_uv->second );
4081 listUV.back().SetCoord( iPar, uv2.Coord( iPar ));
4082 if ( uvMap2.empty() )
4083 uvMap2 = uvMap; // copy the uvMap contents
4084 uvMap2[ nSeam ] = &listUV.back();
4086 // collect movable nodes linked to ones on seam in nodesNearSeam
4087 SMDS_ElemIteratorPtr eIt = nSeam->GetInverseElementIterator(SMDSAbs_Face);
4088 while ( eIt->more() ) {
4089 const SMDS_MeshElement* e = eIt->next();
4090 int nbUseMap1 = 0, nbUseMap2 = 0;
4091 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
4092 int nn = 0, nbn = e->NbNodes();
4093 if(e->IsQuadratic()) nbn = nbn/2;
4094 while ( nn++ < nbn )
4096 const SMDS_MeshNode* n =
4097 static_cast<const SMDS_MeshNode*>( nIt->next() );
4099 setMovableNodes.find( n ) == setMovableNodes.end() )
4101 // add only nodes being closer to uv2 than to uv1
4102 // gp_Pnt pMid (0.5 * ( n->X() + nSeam->X() ),
4103 // 0.5 * ( n->Y() + nSeam->Y() ),
4104 // 0.5 * ( n->Z() + nSeam->Z() ));
4106 // getClosestUV( projector, pMid, uv );
4107 double x = uvMap[ n ]->Coord( iPar );
4108 if ( Abs( uv1.Coord( iPar ) - x ) >
4109 Abs( uv2.Coord( iPar ) - x )) {
4110 nodesNearSeam.insert( n );
4116 // for centroidalSmooth all element nodes must
4117 // be on one side of a seam
4118 if ( theSmoothMethod == CENTROIDAL && nbUseMap1 && nbUseMap2 ) {
4119 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
4121 while ( nn++ < nbn ) {
4122 const SMDS_MeshNode* n =
4123 static_cast<const SMDS_MeshNode*>( nIt->next() );
4124 setMovableNodes.erase( n );
4128 } // loop on nodes on seam
4129 } // loop on edge of a face
4130 } // if ( !face.IsNull() )
4132 if ( setMovableNodes.empty() ) {
4133 MESSAGE( "Face id : " << *fId << " - NO SMOOTHING: no nodes to move!!!");
4134 continue; // goto next face
4142 double maxRatio = -1., maxDisplacement = -1.;
4143 set<const SMDS_MeshNode*>::iterator nodeToMove;
4144 for ( it = 0; it < theNbIterations; it++ ) {
4145 maxDisplacement = 0.;
4146 nodeToMove = setMovableNodes.begin();
4147 for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
4148 const SMDS_MeshNode* node = (*nodeToMove);
4149 gp_XYZ aPrevPos ( node->X(), node->Y(), node->Z() );
4152 bool map2 = ( nodesNearSeam.find( node ) != nodesNearSeam.end() );
4153 if ( theSmoothMethod == LAPLACIAN )
4154 laplacianSmooth( node, surface, map2 ? uvMap2 : uvMap );
4156 centroidalSmooth( node, surface, map2 ? uvMap2 : uvMap );
4158 // node displacement
4159 gp_XYZ aNewPos ( node->X(), node->Y(), node->Z() );
4160 Standard_Real aDispl = (aPrevPos - aNewPos).SquareModulus();
4161 if ( aDispl > maxDisplacement )
4162 maxDisplacement = aDispl;
4164 // no node movement => exit
4165 //if ( maxDisplacement < 1.e-16 ) {
4166 if ( maxDisplacement < disttol ) {
4167 MESSAGE("-- no node movement --");
4171 // check elements quality
4173 list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4174 for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
4175 const SMDS_MeshElement* elem = (*elemIt);
4176 if ( !elem || elem->GetType() != SMDSAbs_Face )
4178 SMESH::Controls::TSequenceOfXYZ aPoints;
4179 if ( aQualityFunc.GetPoints( elem, aPoints )) {
4180 double aValue = aQualityFunc.GetValue( aPoints );
4181 if ( aValue > maxRatio )
4185 if ( maxRatio <= theTgtAspectRatio ) {
4186 MESSAGE("-- quality achived --");
4189 if (it+1 == theNbIterations) {
4190 MESSAGE("-- Iteration limit exceeded --");
4192 } // smoothing iterations
4194 MESSAGE(" Face id: " << *fId <<
4195 " Nb iterstions: " << it <<
4196 " Displacement: " << maxDisplacement <<
4197 " Aspect Ratio " << maxRatio);
4199 // ---------------------------------------
4200 // new nodes positions are computed,
4201 // record movement in DS and set new UV
4202 // ---------------------------------------
4203 nodeToMove = setMovableNodes.begin();
4204 for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
4205 SMDS_MeshNode* node = const_cast< SMDS_MeshNode* > (*nodeToMove);
4206 aMesh->MoveNode( node, node->X(), node->Y(), node->Z() );
4207 map< const SMDS_MeshNode*, gp_XY* >::iterator node_uv = uvMap.find( node );
4208 if ( node_uv != uvMap.end() ) {
4209 gp_XY* uv = node_uv->second;
4211 ( SMDS_PositionPtr( new SMDS_FacePosition( uv->X(), uv->Y() )));
4215 // move medium nodes of quadratic elements
4218 vector<const SMDS_MeshNode*> nodes;
4220 list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4221 for ( ; elemIt != elemsOnFace.end(); ++elemIt )
4223 const SMDS_MeshElement* QF = *elemIt;
4224 if ( QF->IsQuadratic() )
4226 nodes.assign( SMDS_MeshElement::iterator( QF->interlacedNodesElemIterator() ),
4227 SMDS_MeshElement::iterator() );
4228 nodes.push_back( nodes[0] );
4230 for (size_t i = 1; i < nodes.size(); i += 2 ) // i points to a medium node
4232 if ( !surface.IsNull() )
4234 gp_XY uv1 = helper.GetNodeUV( face, nodes[i-1], nodes[i+1], &checkUV );
4235 gp_XY uv2 = helper.GetNodeUV( face, nodes[i+1], nodes[i-1], &checkUV );
4236 gp_XY uv = helper.GetMiddleUV( surface, uv1, uv2 );
4237 xyz = surface->Value( uv.X(), uv.Y() );
4240 xyz = 0.5 * ( SMESH_TNodeXYZ( nodes[i-1] ) + SMESH_TNodeXYZ( nodes[i+1] ));
4242 if (( SMESH_TNodeXYZ( nodes[i] ) - xyz.XYZ() ).Modulus() > disttol )
4243 // we have to move a medium node
4244 aMesh->MoveNode( nodes[i], xyz.X(), xyz.Y(), xyz.Z() );
4250 } // loop on face ids
4256 //=======================================================================
4257 //function : isReverse
4258 //purpose : Return true if normal of prevNodes is not co-directied with
4259 // gp_Vec(prevNodes[iNotSame],nextNodes[iNotSame]).
4260 // iNotSame is where prevNodes and nextNodes are different.
4261 // If result is true then future volume orientation is OK
4262 //=======================================================================
4264 bool isReverse(const SMDS_MeshElement* face,
4265 const vector<const SMDS_MeshNode*>& prevNodes,
4266 const vector<const SMDS_MeshNode*>& nextNodes,
4270 SMESH_TNodeXYZ pP = prevNodes[ iNotSame ];
4271 SMESH_TNodeXYZ pN = nextNodes[ iNotSame ];
4272 gp_XYZ extrDir( pN - pP ), faceNorm;
4273 SMESH_MeshAlgos::FaceNormal( face, faceNorm, /*normalized=*/false );
4275 return faceNorm * extrDir < 0.0;
4278 //================================================================================
4280 * \brief Assure that theElemSets[0] holds elements, not nodes
4282 //================================================================================
4284 void setElemsFirst( TIDSortedElemSet theElemSets[2] )
4286 if ( !theElemSets[0].empty() &&
4287 (*theElemSets[0].begin())->GetType() == SMDSAbs_Node )
4289 std::swap( theElemSets[0], theElemSets[1] );
4291 else if ( !theElemSets[1].empty() &&
4292 (*theElemSets[1].begin())->GetType() != SMDSAbs_Node )
4294 std::swap( theElemSets[0], theElemSets[1] );
4299 //=======================================================================
4301 * \brief Create elements by sweeping an element
4302 * \param elem - element to sweep
4303 * \param newNodesItVec - nodes generated from each node of the element
4304 * \param newElems - generated elements
4305 * \param nbSteps - number of sweeping steps
4306 * \param srcElements - to append elem for each generated element
4308 //=======================================================================
4310 void SMESH_MeshEditor::sweepElement(const SMDS_MeshElement* elem,
4311 const vector<TNodeOfNodeListMapItr> & newNodesItVec,
4312 list<const SMDS_MeshElement*>& newElems,
4314 SMESH_SequenceOfElemPtr& srcElements)
4316 //MESSAGE("sweepElement " << nbSteps);
4317 SMESHDS_Mesh* aMesh = GetMeshDS();
4319 const int nbNodes = elem->NbNodes();
4320 const int nbCorners = elem->NbCornerNodes();
4321 SMDSAbs_EntityType baseType = elem->GetEntityType(); /* it can change in case of
4322 polyhedron creation !!! */
4323 // Loop on elem nodes:
4324 // find new nodes and detect same nodes indices
4325 vector < list< const SMDS_MeshNode* >::const_iterator > itNN( nbNodes );
4326 vector<const SMDS_MeshNode*> prevNod( nbNodes );
4327 vector<const SMDS_MeshNode*> nextNod( nbNodes );
4328 vector<const SMDS_MeshNode*> midlNod( nbNodes );
4330 int iNode, nbSame = 0, nbDouble = 0, iNotSameNode = 0;
4331 vector<int> sames(nbNodes);
4332 vector<bool> isSingleNode(nbNodes);
4334 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4335 TNodeOfNodeListMapItr nnIt = newNodesItVec[ iNode ];
4336 const SMDS_MeshNode* node = nnIt->first;
4337 const list< const SMDS_MeshNode* > & listNewNodes = nnIt->second;
4338 if ( listNewNodes.empty() )
4341 itNN [ iNode ] = listNewNodes.begin();
4342 prevNod[ iNode ] = node;
4343 nextNod[ iNode ] = listNewNodes.front();
4345 isSingleNode[iNode] = (listNewNodes.size()==nbSteps); /* medium node of quadratic or
4346 corner node of linear */
4347 if ( prevNod[ iNode ] != nextNod [ iNode ])
4348 nbDouble += !isSingleNode[iNode];
4350 if( iNode < nbCorners ) { // check corners only
4351 if ( prevNod[ iNode ] == nextNod [ iNode ])
4352 sames[nbSame++] = iNode;
4354 iNotSameNode = iNode;
4358 if ( nbSame == nbNodes || nbSame > 2) {
4359 MESSAGE( " Too many same nodes of element " << elem->GetID() );
4363 if ( elem->GetType() == SMDSAbs_Face && !isReverse( elem, prevNod, nextNod, iNotSameNode ))
4365 // fix nodes order to have bottom normal external
4366 if ( baseType == SMDSEntity_Polygon )
4368 std::reverse( itNN.begin(), itNN.end() );
4369 std::reverse( prevNod.begin(), prevNod.end() );
4370 std::reverse( midlNod.begin(), midlNod.end() );
4371 std::reverse( nextNod.begin(), nextNod.end() );
4372 std::reverse( isSingleNode.begin(), isSingleNode.end() );
4376 const vector<int>& ind = SMDS_MeshCell::reverseSmdsOrder( baseType, nbNodes );
4377 SMDS_MeshCell::applyInterlace( ind, itNN );
4378 SMDS_MeshCell::applyInterlace( ind, prevNod );
4379 SMDS_MeshCell::applyInterlace( ind, nextNod );
4380 SMDS_MeshCell::applyInterlace( ind, midlNod );
4381 SMDS_MeshCell::applyInterlace( ind, isSingleNode );
4384 sames[nbSame] = iNotSameNode;
4385 for ( int j = 0; j <= nbSame; ++j )
4386 for ( size_t i = 0; i < ind.size(); ++i )
4387 if ( ind[i] == sames[j] )
4392 iNotSameNode = sames[nbSame];
4396 else if ( elem->GetType() == SMDSAbs_Edge )
4398 // orient a new face same as adjacent one
4400 const SMDS_MeshElement* e;
4401 TIDSortedElemSet dummy;
4402 if (( e = SMESH_MeshAlgos::FindFaceInSet( nextNod[0], prevNod[0], dummy,dummy, &i1, &i2 )) ||
4403 ( e = SMESH_MeshAlgos::FindFaceInSet( prevNod[1], nextNod[1], dummy,dummy, &i1, &i2 )) ||
4404 ( e = SMESH_MeshAlgos::FindFaceInSet( prevNod[0], prevNod[1], dummy,dummy, &i1, &i2 )))
4406 // there is an adjacent face, check order of nodes in it
4407 bool sameOrder = ( Abs( i2 - i1 ) == 1 ) ? ( i2 > i1 ) : ( i2 < i1 );
4410 std::swap( itNN[0], itNN[1] );
4411 std::swap( prevNod[0], prevNod[1] );
4412 std::swap( nextNod[0], nextNod[1] );
4413 isSingleNode.swap( isSingleNode[0], isSingleNode[1] );
4415 sames[0] = 1 - sames[0];
4416 iNotSameNode = 1 - iNotSameNode;
4421 int iSameNode = 0, iBeforeSame = 0, iAfterSame = 0, iOpposSame = 0;
4423 iSameNode = sames[ nbSame-1 ];
4424 iBeforeSame = ( iSameNode + nbCorners - 1 ) % nbCorners;
4425 iAfterSame = ( iSameNode + 1 ) % nbCorners;
4426 iOpposSame = ( iSameNode - 2 < 0 ? iSameNode + 2 : iSameNode - 2 );
4429 if ( baseType == SMDSEntity_Polygon )
4431 if ( nbNodes == 3 ) baseType = SMDSEntity_Triangle;
4432 else if ( nbNodes == 4 ) baseType = SMDSEntity_Quadrangle;
4434 else if ( baseType == SMDSEntity_Quad_Polygon )
4436 if ( nbNodes == 6 ) baseType = SMDSEntity_Quad_Triangle;
4437 else if ( nbNodes == 8 ) baseType = SMDSEntity_Quad_Quadrangle;
4440 // make new elements
4441 for (int iStep = 0; iStep < nbSteps; iStep++ )
4444 for ( iNode = 0; iNode < nbNodes; iNode++ )
4446 midlNod[ iNode ] = isSingleNode[iNode] ? 0 : *itNN[ iNode ]++;
4447 nextNod[ iNode ] = *itNN[ iNode ]++;
4450 SMDS_MeshElement* aNewElem = 0;
4451 /*if(!elem->IsPoly())*/ {
4452 switch ( baseType ) {
4454 case SMDSEntity_Node: { // sweep NODE
4455 if ( nbSame == 0 ) {
4456 if ( isSingleNode[0] )
4457 aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ] );
4459 aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ], midlNod[ 0 ] );
4465 case SMDSEntity_Edge: { // sweep EDGE
4466 if ( nbDouble == 0 )
4468 if ( nbSame == 0 ) // ---> quadrangle
4469 aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
4470 nextNod[ 1 ], nextNod[ 0 ] );
4471 else // ---> triangle
4472 aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
4473 nextNod[ iNotSameNode ] );
4475 else // ---> polygon
4477 vector<const SMDS_MeshNode*> poly_nodes;
4478 poly_nodes.push_back( prevNod[0] );
4479 poly_nodes.push_back( prevNod[1] );
4480 if ( prevNod[1] != nextNod[1] )
4482 if ( midlNod[1]) poly_nodes.push_back( midlNod[1]);
4483 poly_nodes.push_back( nextNod[1] );
4485 if ( prevNod[0] != nextNod[0] )
4487 poly_nodes.push_back( nextNod[0] );
4488 if ( midlNod[0]) poly_nodes.push_back( midlNod[0]);
4490 switch ( poly_nodes.size() ) {
4492 aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ], poly_nodes[ 2 ]);
4495 aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ],
4496 poly_nodes[ 2 ], poly_nodes[ 3 ]);
4499 aNewElem = aMesh->AddPolygonalFace (poly_nodes);
4504 case SMDSEntity_Triangle: // TRIANGLE --->
4506 if ( nbDouble > 0 ) break;
4507 if ( nbSame == 0 ) // ---> pentahedron
4508 aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
4509 nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ] );
4511 else if ( nbSame == 1 ) // ---> pyramid
4512 aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
4513 nextNod[ iAfterSame ], nextNod[ iBeforeSame ],
4514 nextNod[ iSameNode ]);
4516 else // 2 same nodes: ---> tetrahedron
4517 aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
4518 nextNod[ iNotSameNode ]);
4521 case SMDSEntity_Quad_Edge: // sweep quadratic EDGE --->
4525 if ( nbDouble+nbSame == 2 )
4527 if(nbSame==0) { // ---> quadratic quadrangle
4528 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
4529 prevNod[2], midlNod[1], nextNod[2], midlNod[0]);
4531 else { //(nbSame==1) // ---> quadratic triangle
4533 return; // medium node on axis
4535 else if(sames[0]==0)
4536 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1],
4537 prevNod[2], midlNod[1], nextNod[2] );
4539 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[0],
4540 prevNod[2], nextNod[2], midlNod[0]);
4543 else if ( nbDouble == 3 )
4545 if ( nbSame == 0 ) { // ---> bi-quadratic quadrangle
4546 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
4547 prevNod[2], midlNod[1], nextNod[2], midlNod[0], midlNod[2]);
4554 case SMDSEntity_Quadrangle: { // sweep QUADRANGLE --->
4555 if ( nbDouble > 0 ) break;
4557 if ( nbSame == 0 ) // ---> hexahedron
4558 aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ], prevNod[ 3 ],
4559 nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ], nextNod[ 3 ]);
4561 else if ( nbSame == 1 ) { // ---> pyramid + pentahedron
4562 aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
4563 nextNod[ iAfterSame ], nextNod[ iBeforeSame ],
4564 nextNod[ iSameNode ]);
4565 newElems.push_back( aNewElem );
4566 aNewElem = aMesh->AddVolume (prevNod[ iAfterSame ], prevNod[ iOpposSame ],
4567 prevNod[ iBeforeSame ], nextNod[ iAfterSame ],
4568 nextNod[ iOpposSame ], nextNod[ iBeforeSame ] );
4570 else if ( nbSame == 2 ) { // ---> pentahedron
4571 if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] )
4572 // iBeforeSame is same too
4573 aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iOpposSame ],
4574 nextNod[ iOpposSame ], prevNod[ iSameNode ],
4575 prevNod[ iAfterSame ], nextNod[ iAfterSame ]);
4577 // iAfterSame is same too
4578 aNewElem = aMesh->AddVolume (prevNod[ iSameNode ], prevNod[ iBeforeSame ],
4579 nextNod[ iBeforeSame ], prevNod[ iAfterSame ],
4580 prevNod[ iOpposSame ], nextNod[ iOpposSame ]);
4584 case SMDSEntity_Quad_Triangle: // sweep (Bi)Quadratic TRIANGLE --->
4585 case SMDSEntity_BiQuad_Triangle: /* ??? */ {
4586 if ( nbDouble+nbSame != 3 ) break;
4588 // ---> pentahedron with 15 nodes
4589 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
4590 nextNod[0], nextNod[1], nextNod[2],
4591 prevNod[3], prevNod[4], prevNod[5],
4592 nextNod[3], nextNod[4], nextNod[5],
4593 midlNod[0], midlNod[1], midlNod[2]);
4595 else if(nbSame==1) {
4596 // ---> 2d order pyramid of 13 nodes
4597 int apex = iSameNode;
4598 int i0 = ( apex + 1 ) % nbCorners;
4599 int i1 = ( apex - 1 + nbCorners ) % nbCorners;
4603 aNewElem = aMesh->AddVolume(prevNod[i1], prevNod[i0],
4604 nextNod[i0], nextNod[i1], prevNod[apex],
4605 prevNod[i01], midlNod[i0],
4606 nextNod[i01], midlNod[i1],
4607 prevNod[i1a], prevNod[i0a],
4608 nextNod[i0a], nextNod[i1a]);
4610 else if(nbSame==2) {
4611 // ---> 2d order tetrahedron of 10 nodes
4612 int n1 = iNotSameNode;
4613 int n2 = ( n1 + 1 ) % nbCorners;
4614 int n3 = ( n1 + nbCorners - 1 ) % nbCorners;
4618 aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], prevNod[n3], nextNod[n1],
4619 prevNod[n12], prevNod[n23], prevNod[n31],
4620 midlNod[n1], nextNod[n12], nextNod[n31]);
4624 case SMDSEntity_Quad_Quadrangle: { // sweep Quadratic QUADRANGLE --->
4626 if ( nbDouble != 4 ) break;
4627 // ---> hexahedron with 20 nodes
4628 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
4629 nextNod[0], nextNod[1], nextNod[2], nextNod[3],
4630 prevNod[4], prevNod[5], prevNod[6], prevNod[7],
4631 nextNod[4], nextNod[5], nextNod[6], nextNod[7],
4632 midlNod[0], midlNod[1], midlNod[2], midlNod[3]);
4634 else if(nbSame==1) {
4635 // ---> pyramid + pentahedron - can not be created since it is needed
4636 // additional middle node at the center of face
4637 INFOS( " Sweep for face " << elem->GetID() << " can not be created" );
4640 else if( nbSame == 2 ) {
4641 if ( nbDouble != 2 ) break;
4642 // ---> 2d order Pentahedron with 15 nodes
4644 if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] ) {
4645 // iBeforeSame is same too
4652 // iAfterSame is same too
4662 aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], nextNod[n2],
4663 prevNod[n4], prevNod[n5], nextNod[n5],
4664 prevNod[n12], midlNod[n2], nextNod[n12],
4665 prevNod[n45], midlNod[n5], nextNod[n45],
4666 prevNod[n14], prevNod[n25], nextNod[n25]);
4670 case SMDSEntity_BiQuad_Quadrangle: { // sweep BiQuadratic QUADRANGLE --->
4672 if( nbSame == 0 && nbDouble == 9 ) {
4673 // ---> tri-quadratic hexahedron with 27 nodes
4674 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
4675 nextNod[0], nextNod[1], nextNod[2], nextNod[3],
4676 prevNod[4], prevNod[5], prevNod[6], prevNod[7],
4677 nextNod[4], nextNod[5], nextNod[6], nextNod[7],
4678 midlNod[0], midlNod[1], midlNod[2], midlNod[3],
4679 prevNod[8], // bottom center
4680 midlNod[4], midlNod[5], midlNod[6], midlNod[7],
4681 nextNod[8], // top center
4682 midlNod[8]);// elem center
4690 case SMDSEntity_Polygon: { // sweep POLYGON
4692 if ( nbNodes == 6 && nbSame == 0 && nbDouble == 0 ) {
4693 // ---> hexagonal prism
4694 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
4695 prevNod[3], prevNod[4], prevNod[5],
4696 nextNod[0], nextNod[1], nextNod[2],
4697 nextNod[3], nextNod[4], nextNod[5]);
4701 case SMDSEntity_Ball:
4706 } // switch ( baseType )
4709 if ( !aNewElem && elem->GetType() == SMDSAbs_Face ) // try to create a polyherdal prism
4711 if ( baseType != SMDSEntity_Polygon )
4713 const std::vector<int>& ind = SMDS_MeshCell::interlacedSmdsOrder(baseType,nbNodes);
4714 SMDS_MeshCell::applyInterlace( ind, prevNod );
4715 SMDS_MeshCell::applyInterlace( ind, nextNod );
4716 SMDS_MeshCell::applyInterlace( ind, midlNod );
4717 SMDS_MeshCell::applyInterlace( ind, itNN );
4718 SMDS_MeshCell::applyInterlace( ind, isSingleNode );
4719 baseType = SMDSEntity_Polygon; // WARNING: change baseType !!!!
4721 vector<const SMDS_MeshNode*> polyedre_nodes (nbNodes*2 + 4*nbNodes);
4722 vector<int> quantities (nbNodes + 2);
4723 polyedre_nodes.clear();
4727 for (int inode = 0; inode < nbNodes; inode++)
4728 polyedre_nodes.push_back( prevNod[inode] );
4729 quantities.push_back( nbNodes );
4732 polyedre_nodes.push_back( nextNod[0] );
4733 for (int inode = nbNodes; inode-1; --inode )
4734 polyedre_nodes.push_back( nextNod[inode-1] );
4735 quantities.push_back( nbNodes );
4743 const int iQuad = elem->IsQuadratic();
4744 for (int iface = 0; iface < nbNodes; iface += 1+iQuad )
4746 const int prevNbNodes = polyedre_nodes.size(); // to detect degenerated face
4747 int inextface = (iface+1+iQuad) % nbNodes;
4748 int imid = (iface+1) % nbNodes;
4749 polyedre_nodes.push_back( prevNod[inextface] ); // 0
4750 if ( iQuad ) polyedre_nodes.push_back( prevNod[imid] ); // 4
4751 polyedre_nodes.push_back( prevNod[iface] ); // 1
4752 if ( prevNod[iface] != nextNod[iface] ) // 1 != 2
4754 if ( midlNod[ iface ]) polyedre_nodes.push_back( midlNod[ iface ]); // 5
4755 polyedre_nodes.push_back( nextNod[iface] ); // 2
4757 if ( iQuad ) polyedre_nodes.push_back( nextNod[imid] ); // 6
4758 if ( prevNod[inextface] != nextNod[inextface] ) // 0 != 3
4760 polyedre_nodes.push_back( nextNod[inextface] ); // 3
4761 if ( midlNod[ inextface ]) polyedre_nodes.push_back( midlNod[ inextface ]);// 7
4763 const int nbFaceNodes = polyedre_nodes.size() - prevNbNodes;
4764 if ( nbFaceNodes > 2 )
4765 quantities.push_back( nbFaceNodes );
4766 else // degenerated face
4767 polyedre_nodes.resize( prevNbNodes );
4769 aNewElem = aMesh->AddPolyhedralVolume (polyedre_nodes, quantities);
4771 } // try to create a polyherdal prism
4774 newElems.push_back( aNewElem );
4775 myLastCreatedElems.Append(aNewElem);
4776 srcElements.Append( elem );
4779 // set new prev nodes
4780 for ( iNode = 0; iNode < nbNodes; iNode++ )
4781 prevNod[ iNode ] = nextNod[ iNode ];
4786 //=======================================================================
4788 * \brief Create 1D and 2D elements around swept elements
4789 * \param mapNewNodes - source nodes and ones generated from them
4790 * \param newElemsMap - source elements and ones generated from them
4791 * \param elemNewNodesMap - nodes generated from each node of each element
4792 * \param elemSet - all swept elements
4793 * \param nbSteps - number of sweeping steps
4794 * \param srcElements - to append elem for each generated element
4796 //=======================================================================
4798 void SMESH_MeshEditor::makeWalls (TNodeOfNodeListMap & mapNewNodes,
4799 TTElemOfElemListMap & newElemsMap,
4800 TElemOfVecOfNnlmiMap & elemNewNodesMap,
4801 TIDSortedElemSet& elemSet,
4803 SMESH_SequenceOfElemPtr& srcElements)
4805 ASSERT( newElemsMap.size() == elemNewNodesMap.size() );
4806 SMESHDS_Mesh* aMesh = GetMeshDS();
4808 // Find nodes belonging to only one initial element - sweep them into edges.
4810 TNodeOfNodeListMapItr nList = mapNewNodes.begin();
4811 for ( ; nList != mapNewNodes.end(); nList++ )
4813 const SMDS_MeshNode* node =
4814 static_cast<const SMDS_MeshNode*>( nList->first );
4815 if ( newElemsMap.count( node ))
4816 continue; // node was extruded into edge
4817 SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator();
4818 int nbInitElems = 0;
4819 const SMDS_MeshElement* el = 0;
4820 SMDSAbs_ElementType highType = SMDSAbs_Edge; // count most complex elements only
4821 while ( eIt->more() && nbInitElems < 2 ) {
4822 const SMDS_MeshElement* e = eIt->next();
4823 SMDSAbs_ElementType type = e->GetType();
4824 if ( type == SMDSAbs_Volume || type < highType ) continue;
4825 if ( type > highType ) {
4830 nbInitElems += elemSet.count(el);
4832 if ( nbInitElems < 2 ) {
4833 bool NotCreateEdge = el && el->IsMediumNode(node);
4834 if(!NotCreateEdge) {
4835 vector<TNodeOfNodeListMapItr> newNodesItVec( 1, nList );
4836 list<const SMDS_MeshElement*> newEdges;
4837 sweepElement( node, newNodesItVec, newEdges, nbSteps, srcElements );
4842 // Make a ceiling for each element ie an equal element of last new nodes.
4843 // Find free links of faces - make edges and sweep them into faces.
4845 ElemFeatures polyFace( SMDSAbs_Face, /*isPoly=*/true ), anyFace;
4847 TTElemOfElemListMap::iterator itElem = newElemsMap.begin();
4848 TElemOfVecOfNnlmiMap::iterator itElemNodes = elemNewNodesMap.begin();
4849 for ( ; itElem != newElemsMap.end(); itElem++, itElemNodes++ )
4851 const SMDS_MeshElement* elem = itElem->first;
4852 vector<TNodeOfNodeListMapItr>& vecNewNodes = itElemNodes->second;
4854 if(itElem->second.size()==0) continue;
4856 const bool isQuadratic = elem->IsQuadratic();
4858 if ( elem->GetType() == SMDSAbs_Edge ) {
4859 // create a ceiling edge
4860 if ( !isQuadratic ) {
4861 if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
4862 vecNewNodes[ 1 ]->second.back())) {
4863 myLastCreatedElems.Append(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
4864 vecNewNodes[ 1 ]->second.back()));
4865 srcElements.Append( elem );
4869 if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
4870 vecNewNodes[ 1 ]->second.back(),
4871 vecNewNodes[ 2 ]->second.back())) {
4872 myLastCreatedElems.Append(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
4873 vecNewNodes[ 1 ]->second.back(),
4874 vecNewNodes[ 2 ]->second.back()));
4875 srcElements.Append( elem );
4879 if ( elem->GetType() != SMDSAbs_Face )
4882 bool hasFreeLinks = false;
4884 TIDSortedElemSet avoidSet;
4885 avoidSet.insert( elem );
4887 set<const SMDS_MeshNode*> aFaceLastNodes;
4888 int iNode, nbNodes = vecNewNodes.size();
4889 if ( !isQuadratic ) {
4890 // loop on the face nodes
4891 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4892 aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4893 // look for free links of the face
4894 int iNext = ( iNode + 1 == nbNodes ) ? 0 : iNode + 1;
4895 const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
4896 const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
4897 // check if a link n1-n2 is free
4898 if ( ! SMESH_MeshAlgos::FindFaceInSet ( n1, n2, elemSet, avoidSet )) {
4899 hasFreeLinks = true;
4900 // make a new edge and a ceiling for a new edge
4901 const SMDS_MeshElement* edge;
4902 if ( ! ( edge = aMesh->FindEdge( n1, n2 ))) {
4903 myLastCreatedElems.Append( edge = aMesh->AddEdge( n1, n2 )); // free link edge
4904 srcElements.Append( myLastCreatedElems.Last() );
4906 n1 = vecNewNodes[ iNode ]->second.back();
4907 n2 = vecNewNodes[ iNext ]->second.back();
4908 if ( !aMesh->FindEdge( n1, n2 )) {
4909 myLastCreatedElems.Append(aMesh->AddEdge( n1, n2 )); // new edge ceiling
4910 srcElements.Append( edge );
4915 else { // elem is quadratic face
4916 int nbn = nbNodes/2;
4917 for ( iNode = 0; iNode < nbn; iNode++ ) {
4918 aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4919 int iNext = ( iNode + 1 == nbn ) ? 0 : iNode + 1;
4920 const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
4921 const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
4922 const SMDS_MeshNode* n3 = vecNewNodes[ iNode+nbn ]->first;
4923 // check if a link is free
4924 if ( ! SMESH_MeshAlgos::FindFaceInSet ( n1, n2, elemSet, avoidSet ) &&
4925 ! SMESH_MeshAlgos::FindFaceInSet ( n1, n3, elemSet, avoidSet ) &&
4926 ! SMESH_MeshAlgos::FindFaceInSet ( n3, n2, elemSet, avoidSet ) ) {
4927 hasFreeLinks = true;
4928 // make an edge and a ceiling for a new edge
4930 if ( !aMesh->FindEdge( n1, n2, n3 )) {
4931 myLastCreatedElems.Append(aMesh->AddEdge( n1, n2, n3 )); // free link edge
4932 srcElements.Append( elem );
4934 n1 = vecNewNodes[ iNode ]->second.back();
4935 n2 = vecNewNodes[ iNext ]->second.back();
4936 n3 = vecNewNodes[ iNode+nbn ]->second.back();
4937 if ( !aMesh->FindEdge( n1, n2, n3 )) {
4938 myLastCreatedElems.Append(aMesh->AddEdge( n1, n2, n3 )); // ceiling edge
4939 srcElements.Append( elem );
4943 for ( iNode = nbn; iNode < nbNodes; iNode++ ) {
4944 aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4948 // sweep free links into faces
4950 if ( hasFreeLinks ) {
4951 list<const SMDS_MeshElement*> & newVolumes = itElem->second;
4952 int iVol, volNb, nbVolumesByStep = newVolumes.size() / nbSteps;
4954 set<const SMDS_MeshNode*> initNodeSet, topNodeSet, faceNodeSet;
4955 set<const SMDS_MeshNode*> initNodeSetNoCenter/*, topNodeSetNoCenter*/;
4956 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4957 initNodeSet.insert( vecNewNodes[ iNode ]->first );
4958 topNodeSet .insert( vecNewNodes[ iNode ]->second.back() );
4960 if ( isQuadratic && nbNodes % 2 ) { // node set for the case of a biquadratic
4961 initNodeSetNoCenter = initNodeSet; // swept face and a not biquadratic volume
4962 initNodeSetNoCenter.erase( vecNewNodes.back()->first );
4964 for ( volNb = 0; volNb < nbVolumesByStep; volNb++ ) {
4965 list<const SMDS_MeshElement*>::iterator v = newVolumes.begin();
4966 std::advance( v, volNb );
4967 // find indices of free faces of a volume and their source edges
4968 list< int > freeInd;
4969 list< const SMDS_MeshElement* > srcEdges; // source edges of free faces
4970 SMDS_VolumeTool vTool( *v, /*ignoreCentralNodes=*/false );
4971 int iF, nbF = vTool.NbFaces();
4972 for ( iF = 0; iF < nbF; iF ++ ) {
4973 if (vTool.IsFreeFace( iF ) &&
4974 vTool.GetFaceNodes( iF, faceNodeSet ) &&
4975 initNodeSet != faceNodeSet) // except an initial face
4977 if ( nbSteps == 1 && faceNodeSet == topNodeSet )
4979 if ( faceNodeSet == initNodeSetNoCenter )
4981 freeInd.push_back( iF );
4982 // find source edge of a free face iF
4983 vector<const SMDS_MeshNode*> commonNodes; // shared by the initial and free faces
4984 vector<const SMDS_MeshNode*>::iterator lastCommom;
4985 commonNodes.resize( nbNodes, 0 );
4986 lastCommom = std::set_intersection( faceNodeSet.begin(), faceNodeSet.end(),
4987 initNodeSet.begin(), initNodeSet.end(),
4988 commonNodes.begin());
4989 if ( std::distance( commonNodes.begin(), lastCommom ) == 3 )
4990 srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1],commonNodes[2]));
4992 srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1]));
4994 if ( !srcEdges.back() )
4996 cout << "SMESH_MeshEditor::makeWalls(), no source edge found for a free face #"
4997 << iF << " of volume #" << vTool.ID() << endl;
5002 if ( freeInd.empty() )
5005 // create wall faces for all steps;
5006 // if such a face has been already created by sweep of edge,
5007 // assure that its orientation is OK
5008 for ( int iStep = 0; iStep < nbSteps; iStep++ )
5010 vTool.Set( *v, /*ignoreCentralNodes=*/false );
5011 vTool.SetExternalNormal();
5012 const int nextShift = vTool.IsForward() ? +1 : -1;
5013 list< int >::iterator ind = freeInd.begin();
5014 list< const SMDS_MeshElement* >::iterator srcEdge = srcEdges.begin();
5015 for ( ; ind != freeInd.end(); ++ind, ++srcEdge ) // loop on free faces
5017 const SMDS_MeshNode** nodes = vTool.GetFaceNodes( *ind );
5018 int nbn = vTool.NbFaceNodes( *ind );
5019 const SMDS_MeshElement * f = 0;
5020 if ( nbn == 3 ) ///// triangle
5022 f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ]);
5024 nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
5026 const SMDS_MeshNode* newOrder[3] = { nodes[ 1 - nextShift ],
5028 nodes[ 1 + nextShift ] };
5030 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5032 myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
5036 else if ( nbn == 4 ) ///// quadrangle
5038 f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ], nodes[ 3 ]);
5040 nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
5042 const SMDS_MeshNode* newOrder[4] = { nodes[ 0 ], nodes[ 2-nextShift ],
5043 nodes[ 2 ], nodes[ 2+nextShift ] };
5045 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5047 myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
5048 newOrder[ 2 ], newOrder[ 3 ]));
5051 else if ( nbn == 6 && isQuadratic ) /////// quadratic triangle
5053 f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[1], nodes[3], nodes[5] );
5055 nodes[2] != f->GetNodeWrap( f->GetNodeIndex( nodes[0] ) + 2*nextShift ))
5057 const SMDS_MeshNode* newOrder[6] = { nodes[2 - 2*nextShift],
5059 nodes[2 + 2*nextShift],
5060 nodes[3 - 2*nextShift],
5062 nodes[3 + 2*nextShift]};
5064 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5066 myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ],
5074 else if ( nbn == 8 && isQuadratic ) /////// quadratic quadrangle
5076 f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[6],
5077 nodes[1], nodes[3], nodes[5], nodes[7] );
5079 nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
5081 const SMDS_MeshNode* newOrder[8] = { nodes[0],
5082 nodes[4 - 2*nextShift],
5084 nodes[4 + 2*nextShift],
5086 nodes[5 - 2*nextShift],
5088 nodes[5 + 2*nextShift] };
5090 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5092 myLastCreatedElems.Append(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
5093 newOrder[ 2 ], newOrder[ 3 ],
5094 newOrder[ 4 ], newOrder[ 5 ],
5095 newOrder[ 6 ], newOrder[ 7 ]));
5098 else if ( nbn == 9 && isQuadratic ) /////// bi-quadratic quadrangle
5100 f = aMesh->FindElement( vector<const SMDS_MeshNode*>( nodes, nodes+nbn ),
5101 SMDSAbs_Face, /*noMedium=*/false);
5103 nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
5105 const SMDS_MeshNode* newOrder[9] = { nodes[0],
5106 nodes[4 - 2*nextShift],
5108 nodes[4 + 2*nextShift],
5110 nodes[5 - 2*nextShift],
5112 nodes[5 + 2*nextShift],
5115 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5117 myLastCreatedElems.Append(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
5118 newOrder[ 2 ], newOrder[ 3 ],
5119 newOrder[ 4 ], newOrder[ 5 ],
5120 newOrder[ 6 ], newOrder[ 7 ],
5124 else //////// polygon
5126 vector<const SMDS_MeshNode*> polygon_nodes ( nodes, nodes+nbn );
5127 const SMDS_MeshFace * f = aMesh->FindFace( polygon_nodes );
5129 nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + nextShift ))
5131 if ( !vTool.IsForward() )
5132 std::reverse( polygon_nodes.begin(), polygon_nodes.end());
5134 aMesh->ChangeElementNodes( f, &polygon_nodes[0], nbn );
5136 AddElement( polygon_nodes, polyFace.SetQuad( (*v)->IsQuadratic() ));
5140 while ( srcElements.Length() < myLastCreatedElems.Length() )
5141 srcElements.Append( *srcEdge );
5143 } // loop on free faces
5145 // go to the next volume
5147 while ( iVol++ < nbVolumesByStep ) v++;
5150 } // loop on volumes of one step
5151 } // sweep free links into faces
5153 // Make a ceiling face with a normal external to a volume
5155 // use SMDS_VolumeTool to get a correctly ordered nodes of a ceiling face
5156 SMDS_VolumeTool lastVol( itElem->second.back(), /*ignoreCentralNodes=*/false );
5157 int iF = lastVol.GetFaceIndex( aFaceLastNodes );
5159 if ( iF < 0 && isQuadratic && nbNodes % 2 ) { // remove a central node of biquadratic
5160 aFaceLastNodes.erase( vecNewNodes.back()->second.back() );
5161 iF = lastVol.GetFaceIndex( aFaceLastNodes );
5165 lastVol.SetExternalNormal();
5166 const SMDS_MeshNode** nodes = lastVol.GetFaceNodes( iF );
5167 const int nbn = lastVol.NbFaceNodes( iF );
5168 vector<const SMDS_MeshNode*> nodeVec( nodes, nodes+nbn );
5169 if ( !hasFreeLinks ||
5170 !aMesh->FindElement( nodeVec, SMDSAbs_Face, /*noMedium=*/false) )
5172 const vector<int>& interlace =
5173 SMDS_MeshCell::interlacedSmdsOrder( elem->GetEntityType(), nbn );
5174 SMDS_MeshCell::applyInterlaceRev( interlace, nodeVec );
5176 if ( const SMDS_MeshElement* face = AddElement( nodeVec, anyFace.Init( elem )))
5177 myLastCreatedElems.Append( face );
5179 while ( srcElements.Length() < myLastCreatedElems.Length() )
5180 srcElements.Append( elem );
5183 } // loop on swept elements
5186 //=======================================================================
5187 //function : RotationSweep
5189 //=======================================================================
5191 SMESH_MeshEditor::PGroupIDs
5192 SMESH_MeshEditor::RotationSweep(TIDSortedElemSet theElemSets[2],
5193 const gp_Ax1& theAxis,
5194 const double theAngle,
5195 const int theNbSteps,
5196 const double theTol,
5197 const bool theMakeGroups,
5198 const bool theMakeWalls)
5200 myLastCreatedElems.Clear();
5201 myLastCreatedNodes.Clear();
5203 // source elements for each generated one
5204 SMESH_SequenceOfElemPtr srcElems, srcNodes;
5206 MESSAGE( "RotationSweep()");
5208 aTrsf.SetRotation( theAxis, theAngle );
5210 aTrsf2.SetRotation( theAxis, theAngle/2. );
5212 gp_Lin aLine( theAxis );
5213 double aSqTol = theTol * theTol;
5215 SMESHDS_Mesh* aMesh = GetMeshDS();
5217 TNodeOfNodeListMap mapNewNodes;
5218 TElemOfVecOfNnlmiMap mapElemNewNodes;
5219 TTElemOfElemListMap newElemsMap;
5221 const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
5222 myMesh->NbFaces(ORDER_QUADRATIC) +
5223 myMesh->NbVolumes(ORDER_QUADRATIC) );
5224 // loop on theElemSets
5225 setElemsFirst( theElemSets );
5226 TIDSortedElemSet::iterator itElem;
5227 for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
5229 TIDSortedElemSet& theElems = theElemSets[ is2ndSet ];
5230 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
5231 const SMDS_MeshElement* elem = *itElem;
5232 if ( !elem || elem->GetType() == SMDSAbs_Volume )
5234 vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5235 newNodesItVec.reserve( elem->NbNodes() );
5237 // loop on elem nodes
5238 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5239 while ( itN->more() )
5241 const SMDS_MeshNode* node = cast2Node( itN->next() );
5243 gp_XYZ aXYZ( node->X(), node->Y(), node->Z() );
5245 aXYZ.Coord( coord[0], coord[1], coord[2] );
5246 bool isOnAxis = ( aLine.SquareDistance( aXYZ ) <= aSqTol );
5248 // check if a node has been already sweeped
5249 TNodeOfNodeListMapItr nIt =
5250 mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5251 list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5252 if ( listNewNodes.empty() )
5254 // check if we are to create medium nodes between corner ones
5255 bool needMediumNodes = false;
5256 if ( isQuadraticMesh )
5258 SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
5259 while (it->more() && !needMediumNodes )
5261 const SMDS_MeshElement* invElem = it->next();
5262 if ( invElem != elem && !theElems.count( invElem )) continue;
5263 needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
5264 if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
5265 needMediumNodes = true;
5270 const SMDS_MeshNode * newNode = node;
5271 for ( int i = 0; i < theNbSteps; i++ ) {
5273 if ( needMediumNodes ) // create a medium node
5275 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
5276 newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5277 myLastCreatedNodes.Append(newNode);
5278 srcNodes.Append( node );
5279 listNewNodes.push_back( newNode );
5280 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
5283 aTrsf.Transforms( coord[0], coord[1], coord[2] );
5285 // create a corner node
5286 newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5287 myLastCreatedNodes.Append(newNode);
5288 srcNodes.Append( node );
5289 listNewNodes.push_back( newNode );
5292 listNewNodes.push_back( newNode );
5293 // if ( needMediumNodes )
5294 // listNewNodes.push_back( newNode );
5298 newNodesItVec.push_back( nIt );
5300 // make new elements
5301 sweepElement( elem, newNodesItVec, newElemsMap[elem], theNbSteps, srcElems );
5306 makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElemSets[0], theNbSteps, srcElems );
5308 PGroupIDs newGroupIDs;
5309 if ( theMakeGroups )
5310 newGroupIDs = generateGroups( srcNodes, srcElems, "rotated");
5315 //=======================================================================
5316 //function : ExtrusParam
5317 //purpose : standard construction
5318 //=======================================================================
5320 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const gp_Vec& theStep,
5321 const int theNbSteps,
5323 const double theTolerance):
5325 myFlags( theFlags ),
5326 myTolerance( theTolerance ),
5327 myElemsToUse( NULL )
5329 mySteps = new TColStd_HSequenceOfReal;
5330 const double stepSize = theStep.Magnitude();
5331 for (int i=1; i<=theNbSteps; i++ )
5332 mySteps->Append( stepSize );
5334 if (( theFlags & EXTRUSION_FLAG_SEW ) &&
5335 ( theTolerance > 0 ))
5337 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDirAndSew;
5341 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDir;
5345 //=======================================================================
5346 //function : ExtrusParam
5347 //purpose : steps are given explicitly
5348 //=======================================================================
5350 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const gp_Dir& theDir,
5351 Handle(TColStd_HSequenceOfReal) theSteps,
5353 const double theTolerance):
5355 mySteps( theSteps ),
5356 myFlags( theFlags ),
5357 myTolerance( theTolerance ),
5358 myElemsToUse( NULL )
5360 if (( theFlags & EXTRUSION_FLAG_SEW ) &&
5361 ( theTolerance > 0 ))
5363 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDirAndSew;
5367 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDir;
5371 //=======================================================================
5372 //function : ExtrusParam
5373 //purpose : for extrusion by normal
5374 //=======================================================================
5376 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const double theStepSize,
5377 const int theNbSteps,
5381 mySteps( new TColStd_HSequenceOfReal ),
5382 myFlags( theFlags ),
5384 myElemsToUse( NULL )
5386 for (int i = 0; i < theNbSteps; i++ )
5387 mySteps->Append( theStepSize );
5391 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByNormal1D;
5395 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByNormal2D;
5399 //=======================================================================
5400 //function : ExtrusParam::SetElementsToUse
5401 //purpose : stores elements to use for extrusion by normal, depending on
5402 // state of EXTRUSION_FLAG_USE_INPUT_ELEMS_ONLY flag
5403 //=======================================================================
5405 void SMESH_MeshEditor::ExtrusParam::SetElementsToUse( const TIDSortedElemSet& elems )
5407 myElemsToUse = ToUseInpElemsOnly() ? & elems : 0;
5410 //=======================================================================
5411 //function : ExtrusParam::beginStepIter
5412 //purpose : prepare iteration on steps
5413 //=======================================================================
5415 void SMESH_MeshEditor::ExtrusParam::beginStepIter( bool withMediumNodes )
5417 myWithMediumNodes = withMediumNodes;
5421 //=======================================================================
5422 //function : ExtrusParam::moreSteps
5423 //purpose : are there more steps?
5424 //=======================================================================
5426 bool SMESH_MeshEditor::ExtrusParam::moreSteps()
5428 return myNextStep <= mySteps->Length() || !myCurSteps.empty();
5430 //=======================================================================
5431 //function : ExtrusParam::nextStep
5432 //purpose : returns the next step
5433 //=======================================================================
5435 double SMESH_MeshEditor::ExtrusParam::nextStep()
5438 if ( !myCurSteps.empty() )
5440 res = myCurSteps.back();
5441 myCurSteps.pop_back();
5443 else if ( myNextStep <= mySteps->Length() )
5445 myCurSteps.push_back( mySteps->Value( myNextStep ));
5447 if ( myWithMediumNodes )
5449 myCurSteps.back() /= 2.;
5450 myCurSteps.push_back( myCurSteps.back() );
5457 //=======================================================================
5458 //function : ExtrusParam::makeNodesByDir
5459 //purpose : create nodes for standard extrusion
5460 //=======================================================================
5462 int SMESH_MeshEditor::ExtrusParam::
5463 makeNodesByDir( SMESHDS_Mesh* mesh,
5464 const SMDS_MeshNode* srcNode,
5465 std::list<const SMDS_MeshNode*> & newNodes,
5466 const bool makeMediumNodes)
5468 gp_XYZ p = SMESH_TNodeXYZ( srcNode );
5471 for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5473 p += myDir.XYZ() * nextStep();
5474 const SMDS_MeshNode * newNode = mesh->AddNode( p.X(), p.Y(), p.Z() );
5475 newNodes.push_back( newNode );
5480 //=======================================================================
5481 //function : ExtrusParam::makeNodesByDirAndSew
5482 //purpose : create nodes for standard extrusion with sewing
5483 //=======================================================================
5485 int SMESH_MeshEditor::ExtrusParam::
5486 makeNodesByDirAndSew( SMESHDS_Mesh* mesh,
5487 const SMDS_MeshNode* srcNode,
5488 std::list<const SMDS_MeshNode*> & newNodes,
5489 const bool makeMediumNodes)
5491 gp_XYZ P1 = SMESH_TNodeXYZ( srcNode );
5494 for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5496 P1 += myDir.XYZ() * nextStep();
5498 // try to search in sequence of existing nodes
5499 // if myNodes.Length()>0 we 'nave to use given sequence
5500 // else - use all nodes of mesh
5501 const SMDS_MeshNode * node = 0;
5502 if ( myNodes.Length() > 0 ) {
5504 for(i=1; i<=myNodes.Length(); i++) {
5505 gp_XYZ P2 = SMESH_TNodeXYZ( myNodes.Value(i) );
5506 if (( P1 - P2 ).SquareModulus() < myTolerance * myTolerance )
5508 node = myNodes.Value(i);
5514 SMDS_NodeIteratorPtr itn = mesh->nodesIterator();
5515 while(itn->more()) {
5516 SMESH_TNodeXYZ P2( itn->next() );
5517 if (( P1 - P2 ).SquareModulus() < myTolerance * myTolerance )
5526 node = mesh->AddNode( P1.X(), P1.Y(), P1.Z() );
5528 newNodes.push_back( node );
5535 //=======================================================================
5536 //function : ExtrusParam::makeNodesByNormal2D
5537 //purpose : create nodes for extrusion using normals of faces
5538 //=======================================================================
5540 int SMESH_MeshEditor::ExtrusParam::
5541 makeNodesByNormal2D( SMESHDS_Mesh* mesh,
5542 const SMDS_MeshNode* srcNode,
5543 std::list<const SMDS_MeshNode*> & newNodes,
5544 const bool makeMediumNodes)
5546 const bool alongAvgNorm = ( myFlags & EXTRUSION_FLAG_BY_AVG_NORMAL );
5548 gp_XYZ p = SMESH_TNodeXYZ( srcNode );
5550 // get normals to faces sharing srcNode
5551 vector< gp_XYZ > norms, baryCenters;
5552 gp_XYZ norm, avgNorm( 0,0,0 );
5553 SMDS_ElemIteratorPtr faceIt = srcNode->GetInverseElementIterator( SMDSAbs_Face );
5554 while ( faceIt->more() )
5556 const SMDS_MeshElement* face = faceIt->next();
5557 if ( myElemsToUse && !myElemsToUse->count( face ))
5559 if ( SMESH_MeshAlgos::FaceNormal( face, norm, /*normalized=*/true ))
5561 norms.push_back( norm );
5563 if ( !alongAvgNorm )
5567 for ( SMDS_ElemIteratorPtr nIt = face->nodesIterator(); nIt->more(); ++nbN )
5568 bc += SMESH_TNodeXYZ( nIt->next() );
5569 baryCenters.push_back( bc / nbN );
5574 if ( norms.empty() ) return 0;
5576 double normSize = avgNorm.Modulus();
5577 if ( normSize < std::numeric_limits<double>::min() )
5580 if ( myFlags & EXTRUSION_FLAG_BY_AVG_NORMAL ) // extrude along avgNorm
5583 return makeNodesByDir( mesh, srcNode, newNodes, makeMediumNodes );
5586 avgNorm /= normSize;
5589 for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5592 double stepSize = nextStep();
5594 if ( norms.size() > 1 )
5596 for ( size_t iF = 0; iF < norms.size(); ++iF ) // loop on faces
5598 // translate plane of a face
5599 baryCenters[ iF ] += norms[ iF ] * stepSize;
5601 // find point of intersection of the face plane located at baryCenters[ iF ]
5602 // and avgNorm located at pNew
5603 double d = -( norms[ iF ] * baryCenters[ iF ]); // d of plane equation ax+by+cz+d=0
5604 double dot = ( norms[ iF ] * avgNorm );
5605 if ( dot < std::numeric_limits<double>::min() )
5606 dot = stepSize * 1e-3;
5607 double step = -( norms[ iF ] * pNew + d ) / dot;
5608 pNew += step * avgNorm;
5613 pNew += stepSize * avgNorm;
5617 const SMDS_MeshNode * newNode = mesh->AddNode( p.X(), p.Y(), p.Z() );
5618 newNodes.push_back( newNode );
5623 //=======================================================================
5624 //function : ExtrusParam::makeNodesByNormal1D
5625 //purpose : create nodes for extrusion using normals of edges
5626 //=======================================================================
5628 int SMESH_MeshEditor::ExtrusParam::
5629 makeNodesByNormal1D( SMESHDS_Mesh* mesh,
5630 const SMDS_MeshNode* srcNode,
5631 std::list<const SMDS_MeshNode*> & newNodes,
5632 const bool makeMediumNodes)
5634 throw SALOME_Exception("Extrusion 1D by Normal not implemented");
5638 //=======================================================================
5639 //function : ExtrusionSweep
5641 //=======================================================================
5643 SMESH_MeshEditor::PGroupIDs
5644 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet theElems[2],
5645 const gp_Vec& theStep,
5646 const int theNbSteps,
5647 TTElemOfElemListMap& newElemsMap,
5649 const double theTolerance)
5651 ExtrusParam aParams( theStep, theNbSteps, theFlags, theTolerance );
5652 return ExtrusionSweep( theElems, aParams, newElemsMap );
5656 //=======================================================================
5657 //function : ExtrusionSweep
5659 //=======================================================================
5661 SMESH_MeshEditor::PGroupIDs
5662 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet theElemSets[2],
5663 ExtrusParam& theParams,
5664 TTElemOfElemListMap& newElemsMap)
5666 myLastCreatedElems.Clear();
5667 myLastCreatedNodes.Clear();
5669 // source elements for each generated one
5670 SMESH_SequenceOfElemPtr srcElems, srcNodes;
5672 SMESHDS_Mesh* aMesh = GetMeshDS();
5674 setElemsFirst( theElemSets );
5675 const int nbSteps = theParams.NbSteps();
5676 theParams.SetElementsToUse( theElemSets[0] );
5678 TNodeOfNodeListMap mapNewNodes;
5679 //TNodeOfNodeVecMap mapNewNodes;
5680 TElemOfVecOfNnlmiMap mapElemNewNodes;
5681 //TElemOfVecOfMapNodesMap mapElemNewNodes;
5683 const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
5684 myMesh->NbFaces(ORDER_QUADRATIC) +
5685 myMesh->NbVolumes(ORDER_QUADRATIC) );
5687 TIDSortedElemSet::iterator itElem;
5688 for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
5690 TIDSortedElemSet& theElems = theElemSets[ is2ndSet ];
5691 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
5693 // check element type
5694 const SMDS_MeshElement* elem = *itElem;
5695 if ( !elem || elem->GetType() == SMDSAbs_Volume )
5698 const size_t nbNodes = elem->NbNodes();
5699 vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5700 newNodesItVec.reserve( nbNodes );
5702 // loop on elem nodes
5703 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5704 while ( itN->more() )
5706 // check if a node has been already sweeped
5707 const SMDS_MeshNode* node = cast2Node( itN->next() );
5708 TNodeOfNodeListMap::iterator nIt =
5709 mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5710 list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5711 if ( listNewNodes.empty() )
5715 // check if we are to create medium nodes between corner ones
5716 bool needMediumNodes = false;
5717 if ( isQuadraticMesh )
5719 SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
5720 while (it->more() && !needMediumNodes )
5722 const SMDS_MeshElement* invElem = it->next();
5723 if ( invElem != elem && !theElems.count( invElem )) continue;
5724 needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
5725 if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
5726 needMediumNodes = true;
5729 // create nodes for all steps
5730 if ( theParams.MakeNodes( GetMeshDS(), node, listNewNodes, needMediumNodes ))
5732 list<const SMDS_MeshNode*>::iterator newNodesIt = listNewNodes.begin();
5733 for ( ; newNodesIt != listNewNodes.end(); ++newNodesIt )
5735 myLastCreatedNodes.Append( *newNodesIt );
5736 srcNodes.Append( node );
5741 break; // newNodesItVec will be shorter than nbNodes
5744 newNodesItVec.push_back( nIt );
5746 // make new elements
5747 if ( newNodesItVec.size() == nbNodes )
5748 sweepElement( elem, newNodesItVec, newElemsMap[elem], nbSteps, srcElems );
5752 if ( theParams.ToMakeBoundary() ) {
5753 makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElemSets[0], nbSteps, srcElems );
5755 PGroupIDs newGroupIDs;
5756 if ( theParams.ToMakeGroups() )
5757 newGroupIDs = generateGroups( srcNodes, srcElems, "extruded");
5762 //=======================================================================
5763 //function : ExtrusionAlongTrack
5765 //=======================================================================
5766 SMESH_MeshEditor::Extrusion_Error
5767 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet theElements[2],
5768 SMESH_subMesh* theTrack,
5769 const SMDS_MeshNode* theN1,
5770 const bool theHasAngles,
5771 list<double>& theAngles,
5772 const bool theLinearVariation,
5773 const bool theHasRefPoint,
5774 const gp_Pnt& theRefPoint,
5775 const bool theMakeGroups)
5777 MESSAGE("ExtrusionAlongTrack");
5778 myLastCreatedElems.Clear();
5779 myLastCreatedNodes.Clear();
5782 std::list<double> aPrms;
5783 TIDSortedElemSet::iterator itElem;
5786 TopoDS_Edge aTrackEdge;
5787 TopoDS_Vertex aV1, aV2;
5789 SMDS_ElemIteratorPtr aItE;
5790 SMDS_NodeIteratorPtr aItN;
5791 SMDSAbs_ElementType aTypeE;
5793 TNodeOfNodeListMap mapNewNodes;
5796 aNbE = theElements[0].size() + theElements[1].size();
5799 return EXTR_NO_ELEMENTS;
5801 // 1.1 Track Pattern
5804 SMESHDS_SubMesh* pSubMeshDS = theTrack->GetSubMeshDS();
5806 aItE = pSubMeshDS->GetElements();
5807 while ( aItE->more() ) {
5808 const SMDS_MeshElement* pE = aItE->next();
5809 aTypeE = pE->GetType();
5810 // Pattern must contain links only
5811 if ( aTypeE != SMDSAbs_Edge )
5812 return EXTR_PATH_NOT_EDGE;
5815 list<SMESH_MeshEditor_PathPoint> fullList;
5817 const TopoDS_Shape& aS = theTrack->GetSubShape();
5818 // Sub-shape for the Pattern must be an Edge or Wire
5819 if( aS.ShapeType() == TopAbs_EDGE ) {
5820 aTrackEdge = TopoDS::Edge( aS );
5821 // the Edge must not be degenerated
5822 if ( SMESH_Algo::isDegenerated( aTrackEdge ) )
5823 return EXTR_BAD_PATH_SHAPE;
5824 TopExp::Vertices( aTrackEdge, aV1, aV2 );
5825 aItN = theTrack->GetFather()->GetSubMesh( aV1 )->GetSubMeshDS()->GetNodes();
5826 const SMDS_MeshNode* aN1 = aItN->next();
5827 aItN = theTrack->GetFather()->GetSubMesh( aV2 )->GetSubMeshDS()->GetNodes();
5828 const SMDS_MeshNode* aN2 = aItN->next();
5829 // starting node must be aN1 or aN2
5830 if ( !( aN1 == theN1 || aN2 == theN1 ) )
5831 return EXTR_BAD_STARTING_NODE;
5832 aItN = pSubMeshDS->GetNodes();
5833 while ( aItN->more() ) {
5834 const SMDS_MeshNode* pNode = aItN->next();
5835 const SMDS_EdgePosition* pEPos =
5836 static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
5837 double aT = pEPos->GetUParameter();
5838 aPrms.push_back( aT );
5840 //Extrusion_Error err =
5841 MakeEdgePathPoints(aPrms, aTrackEdge, (aN1==theN1), fullList);
5842 } else if( aS.ShapeType() == TopAbs_WIRE ) {
5843 list< SMESH_subMesh* > LSM;
5844 TopTools_SequenceOfShape Edges;
5845 SMESH_subMeshIteratorPtr itSM = theTrack->getDependsOnIterator(false,true);
5846 while(itSM->more()) {
5847 SMESH_subMesh* SM = itSM->next();
5849 const TopoDS_Shape& aS = SM->GetSubShape();
5852 list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
5853 int startNid = theN1->GetID();
5854 TColStd_MapOfInteger UsedNums;
5856 int NbEdges = Edges.Length();
5858 for(; i<=NbEdges; i++) {
5860 list< SMESH_subMesh* >::iterator itLSM = LSM.begin();
5861 for(; itLSM!=LSM.end(); itLSM++) {
5863 if(UsedNums.Contains(k)) continue;
5864 aTrackEdge = TopoDS::Edge( Edges.Value(k) );
5865 SMESH_subMesh* locTrack = *itLSM;
5866 SMESHDS_SubMesh* locMeshDS = locTrack->GetSubMeshDS();
5867 TopExp::Vertices( aTrackEdge, aV1, aV2 );
5868 aItN = locTrack->GetFather()->GetSubMesh(aV1)->GetSubMeshDS()->GetNodes();
5869 const SMDS_MeshNode* aN1 = aItN->next();
5870 aItN = locTrack->GetFather()->GetSubMesh(aV2)->GetSubMeshDS()->GetNodes();
5871 const SMDS_MeshNode* aN2 = aItN->next();
5872 // starting node must be aN1 or aN2
5873 if ( !( aN1->GetID() == startNid || aN2->GetID() == startNid ) ) continue;
5874 // 2. Collect parameters on the track edge
5876 aItN = locMeshDS->GetNodes();
5877 while ( aItN->more() ) {
5878 const SMDS_MeshNode* pNode = aItN->next();
5879 const SMDS_EdgePosition* pEPos =
5880 static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
5881 double aT = pEPos->GetUParameter();
5882 aPrms.push_back( aT );
5884 list<SMESH_MeshEditor_PathPoint> LPP;
5885 //Extrusion_Error err =
5886 MakeEdgePathPoints(aPrms, aTrackEdge,(aN1->GetID()==startNid), LPP);
5887 LLPPs.push_back(LPP);
5889 // update startN for search following egde
5890 if( aN1->GetID() == startNid ) startNid = aN2->GetID();
5891 else startNid = aN1->GetID();
5895 list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
5896 list<SMESH_MeshEditor_PathPoint> firstList = *itLLPP;
5897 list<SMESH_MeshEditor_PathPoint>::iterator itPP = firstList.begin();
5898 for(; itPP!=firstList.end(); itPP++) {
5899 fullList.push_back( *itPP );
5901 SMESH_MeshEditor_PathPoint PP1 = fullList.back();
5902 fullList.pop_back();
5904 for(; itLLPP!=LLPPs.end(); itLLPP++) {
5905 list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
5906 itPP = currList.begin();
5907 SMESH_MeshEditor_PathPoint PP2 = currList.front();
5908 gp_Dir D1 = PP1.Tangent();
5909 gp_Dir D2 = PP2.Tangent();
5910 gp_Dir Dnew( gp_Vec( (D1.X()+D2.X())/2, (D1.Y()+D2.Y())/2,
5911 (D1.Z()+D2.Z())/2 ) );
5912 PP1.SetTangent(Dnew);
5913 fullList.push_back(PP1);
5915 for(; itPP!=firstList.end(); itPP++) {
5916 fullList.push_back( *itPP );
5918 PP1 = fullList.back();
5919 fullList.pop_back();
5921 // if wire not closed
5922 fullList.push_back(PP1);
5926 return EXTR_BAD_PATH_SHAPE;
5929 return MakeExtrElements(theElements, fullList, theHasAngles, theAngles, theLinearVariation,
5930 theHasRefPoint, theRefPoint, theMakeGroups);
5934 //=======================================================================
5935 //function : ExtrusionAlongTrack
5937 //=======================================================================
5938 SMESH_MeshEditor::Extrusion_Error
5939 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet theElements[2],
5940 SMESH_Mesh* theTrack,
5941 const SMDS_MeshNode* theN1,
5942 const bool theHasAngles,
5943 list<double>& theAngles,
5944 const bool theLinearVariation,
5945 const bool theHasRefPoint,
5946 const gp_Pnt& theRefPoint,
5947 const bool theMakeGroups)
5949 myLastCreatedElems.Clear();
5950 myLastCreatedNodes.Clear();
5953 std::list<double> aPrms;
5954 TIDSortedElemSet::iterator itElem;
5957 TopoDS_Edge aTrackEdge;
5958 TopoDS_Vertex aV1, aV2;
5960 SMDS_ElemIteratorPtr aItE;
5961 SMDS_NodeIteratorPtr aItN;
5962 SMDSAbs_ElementType aTypeE;
5964 TNodeOfNodeListMap mapNewNodes;
5967 aNbE = theElements[0].size() + theElements[1].size();
5970 return EXTR_NO_ELEMENTS;
5972 // 1.1 Track Pattern
5975 SMESHDS_Mesh* pMeshDS = theTrack->GetMeshDS();
5977 aItE = pMeshDS->elementsIterator();
5978 while ( aItE->more() ) {
5979 const SMDS_MeshElement* pE = aItE->next();
5980 aTypeE = pE->GetType();
5981 // Pattern must contain links only
5982 if ( aTypeE != SMDSAbs_Edge )
5983 return EXTR_PATH_NOT_EDGE;
5986 list<SMESH_MeshEditor_PathPoint> fullList;
5988 const TopoDS_Shape& aS = theTrack->GetShapeToMesh();
5990 if ( !theTrack->HasShapeToMesh() ) {
5991 //Mesh without shape
5992 const SMDS_MeshNode* currentNode = NULL;
5993 const SMDS_MeshNode* prevNode = theN1;
5994 std::vector<const SMDS_MeshNode*> aNodesList;
5995 aNodesList.push_back(theN1);
5996 int nbEdges = 0, conn=0;
5997 const SMDS_MeshElement* prevElem = NULL;
5998 const SMDS_MeshElement* currentElem = NULL;
5999 int totalNbEdges = theTrack->NbEdges();
6000 SMDS_ElemIteratorPtr nIt;
6003 if( !theTrack->GetMeshDS()->Contains(theN1) ) {
6004 return EXTR_BAD_STARTING_NODE;
6007 conn = nbEdgeConnectivity(theN1);
6009 return EXTR_PATH_NOT_EDGE;
6011 aItE = theN1->GetInverseElementIterator();
6012 prevElem = aItE->next();
6013 currentElem = prevElem;
6015 if(totalNbEdges == 1 ) {
6016 nIt = currentElem->nodesIterator();
6017 currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
6018 if(currentNode == prevNode)
6019 currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
6020 aNodesList.push_back(currentNode);
6022 nIt = currentElem->nodesIterator();
6023 while( nIt->more() ) {
6024 currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
6025 if(currentNode == prevNode)
6026 currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
6027 aNodesList.push_back(currentNode);
6029 //case of the closed mesh
6030 if(currentNode == theN1) {
6035 conn = nbEdgeConnectivity(currentNode);
6037 return EXTR_PATH_NOT_EDGE;
6038 }else if( conn == 1 && nbEdges > 0 ) {
6043 prevNode = currentNode;
6044 aItE = currentNode->GetInverseElementIterator();
6045 currentElem = aItE->next();
6046 if( currentElem == prevElem)
6047 currentElem = aItE->next();
6048 nIt = currentElem->nodesIterator();
6049 prevElem = currentElem;
6055 if(nbEdges != totalNbEdges)
6056 return EXTR_PATH_NOT_EDGE;
6058 TopTools_SequenceOfShape Edges;
6059 double x1,x2,y1,y2,z1,z2;
6060 list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
6061 int startNid = theN1->GetID();
6062 for(int i = 1; i < aNodesList.size(); i++) {
6063 x1 = aNodesList[i-1]->X();x2 = aNodesList[i]->X();
6064 y1 = aNodesList[i-1]->Y();y2 = aNodesList[i]->Y();
6065 z1 = aNodesList[i-1]->Z();z2 = aNodesList[i]->Z();
6066 TopoDS_Edge e = BRepBuilderAPI_MakeEdge(gp_Pnt(x1,y1,z1),gp_Pnt(x2,y2,z2));
6067 list<SMESH_MeshEditor_PathPoint> LPP;
6069 MakeEdgePathPoints(aPrms, e, (aNodesList[i-1]->GetID()==startNid), LPP);
6070 LLPPs.push_back(LPP);
6071 if( aNodesList[i-1]->GetID() == startNid ) startNid = aNodesList[i]->GetID();
6072 else startNid = aNodesList[i-1]->GetID();
6076 list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
6077 list<SMESH_MeshEditor_PathPoint> firstList = *itLLPP;
6078 list<SMESH_MeshEditor_PathPoint>::iterator itPP = firstList.begin();
6079 for(; itPP!=firstList.end(); itPP++) {
6080 fullList.push_back( *itPP );
6083 SMESH_MeshEditor_PathPoint PP1 = fullList.back();
6084 SMESH_MeshEditor_PathPoint PP2;
6085 fullList.pop_back();
6087 for(; itLLPP!=LLPPs.end(); itLLPP++) {
6088 list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
6089 itPP = currList.begin();
6090 PP2 = currList.front();
6091 gp_Dir D1 = PP1.Tangent();
6092 gp_Dir D2 = PP2.Tangent();
6093 gp_Dir Dnew( gp_Vec( (D1.X()+D2.X())/2, (D1.Y()+D2.Y())/2,
6094 (D1.Z()+D2.Z())/2 ) );
6095 PP1.SetTangent(Dnew);
6096 fullList.push_back(PP1);
6098 for(; itPP!=currList.end(); itPP++) {
6099 fullList.push_back( *itPP );
6101 PP1 = fullList.back();
6102 fullList.pop_back();
6104 fullList.push_back(PP1);
6106 } // Sub-shape for the Pattern must be an Edge or Wire
6107 else if( aS.ShapeType() == TopAbs_EDGE ) {
6108 aTrackEdge = TopoDS::Edge( aS );
6109 // the Edge must not be degenerated
6110 if ( SMESH_Algo::isDegenerated( aTrackEdge ) )
6111 return EXTR_BAD_PATH_SHAPE;
6112 TopExp::Vertices( aTrackEdge, aV1, aV2 );
6113 const SMDS_MeshNode* aN1 = SMESH_Algo::VertexNode( aV1, pMeshDS );
6114 const SMDS_MeshNode* aN2 = SMESH_Algo::VertexNode( aV2, pMeshDS );
6115 // starting node must be aN1 or aN2
6116 if ( !( aN1 == theN1 || aN2 == theN1 ) )
6117 return EXTR_BAD_STARTING_NODE;
6118 aItN = pMeshDS->nodesIterator();
6119 while ( aItN->more() ) {
6120 const SMDS_MeshNode* pNode = aItN->next();
6121 if( pNode==aN1 || pNode==aN2 ) continue;
6122 const SMDS_EdgePosition* pEPos =
6123 static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
6124 double aT = pEPos->GetUParameter();
6125 aPrms.push_back( aT );
6127 //Extrusion_Error err =
6128 MakeEdgePathPoints(aPrms, aTrackEdge, (aN1==theN1), fullList);
6130 else if( aS.ShapeType() == TopAbs_WIRE ) {
6131 list< SMESH_subMesh* > LSM;
6132 TopTools_SequenceOfShape Edges;
6133 TopExp_Explorer eExp(aS, TopAbs_EDGE);
6134 for(; eExp.More(); eExp.Next()) {
6135 TopoDS_Edge E = TopoDS::Edge( eExp.Current() );
6136 if( SMESH_Algo::isDegenerated(E) ) continue;
6137 SMESH_subMesh* SM = theTrack->GetSubMesh(E);
6143 list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
6144 TopoDS_Vertex aVprev;
6145 TColStd_MapOfInteger UsedNums;
6146 int NbEdges = Edges.Length();
6148 for(; i<=NbEdges; i++) {
6150 list< SMESH_subMesh* >::iterator itLSM = LSM.begin();
6151 for(; itLSM!=LSM.end(); itLSM++) {
6153 if(UsedNums.Contains(k)) continue;
6154 aTrackEdge = TopoDS::Edge( Edges.Value(k) );
6155 SMESH_subMesh* locTrack = *itLSM;
6156 SMESHDS_SubMesh* locMeshDS = locTrack->GetSubMeshDS();
6157 TopExp::Vertices( aTrackEdge, aV1, aV2 );
6158 bool aN1isOK = false, aN2isOK = false;
6159 if ( aVprev.IsNull() ) {
6160 // if previous vertex is not yet defined, it means that we in the beginning of wire
6161 // and we have to find initial vertex corresponding to starting node theN1
6162 const SMDS_MeshNode* aN1 = SMESH_Algo::VertexNode( aV1, pMeshDS );
6163 const SMDS_MeshNode* aN2 = SMESH_Algo::VertexNode( aV2, pMeshDS );
6164 // starting node must be aN1 or aN2
6165 aN1isOK = ( aN1 && aN1 == theN1 );
6166 aN2isOK = ( aN2 && aN2 == theN1 );
6169 // we have specified ending vertex of the previous edge on the previous iteration
6170 // and we have just to check that it corresponds to any vertex in current segment
6171 aN1isOK = aVprev.IsSame( aV1 );
6172 aN2isOK = aVprev.IsSame( aV2 );
6174 if ( !aN1isOK && !aN2isOK ) continue;
6175 // 2. Collect parameters on the track edge
6177 aItN = locMeshDS->GetNodes();
6178 while ( aItN->more() ) {
6179 const SMDS_MeshNode* pNode = aItN->next();
6180 const SMDS_EdgePosition* pEPos =
6181 static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
6182 double aT = pEPos->GetUParameter();
6183 aPrms.push_back( aT );
6185 list<SMESH_MeshEditor_PathPoint> LPP;
6186 //Extrusion_Error err =
6187 MakeEdgePathPoints(aPrms, aTrackEdge, aN1isOK, LPP);
6188 LLPPs.push_back(LPP);
6190 // update startN for search following egde
6191 if ( aN1isOK ) aVprev = aV2;
6196 list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
6197 list<SMESH_MeshEditor_PathPoint>& firstList = *itLLPP;
6198 fullList.splice( fullList.end(), firstList );
6200 SMESH_MeshEditor_PathPoint PP1 = fullList.back();
6201 fullList.pop_back();
6203 for(; itLLPP!=LLPPs.end(); itLLPP++) {
6204 list<SMESH_MeshEditor_PathPoint>& currList = *itLLPP;
6205 SMESH_MeshEditor_PathPoint PP2 = currList.front();
6206 gp_Dir D1 = PP1.Tangent();
6207 gp_Dir D2 = PP2.Tangent();
6208 gp_Dir Dnew( ( D1.XYZ() + D2.XYZ() ) / 2 );
6209 PP1.SetTangent(Dnew);
6210 fullList.push_back(PP1);
6211 fullList.splice( fullList.end(), currList, ++currList.begin(), currList.end() );
6212 PP1 = fullList.back();
6213 fullList.pop_back();
6215 // if wire not closed
6216 fullList.push_back(PP1);
6220 return EXTR_BAD_PATH_SHAPE;
6223 return MakeExtrElements(theElements, fullList, theHasAngles, theAngles, theLinearVariation,
6224 theHasRefPoint, theRefPoint, theMakeGroups);
6228 //=======================================================================
6229 //function : MakeEdgePathPoints
6230 //purpose : auxilary for ExtrusionAlongTrack
6231 //=======================================================================
6232 SMESH_MeshEditor::Extrusion_Error
6233 SMESH_MeshEditor::MakeEdgePathPoints(std::list<double>& aPrms,
6234 const TopoDS_Edge& aTrackEdge,
6236 list<SMESH_MeshEditor_PathPoint>& LPP)
6238 Standard_Real aTx1, aTx2, aL2, aTolVec, aTolVec2;
6240 aTolVec2=aTolVec*aTolVec;
6242 TopoDS_Vertex aV1, aV2;
6243 TopExp::Vertices( aTrackEdge, aV1, aV2 );
6244 aT1=BRep_Tool::Parameter( aV1, aTrackEdge );
6245 aT2=BRep_Tool::Parameter( aV2, aTrackEdge );
6246 // 2. Collect parameters on the track edge
6247 aPrms.push_front( aT1 );
6248 aPrms.push_back( aT2 );
6251 if( FirstIsStart ) {
6262 SMESH_MeshEditor_PathPoint aPP;
6263 Handle(Geom_Curve) aC3D = BRep_Tool::Curve( aTrackEdge, aTx1, aTx2 );
6264 std::list<double>::iterator aItD = aPrms.begin();
6265 for(; aItD != aPrms.end(); ++aItD) {
6269 aC3D->D1( aT, aP3D, aVec );
6270 aL2 = aVec.SquareMagnitude();
6271 if ( aL2 < aTolVec2 )
6272 return EXTR_CANT_GET_TANGENT;
6273 gp_Dir aTgt( aVec );
6275 aPP.SetTangent( aTgt );
6276 aPP.SetParameter( aT );
6283 //=======================================================================
6284 //function : MakeExtrElements
6285 //purpose : auxilary for ExtrusionAlongTrack
6286 //=======================================================================
6287 SMESH_MeshEditor::Extrusion_Error
6288 SMESH_MeshEditor::MakeExtrElements(TIDSortedElemSet theElemSets[2],
6289 list<SMESH_MeshEditor_PathPoint>& fullList,
6290 const bool theHasAngles,
6291 list<double>& theAngles,
6292 const bool theLinearVariation,
6293 const bool theHasRefPoint,
6294 const gp_Pnt& theRefPoint,
6295 const bool theMakeGroups)
6297 const int aNbTP = fullList.size();
6299 if( theHasAngles && !theAngles.empty() && theLinearVariation )
6300 LinearAngleVariation(aNbTP-1, theAngles);
6301 // fill vector of path points with angles
6302 vector<SMESH_MeshEditor_PathPoint> aPPs;
6303 list<SMESH_MeshEditor_PathPoint>::iterator itPP = fullList.begin();
6304 list<double>::iterator itAngles = theAngles.begin();
6305 aPPs.push_back( *itPP++ );
6306 for( ; itPP != fullList.end(); itPP++) {
6307 aPPs.push_back( *itPP );
6308 if ( theHasAngles && itAngles != theAngles.end() )
6309 aPPs.back().SetAngle( *itAngles++ );
6312 TNodeOfNodeListMap mapNewNodes;
6313 TElemOfVecOfNnlmiMap mapElemNewNodes;
6314 TTElemOfElemListMap newElemsMap;
6315 TIDSortedElemSet::iterator itElem;
6316 // source elements for each generated one
6317 SMESH_SequenceOfElemPtr srcElems, srcNodes;
6319 // 3. Center of rotation aV0
6320 gp_Pnt aV0 = theRefPoint;
6321 if ( !theHasRefPoint )
6323 gp_XYZ aGC( 0.,0.,0. );
6324 TIDSortedElemSet newNodes;
6326 for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
6328 TIDSortedElemSet& theElements = theElemSets[ is2ndSet ];
6329 itElem = theElements.begin();
6330 for ( ; itElem != theElements.end(); itElem++ ) {
6331 const SMDS_MeshElement* elem = *itElem;
6333 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6334 while ( itN->more() ) {
6335 const SMDS_MeshElement* node = itN->next();
6336 if ( newNodes.insert( node ).second )
6337 aGC += SMESH_TNodeXYZ( node );
6341 aGC /= newNodes.size();
6343 } // if (!theHasRefPoint) {
6345 // 4. Processing the elements
6346 SMESHDS_Mesh* aMesh = GetMeshDS();
6348 setElemsFirst( theElemSets );
6349 for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
6351 TIDSortedElemSet& theElements = theElemSets[ is2ndSet ];
6352 for ( itElem = theElements.begin(); itElem != theElements.end(); itElem++ ) {
6353 // check element type
6354 const SMDS_MeshElement* elem = *itElem;
6357 // SMDSAbs_ElementType aTypeE = elem->GetType();
6358 // if ( aTypeE != SMDSAbs_Face && aTypeE != SMDSAbs_Edge )
6361 vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
6362 newNodesItVec.reserve( elem->NbNodes() );
6364 // loop on elem nodes
6366 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6367 while ( itN->more() )
6370 // check if a node has been already processed
6371 const SMDS_MeshNode* node =
6372 static_cast<const SMDS_MeshNode*>( itN->next() );
6373 TNodeOfNodeListMap::iterator nIt = mapNewNodes.find( node );
6374 if ( nIt == mapNewNodes.end() ) {
6375 nIt = mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
6376 list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
6379 Standard_Real aAngle1x, aAngleT1T0, aTolAng;
6380 gp_Pnt aP0x, aP1x, aPN0, aPN1, aV0x, aV1x;
6381 gp_Ax1 anAx1, anAxT1T0;
6382 gp_Dir aDT1x, aDT0x, aDT1T0;
6387 aPN0 = SMESH_TNodeXYZ( node );
6389 const SMESH_MeshEditor_PathPoint& aPP0 = aPPs[0];
6391 aDT0x= aPP0.Tangent();
6392 //cout<<"j = 0 PP: Pnt("<<aP0x.X()<<","<<aP0x.Y()<<","<<aP0x.Z()<<")"<<endl;
6394 for ( int j = 1; j < aNbTP; ++j ) {
6395 const SMESH_MeshEditor_PathPoint& aPP1 = aPPs[j];
6397 aDT1x = aPP1.Tangent();
6398 aAngle1x = aPP1.Angle();
6400 gp_Trsf aTrsf, aTrsfRot, aTrsfRotT1T0;
6402 gp_Vec aV01x( aP0x, aP1x );
6403 aTrsf.SetTranslation( aV01x );
6406 aV1x = aV0x.Transformed( aTrsf );
6407 aPN1 = aPN0.Transformed( aTrsf );
6409 // rotation 1 [ T1,T0 ]
6410 aAngleT1T0=-aDT1x.Angle( aDT0x );
6411 if (fabs(aAngleT1T0) > aTolAng) {
6413 anAxT1T0.SetLocation( aV1x );
6414 anAxT1T0.SetDirection( aDT1T0 );
6415 aTrsfRotT1T0.SetRotation( anAxT1T0, aAngleT1T0 );
6417 aPN1 = aPN1.Transformed( aTrsfRotT1T0 );
6421 if ( theHasAngles ) {
6422 anAx1.SetLocation( aV1x );
6423 anAx1.SetDirection( aDT1x );
6424 aTrsfRot.SetRotation( anAx1, aAngle1x );
6426 aPN1 = aPN1.Transformed( aTrsfRot );
6430 //MESSAGE("elem->IsQuadratic " << elem->IsQuadratic() << " " << elem->IsMediumNode(node));
6431 if( elem->IsQuadratic() && !elem->IsMediumNode(node) ) {
6432 // create additional node
6433 double x = ( aPN1.X() + aPN0.X() )/2.;
6434 double y = ( aPN1.Y() + aPN0.Y() )/2.;
6435 double z = ( aPN1.Z() + aPN0.Z() )/2.;
6436 const SMDS_MeshNode* newNode = aMesh->AddNode(x,y,z);
6437 myLastCreatedNodes.Append(newNode);
6438 srcNodes.Append( node );
6439 listNewNodes.push_back( newNode );
6441 const SMDS_MeshNode* newNode = aMesh->AddNode( aPN1.X(), aPN1.Y(), aPN1.Z() );
6442 myLastCreatedNodes.Append(newNode);
6443 srcNodes.Append( node );
6444 listNewNodes.push_back( newNode );
6454 // if current elem is quadratic and current node is not medium
6455 // we have to check - may be it is needed to insert additional nodes
6456 if( elem->IsQuadratic() && !elem->IsMediumNode(node) ) {
6457 list< const SMDS_MeshNode* > & listNewNodes = nIt->second;
6458 if(listNewNodes.size()==aNbTP-1) {
6459 vector<const SMDS_MeshNode*> aNodes(2*(aNbTP-1));
6460 gp_XYZ P(node->X(), node->Y(), node->Z());
6461 list< const SMDS_MeshNode* >::iterator it = listNewNodes.begin();
6463 for(i=0; i<aNbTP-1; i++) {
6464 const SMDS_MeshNode* N = *it;
6465 double x = ( N->X() + P.X() )/2.;
6466 double y = ( N->Y() + P.Y() )/2.;
6467 double z = ( N->Z() + P.Z() )/2.;
6468 const SMDS_MeshNode* newN = aMesh->AddNode(x,y,z);
6469 srcNodes.Append( node );
6470 myLastCreatedNodes.Append(newN);
6473 P = gp_XYZ(N->X(),N->Y(),N->Z());
6475 listNewNodes.clear();
6476 for(i=0; i<2*(aNbTP-1); i++) {
6477 listNewNodes.push_back(aNodes[i]);
6483 newNodesItVec.push_back( nIt );
6485 // make new elements
6486 //sweepElement( aMesh, elem, newNodesItVec, newElemsMap[elem],
6487 // newNodesItVec[0]->second.size(), myLastCreatedElems );
6488 sweepElement( elem, newNodesItVec, newElemsMap[elem], aNbTP-1, srcElems );
6492 makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElemSets[0], aNbTP-1, srcElems );
6494 if ( theMakeGroups )
6495 generateGroups( srcNodes, srcElems, "extruded");
6501 //=======================================================================
6502 //function : LinearAngleVariation
6503 //purpose : auxilary for ExtrusionAlongTrack
6504 //=======================================================================
6505 void SMESH_MeshEditor::LinearAngleVariation(const int nbSteps,
6506 list<double>& Angles)
6508 int nbAngles = Angles.size();
6509 if( nbSteps > nbAngles ) {
6510 vector<double> theAngles(nbAngles);
6511 list<double>::iterator it = Angles.begin();
6513 for(; it!=Angles.end(); it++) {
6515 theAngles[i] = (*it);
6518 double rAn2St = double( nbAngles ) / double( nbSteps );
6519 double angPrev = 0, angle;
6520 for ( int iSt = 0; iSt < nbSteps; ++iSt ) {
6521 double angCur = rAn2St * ( iSt+1 );
6522 double angCurFloor = floor( angCur );
6523 double angPrevFloor = floor( angPrev );
6524 if ( angPrevFloor == angCurFloor )
6525 angle = rAn2St * theAngles[ int( angCurFloor ) ];
6527 int iP = int( angPrevFloor );
6528 double angPrevCeil = ceil(angPrev);
6529 angle = ( angPrevCeil - angPrev ) * theAngles[ iP ];
6531 int iC = int( angCurFloor );
6532 if ( iC < nbAngles )
6533 angle += ( angCur - angCurFloor ) * theAngles[ iC ];
6535 iP = int( angPrevCeil );
6537 angle += theAngles[ iC ];
6539 res.push_back(angle);
6544 for(; it!=res.end(); it++)
6545 Angles.push_back( *it );
6550 //================================================================================
6552 * \brief Move or copy theElements applying theTrsf to their nodes
6553 * \param theElems - elements to transform, if theElems is empty then apply to all mesh nodes
6554 * \param theTrsf - transformation to apply
6555 * \param theCopy - if true, create translated copies of theElems
6556 * \param theMakeGroups - if true and theCopy, create translated groups
6557 * \param theTargetMesh - mesh to copy translated elements into
6558 * \return SMESH_MeshEditor::PGroupIDs - list of ids of created groups
6560 //================================================================================
6562 SMESH_MeshEditor::PGroupIDs
6563 SMESH_MeshEditor::Transform (TIDSortedElemSet & theElems,
6564 const gp_Trsf& theTrsf,
6566 const bool theMakeGroups,
6567 SMESH_Mesh* theTargetMesh)
6569 myLastCreatedElems.Clear();
6570 myLastCreatedNodes.Clear();
6572 bool needReverse = false;
6573 string groupPostfix;
6574 switch ( theTrsf.Form() ) {
6576 MESSAGE("gp_PntMirror");
6578 groupPostfix = "mirrored";
6581 MESSAGE("gp_Ax1Mirror");
6582 groupPostfix = "mirrored";
6585 MESSAGE("gp_Ax2Mirror");
6587 groupPostfix = "mirrored";
6590 MESSAGE("gp_Rotation");
6591 groupPostfix = "rotated";
6593 case gp_Translation:
6594 MESSAGE("gp_Translation");
6595 groupPostfix = "translated";
6598 MESSAGE("gp_Scale");
6599 groupPostfix = "scaled";
6601 case gp_CompoundTrsf: // different scale by axis
6602 MESSAGE("gp_CompoundTrsf");
6603 groupPostfix = "scaled";
6607 needReverse = false;
6608 groupPostfix = "transformed";
6611 SMESHDS_Mesh* aTgtMesh = theTargetMesh ? theTargetMesh->GetMeshDS() : 0;
6612 SMESHDS_Mesh* aMesh = GetMeshDS();
6614 SMESH_MeshEditor targetMeshEditor( theTargetMesh );
6615 SMESH_MeshEditor* editor = theTargetMesh ? & targetMeshEditor : theCopy ? this : 0;
6616 SMESH_MeshEditor::ElemFeatures elemType;
6618 // map old node to new one
6619 TNodeNodeMap nodeMap;
6621 // elements sharing moved nodes; those of them which have all
6622 // nodes mirrored but are not in theElems are to be reversed
6623 TIDSortedElemSet inverseElemSet;
6625 // source elements for each generated one
6626 SMESH_SequenceOfElemPtr srcElems, srcNodes;
6628 // issue 021015: EDF 1578 SMESH: Free nodes are removed when translating a mesh
6629 TIDSortedElemSet orphanNode;
6631 if ( theElems.empty() ) // transform the whole mesh
6634 SMDS_ElemIteratorPtr eIt = aMesh->elementsIterator();
6635 while ( eIt->more() ) theElems.insert( eIt->next() );
6637 SMDS_NodeIteratorPtr nIt = aMesh->nodesIterator();
6638 while ( nIt->more() )
6640 const SMDS_MeshNode* node = nIt->next();
6641 if ( node->NbInverseElements() == 0)
6642 orphanNode.insert( node );
6646 // loop on elements to transform nodes : first orphan nodes then elems
6647 TIDSortedElemSet::iterator itElem;
6648 TIDSortedElemSet *elements[] = { &orphanNode, &theElems };
6649 for (int i=0; i<2; i++)
6650 for ( itElem = elements[i]->begin(); itElem != elements[i]->end(); itElem++ )
6652 const SMDS_MeshElement* elem = *itElem;
6656 // loop on elem nodes
6658 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6659 while ( itN->more() )
6661 const SMDS_MeshNode* node = cast2Node( itN->next() );
6662 // check if a node has been already transformed
6663 pair<TNodeNodeMap::iterator,bool> n2n_isnew =
6664 nodeMap.insert( make_pair ( node, node ));
6665 if ( !n2n_isnew.second )
6668 node->GetXYZ( coord );
6669 theTrsf.Transforms( coord[0], coord[1], coord[2] );
6670 if ( theTargetMesh ) {
6671 const SMDS_MeshNode * newNode = aTgtMesh->AddNode( coord[0], coord[1], coord[2] );
6672 n2n_isnew.first->second = newNode;
6673 myLastCreatedNodes.Append(newNode);
6674 srcNodes.Append( node );
6676 else if ( theCopy ) {
6677 const SMDS_MeshNode * newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
6678 n2n_isnew.first->second = newNode;
6679 myLastCreatedNodes.Append(newNode);
6680 srcNodes.Append( node );
6683 aMesh->MoveNode( node, coord[0], coord[1], coord[2] );
6684 // node position on shape becomes invalid
6685 const_cast< SMDS_MeshNode* > ( node )->SetPosition
6686 ( SMDS_SpacePosition::originSpacePosition() );
6689 // keep inverse elements
6690 if ( !theCopy && !theTargetMesh && needReverse ) {
6691 SMDS_ElemIteratorPtr invElemIt = node->GetInverseElementIterator();
6692 while ( invElemIt->more() ) {
6693 const SMDS_MeshElement* iel = invElemIt->next();
6694 inverseElemSet.insert( iel );
6698 } // loop on elems in { &orphanNode, &theElems };
6700 // either create new elements or reverse mirrored ones
6701 if ( !theCopy && !needReverse && !theTargetMesh )
6704 theElems.insert( inverseElemSet.begin(),inverseElemSet.end() );
6706 // Replicate or reverse elements
6708 std::vector<int> iForw;
6709 vector<const SMDS_MeshNode*> nodes;
6710 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
6712 const SMDS_MeshElement* elem = *itElem;
6713 if ( !elem ) continue;
6715 SMDSAbs_GeometryType geomType = elem->GetGeomType();
6716 int nbNodes = elem->NbNodes();
6717 if ( geomType == SMDSGeom_NONE ) continue; // node
6719 nodes.resize( nbNodes );
6721 if ( geomType == SMDSGeom_POLYHEDRA ) // ------------------ polyhedral volume
6723 const SMDS_VtkVolume* aPolyedre = dynamic_cast<const SMDS_VtkVolume*>( elem );
6727 bool allTransformed = true;
6728 int nbFaces = aPolyedre->NbFaces();
6729 for (int iface = 1; iface <= nbFaces && allTransformed; iface++)
6731 int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
6732 for (int inode = 1; inode <= nbFaceNodes && allTransformed; inode++)
6734 const SMDS_MeshNode* node = aPolyedre->GetFaceNode(iface, inode);
6735 TNodeNodeMap::iterator nodeMapIt = nodeMap.find(node);
6736 if ( nodeMapIt == nodeMap.end() )
6737 allTransformed = false; // not all nodes transformed
6739 nodes.push_back((*nodeMapIt).second);
6741 if ( needReverse && allTransformed )
6742 std::reverse( nodes.end() - nbFaceNodes, nodes.end() );
6744 if ( !allTransformed )
6745 continue; // not all nodes transformed
6747 else // ----------------------- the rest element types
6749 while ( iForw.size() < nbNodes ) iForw.push_back( iForw.size() );
6750 const vector<int>& iRev = SMDS_MeshCell::reverseSmdsOrder( elem->GetEntityType(), nbNodes );
6751 const vector<int>& i = needReverse ? iRev : iForw;
6753 // find transformed nodes
6755 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6756 while ( itN->more() ) {
6757 const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( itN->next() );
6758 TNodeNodeMap::iterator nodeMapIt = nodeMap.find( node );
6759 if ( nodeMapIt == nodeMap.end() )
6760 break; // not all nodes transformed
6761 nodes[ i [ iNode++ ]] = (*nodeMapIt).second;
6763 if ( iNode != nbNodes )
6764 continue; // not all nodes transformed
6768 // copy in this or a new mesh
6769 if ( editor->AddElement( nodes, elemType.Init( elem, /*basicOnly=*/false )))
6770 srcElems.Append( elem );
6773 // reverse element as it was reversed by transformation
6775 aMesh->ChangeElementNodes( elem, &nodes[0], nbNodes );
6778 } // loop on elements
6780 if ( editor && editor != this )
6781 myLastCreatedElems = editor->myLastCreatedElems;
6783 PGroupIDs newGroupIDs;
6785 if ( ( theMakeGroups && theCopy ) ||
6786 ( theMakeGroups && theTargetMesh ) )
6787 newGroupIDs = generateGroups( srcNodes, srcElems, groupPostfix, theTargetMesh, false );
6792 //=======================================================================
6794 * \brief Create groups of elements made during transformation
6795 * \param nodeGens - nodes making corresponding myLastCreatedNodes
6796 * \param elemGens - elements making corresponding myLastCreatedElems
6797 * \param postfix - to append to names of new groups
6798 * \param targetMesh - mesh to create groups in
6799 * \param topPresent - is there "top" elements that are created by sweeping
6801 //=======================================================================
6803 SMESH_MeshEditor::PGroupIDs
6804 SMESH_MeshEditor::generateGroups(const SMESH_SequenceOfElemPtr& nodeGens,
6805 const SMESH_SequenceOfElemPtr& elemGens,
6806 const std::string& postfix,
6807 SMESH_Mesh* targetMesh,
6808 const bool topPresent)
6810 PGroupIDs newGroupIDs( new list<int> );
6811 SMESH_Mesh* mesh = targetMesh ? targetMesh : GetMesh();
6813 // Sort existing groups by types and collect their names
6815 // containers to store an old group and generated new ones;
6816 // 1st new group is for result elems of different type than a source one;
6817 // 2nd new group is for same type result elems ("top" group at extrusion)
6819 using boost::make_tuple;
6820 typedef tuple< SMESHDS_GroupBase*, SMESHDS_Group*, SMESHDS_Group* > TOldNewGroup;
6821 vector< list< TOldNewGroup > > groupsByType( SMDSAbs_NbElementTypes );
6822 vector< TOldNewGroup* > orderedOldNewGroups; // in order of old groups
6824 set< string > groupNames;
6826 SMESH_Mesh::GroupIteratorPtr groupIt = GetMesh()->GetGroups();
6827 if ( !groupIt->more() ) return newGroupIDs;
6829 int newGroupID = mesh->GetGroupIds().back()+1;
6830 while ( groupIt->more() )
6832 SMESH_Group * group = groupIt->next();
6833 if ( !group ) continue;
6834 SMESHDS_GroupBase* groupDS = group->GetGroupDS();
6835 if ( !groupDS || groupDS->IsEmpty() ) continue;
6836 groupNames.insert ( group->GetName() );
6837 groupDS->SetStoreName( group->GetName() );
6838 const SMDSAbs_ElementType type = groupDS->GetType();
6839 SMESHDS_Group* newGroup = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
6840 SMESHDS_Group* newTopGroup = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
6841 groupsByType[ type ].push_back( make_tuple( groupDS, newGroup, newTopGroup ));
6842 orderedOldNewGroups.push_back( & groupsByType[ type ].back() );
6845 // Loop on nodes and elements to add them in new groups
6847 vector< const SMDS_MeshElement* > resultElems;
6848 for ( int isNodes = 0; isNodes < 2; ++isNodes )
6850 const SMESH_SequenceOfElemPtr& gens = isNodes ? nodeGens : elemGens;
6851 const SMESH_SequenceOfElemPtr& elems = isNodes ? myLastCreatedNodes : myLastCreatedElems;
6852 if ( gens.Length() != elems.Length() )
6853 throw SALOME_Exception("SMESH_MeshEditor::generateGroups(): invalid args");
6855 // loop on created elements
6856 for (int iElem = 1; iElem <= elems.Length(); ++iElem )
6858 const SMDS_MeshElement* sourceElem = gens( iElem );
6859 if ( !sourceElem ) {
6860 MESSAGE("generateGroups(): NULL source element");
6863 list< TOldNewGroup > & groupsOldNew = groupsByType[ sourceElem->GetType() ];
6864 if ( groupsOldNew.empty() ) { // no groups of this type at all
6865 while ( iElem < gens.Length() && gens( iElem+1 ) == sourceElem )
6866 ++iElem; // skip all elements made by sourceElem
6869 // collect all elements made by the iElem-th sourceElem
6870 resultElems.clear();
6871 if ( const SMDS_MeshElement* resElem = elems( iElem ))
6872 if ( resElem != sourceElem )
6873 resultElems.push_back( resElem );
6874 while ( iElem < gens.Length() && gens( iElem+1 ) == sourceElem )
6875 if ( const SMDS_MeshElement* resElem = elems( ++iElem ))
6876 if ( resElem != sourceElem )
6877 resultElems.push_back( resElem );
6879 const SMDS_MeshElement* topElem = 0;
6880 if ( isNodes ) // there must be a top element
6882 topElem = resultElems.back();
6883 resultElems.pop_back();
6887 vector< const SMDS_MeshElement* >::reverse_iterator resElemIt = resultElems.rbegin();
6888 for ( ; resElemIt != resultElems.rend() ; ++resElemIt )
6889 if ( (*resElemIt)->GetType() == sourceElem->GetType() )
6891 topElem = *resElemIt;
6892 *resElemIt = 0; // erase *resElemIt
6896 // add resultElems to groups originted from ones the sourceElem belongs to
6897 list< TOldNewGroup >::iterator gOldNew, gLast = groupsOldNew.end();
6898 for ( gOldNew = groupsOldNew.begin(); gOldNew != gLast; ++gOldNew )
6900 SMESHDS_GroupBase* oldGroup = gOldNew->get<0>();
6901 if ( oldGroup->Contains( sourceElem )) // sourceElem is in oldGroup
6903 // fill in a new group
6904 SMDS_MeshGroup & newGroup = gOldNew->get<1>()->SMDSGroup();
6905 vector< const SMDS_MeshElement* >::iterator resLast = resultElems.end(), resElemIt;
6906 for ( resElemIt = resultElems.begin(); resElemIt != resLast; ++resElemIt )
6908 newGroup.Add( *resElemIt );
6910 // fill a "top" group
6913 SMDS_MeshGroup & newTopGroup = gOldNew->get<2>()->SMDSGroup();
6914 newTopGroup.Add( topElem );
6918 } // loop on created elements
6919 }// loop on nodes and elements
6921 // Create new SMESH_Groups from SMESHDS_Groups and remove empty SMESHDS_Groups
6923 list<int> topGrouIds;
6924 for ( size_t i = 0; i < orderedOldNewGroups.size(); ++i )
6926 SMESHDS_GroupBase* oldGroupDS = orderedOldNewGroups[i]->get<0>();
6927 SMESHDS_Group* newGroups[2] = { orderedOldNewGroups[i]->get<1>(),
6928 orderedOldNewGroups[i]->get<2>() };
6929 for ( int is2nd = 0; is2nd < 2; ++is2nd )
6931 SMESHDS_Group* newGroupDS = newGroups[ is2nd ];
6932 if ( newGroupDS->IsEmpty() )
6934 mesh->GetMeshDS()->RemoveGroup( newGroupDS );
6939 newGroupDS->SetType( newGroupDS->GetElements()->next()->GetType() );
6942 const bool isTop = ( topPresent &&
6943 newGroupDS->GetType() == oldGroupDS->GetType() &&
6946 string name = oldGroupDS->GetStoreName();
6947 { // remove trailing whitespaces (issue 22599)
6948 size_t size = name.size();
6949 while ( size > 1 && isspace( name[ size-1 ]))
6951 if ( size != name.size() )
6953 name.resize( size );
6954 oldGroupDS->SetStoreName( name.c_str() );
6957 if ( !targetMesh ) {
6958 string suffix = ( isTop ? "top": postfix.c_str() );
6962 while ( !groupNames.insert( name ).second ) // name exists
6963 name = SMESH_Comment( oldGroupDS->GetStoreName() ) << "_" << suffix << "_" << nb++;
6968 newGroupDS->SetStoreName( name.c_str() );
6970 // make a SMESH_Groups
6971 mesh->AddGroup( newGroupDS );
6973 topGrouIds.push_back( newGroupDS->GetID() );
6975 newGroupIDs->push_back( newGroupDS->GetID() );
6979 newGroupIDs->splice( newGroupIDs->end(), topGrouIds );
6984 //================================================================================
6986 * * \brief Return list of group of nodes close to each other within theTolerance
6987 * * Search among theNodes or in the whole mesh if theNodes is empty using
6988 * * an Octree algorithm
6989 * \param [in,out] theNodes - the nodes to treat
6990 * \param [in] theTolerance - the tolerance
6991 * \param [out] theGroupsOfNodes - the result groups of coincident nodes
6992 * \param [in] theSeparateCornersAndMedium - if \c true, in quadratic mesh puts
6993 * corner and medium nodes in separate groups
6995 //================================================================================
6997 void SMESH_MeshEditor::FindCoincidentNodes (TIDSortedNodeSet & theNodes,
6998 const double theTolerance,
6999 TListOfListOfNodes & theGroupsOfNodes,
7000 bool theSeparateCornersAndMedium)
7002 myLastCreatedElems.Clear();
7003 myLastCreatedNodes.Clear();
7005 if ( myMesh->NbEdges ( ORDER_QUADRATIC ) +
7006 myMesh->NbFaces ( ORDER_QUADRATIC ) +
7007 myMesh->NbVolumes( ORDER_QUADRATIC ) == 0 )
7008 theSeparateCornersAndMedium = false;
7010 TIDSortedNodeSet& corners = theNodes;
7011 TIDSortedNodeSet medium;
7013 if ( theNodes.empty() ) // get all nodes in the mesh
7015 TIDSortedNodeSet* nodes[2] = { &corners, &medium };
7016 SMDS_NodeIteratorPtr nIt = GetMeshDS()->nodesIterator(/*idInceasingOrder=*/true);
7017 if ( theSeparateCornersAndMedium )
7018 while ( nIt->more() )
7020 const SMDS_MeshNode* n = nIt->next();
7021 TIDSortedNodeSet* & nodeSet = nodes[ SMESH_MesherHelper::IsMedium( n )];
7022 nodeSet->insert( nodeSet->end(), n );
7025 while ( nIt->more() )
7026 theNodes.insert( theNodes.end(),nIt->next() );
7028 else if ( theSeparateCornersAndMedium ) // separate corners from medium nodes
7030 TIDSortedNodeSet::iterator nIt = corners.begin();
7031 while ( nIt != corners.end() )
7032 if ( SMESH_MesherHelper::IsMedium( *nIt ))
7034 medium.insert( medium.end(), *nIt );
7035 corners.erase( nIt++ );
7043 if ( !corners.empty() )
7044 SMESH_OctreeNode::FindCoincidentNodes ( corners, &theGroupsOfNodes, theTolerance );
7045 if ( !medium.empty() )
7046 SMESH_OctreeNode::FindCoincidentNodes ( medium, &theGroupsOfNodes, theTolerance );
7049 //=======================================================================
7050 //function : SimplifyFace
7051 //purpose : split a chain of nodes into several closed chains
7052 //=======================================================================
7054 int SMESH_MeshEditor::SimplifyFace (const vector<const SMDS_MeshNode *>& faceNodes,
7055 vector<const SMDS_MeshNode *>& poly_nodes,
7056 vector<int>& quantities) const
7058 int nbNodes = faceNodes.size();
7063 set<const SMDS_MeshNode*> nodeSet;
7065 // get simple seq of nodes
7066 //const SMDS_MeshNode* simpleNodes[ nbNodes ];
7067 vector<const SMDS_MeshNode*> simpleNodes( nbNodes );
7068 int iSimple = 0, nbUnique = 0;
7070 simpleNodes[iSimple++] = faceNodes[0];
7072 for (int iCur = 1; iCur < nbNodes; iCur++) {
7073 if (faceNodes[iCur] != simpleNodes[iSimple - 1]) {
7074 simpleNodes[iSimple++] = faceNodes[iCur];
7075 if (nodeSet.insert( faceNodes[iCur] ).second)
7079 int nbSimple = iSimple;
7080 if (simpleNodes[nbSimple - 1] == simpleNodes[0]) {
7090 bool foundLoop = (nbSimple > nbUnique);
7093 set<const SMDS_MeshNode*> loopSet;
7094 for (iSimple = 0; iSimple < nbSimple && !foundLoop; iSimple++) {
7095 const SMDS_MeshNode* n = simpleNodes[iSimple];
7096 if (!loopSet.insert( n ).second) {
7100 int iC = 0, curLast = iSimple;
7101 for (; iC < curLast; iC++) {
7102 if (simpleNodes[iC] == n) break;
7104 int loopLen = curLast - iC;
7106 // create sub-element
7108 quantities.push_back(loopLen);
7109 for (; iC < curLast; iC++) {
7110 poly_nodes.push_back(simpleNodes[iC]);
7113 // shift the rest nodes (place from the first loop position)
7114 for (iC = curLast + 1; iC < nbSimple; iC++) {
7115 simpleNodes[iC - loopLen] = simpleNodes[iC];
7117 nbSimple -= loopLen;
7120 } // for (iSimple = 0; iSimple < nbSimple; iSimple++)
7121 } // while (foundLoop)
7125 quantities.push_back(iSimple);
7126 for (int i = 0; i < iSimple; i++)
7127 poly_nodes.push_back(simpleNodes[i]);
7133 //=======================================================================
7134 //function : MergeNodes
7135 //purpose : In each group, the cdr of nodes are substituted by the first one
7137 //=======================================================================
7139 void SMESH_MeshEditor::MergeNodes (TListOfListOfNodes & theGroupsOfNodes)
7141 MESSAGE("MergeNodes");
7142 myLastCreatedElems.Clear();
7143 myLastCreatedNodes.Clear();
7145 SMESHDS_Mesh* aMesh = GetMeshDS();
7147 TNodeNodeMap nodeNodeMap; // node to replace - new node
7148 set<const SMDS_MeshElement*> elems; // all elements with changed nodes
7149 list< int > rmElemIds, rmNodeIds;
7151 // Fill nodeNodeMap and elems
7153 TListOfListOfNodes::iterator grIt = theGroupsOfNodes.begin();
7154 for ( ; grIt != theGroupsOfNodes.end(); grIt++ ) {
7155 list<const SMDS_MeshNode*>& nodes = *grIt;
7156 list<const SMDS_MeshNode*>::iterator nIt = nodes.begin();
7157 const SMDS_MeshNode* nToKeep = *nIt;
7158 //MESSAGE("node to keep " << nToKeep->GetID());
7159 for ( ++nIt; nIt != nodes.end(); nIt++ ) {
7160 const SMDS_MeshNode* nToRemove = *nIt;
7161 nodeNodeMap.insert( TNodeNodeMap::value_type( nToRemove, nToKeep ));
7162 if ( nToRemove != nToKeep ) {
7163 //MESSAGE(" node to remove " << nToRemove->GetID());
7164 rmNodeIds.push_back( nToRemove->GetID() );
7165 AddToSameGroups( nToKeep, nToRemove, aMesh );
7166 // set _alwaysComputed to a sub-mesh of VERTEX to enable mesh computing
7167 // after MergeNodes() w/o creating node in place of merged ones.
7168 const SMDS_PositionPtr& pos = nToRemove->GetPosition();
7169 if ( pos && pos->GetTypeOfPosition() == SMDS_TOP_VERTEX )
7170 if ( SMESH_subMesh* sm = myMesh->GetSubMeshContaining( nToRemove->getshapeId() ))
7171 sm->SetIsAlwaysComputed( true );
7174 SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
7175 while ( invElemIt->more() ) {
7176 const SMDS_MeshElement* elem = invElemIt->next();
7181 // Change element nodes or remove an element
7183 set<const SMDS_MeshNode*> nodeSet;
7184 vector< const SMDS_MeshNode*> curNodes, uniqueNodes;
7186 ElemFeatures elemType;
7188 set<const SMDS_MeshElement*>::iterator eIt = elems.begin();
7189 for ( ; eIt != elems.end(); eIt++ )
7191 const SMDS_MeshElement* elem = *eIt;
7192 int nbNodes = elem->NbNodes();
7193 int aShapeId = FindShape( elem );
7196 curNodes.resize( nbNodes );
7197 uniqueNodes.resize( nbNodes );
7198 iRepl.resize( nbNodes );
7199 int iUnique = 0, iCur = 0, nbRepl = 0;
7201 // get new seq of nodes
7202 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
7203 while ( itN->more() )
7205 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( itN->next() );
7207 TNodeNodeMap::iterator nnIt = nodeNodeMap.find( n );
7208 if ( nnIt != nodeNodeMap.end() ) { // n sticks
7210 { ////////// BUG 0020185: begin
7211 bool stopRecur = false;
7212 set<const SMDS_MeshNode*> nodesRecur;
7213 nodesRecur.insert(n);
7214 while (!stopRecur) {
7215 TNodeNodeMap::iterator nnIt_i = nodeNodeMap.find( n );
7216 if ( nnIt_i != nodeNodeMap.end() ) { // n sticks
7217 n = (*nnIt_i).second;
7218 if (!nodesRecur.insert(n).second) {
7219 // error: recursive dependancy
7226 } ////////// BUG 0020185: end
7228 curNodes[ iCur ] = n;
7229 bool isUnique = nodeSet.insert( n ).second;
7231 uniqueNodes[ iUnique++ ] = n;
7233 iRepl[ nbRepl++ ] = iCur;
7237 // Analyse element topology after replacement
7240 int nbUniqueNodes = nodeSet.size();
7241 if ( nbNodes != nbUniqueNodes ) // some nodes stick
7243 if (elem->IsPoly()) // Polygons and Polyhedral volumes
7245 if (elem->GetType() == SMDSAbs_Face) // Polygon
7247 elemType.Init( elem );
7248 const bool isQuad = elemType.myIsQuad;
7250 SMDS_MeshCell::applyInterlace // interlace medium and corner nodes
7251 ( SMDS_MeshCell::interlacedSmdsOrder( SMDSEntity_Quad_Polygon, nbNodes ), curNodes );
7253 // a polygon can divide into several elements
7254 vector<const SMDS_MeshNode *> polygons_nodes;
7255 vector<int> quantities;
7256 int nbNew = SimplifyFace( curNodes, polygons_nodes, quantities );
7259 vector<const SMDS_MeshNode *> face_nodes;
7261 for (int iface = 0; iface < nbNew; iface++)
7263 int nbNewNodes = quantities[iface];
7264 face_nodes.assign( polygons_nodes.begin() + inode,
7265 polygons_nodes.begin() + inode + nbNewNodes );
7266 inode += nbNewNodes;
7267 if ( isQuad ) // check if a result elem is a valid quadratic polygon
7269 bool isValid = ( nbNewNodes % 2 == 0 );
7270 for ( int i = 0; i < nbNewNodes && isValid; ++i )
7271 isValid = ( elem->IsMediumNode( face_nodes[i]) == bool( i % 2 ));
7272 elemType.SetQuad( isValid );
7273 if ( isValid ) // put medium nodes after corners
7274 SMDS_MeshCell::applyInterlaceRev
7275 ( SMDS_MeshCell::interlacedSmdsOrder( SMDSEntity_Quad_Polygon,
7276 nbNewNodes ), face_nodes );
7278 SMDS_MeshElement* newElem = AddElement( face_nodes, elemType );
7280 aMesh->SetMeshElementOnShape(newElem, aShapeId);
7283 rmElemIds.push_back(elem->GetID());
7287 else if (elem->GetType() == SMDSAbs_Volume) // Polyhedral volume
7289 if (nbUniqueNodes < 4) {
7290 rmElemIds.push_back(elem->GetID());
7293 // each face has to be analyzed in order to check volume validity
7294 const SMDS_VtkVolume* aPolyedre =
7295 dynamic_cast<const SMDS_VtkVolume*>( elem );
7297 int nbFaces = aPolyedre->NbFaces();
7299 vector<const SMDS_MeshNode *> poly_nodes;
7300 vector<int> quantities;
7302 for (int iface = 1; iface <= nbFaces; iface++) {
7303 int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
7304 vector<const SMDS_MeshNode *> faceNodes (nbFaceNodes);
7306 for (int inode = 1; inode <= nbFaceNodes; inode++) {
7307 const SMDS_MeshNode * faceNode = aPolyedre->GetFaceNode(iface, inode);
7308 TNodeNodeMap::iterator nnIt = nodeNodeMap.find(faceNode);
7309 if (nnIt != nodeNodeMap.end()) { // faceNode sticks
7310 faceNode = (*nnIt).second;
7312 faceNodes[inode - 1] = faceNode;
7315 SimplifyFace(faceNodes, poly_nodes, quantities);
7318 if (quantities.size() > 3) {
7319 // to be done: remove coincident faces
7322 if (quantities.size() > 3)
7324 //aMesh->ChangePolyhedronNodes(elem, poly_nodes, quantities);
7325 const SMDS_MeshElement* newElem =
7326 aMesh->AddPolyhedralVolume(poly_nodes, quantities);
7327 myLastCreatedElems.Append(newElem);
7328 if ( aShapeId && newElem )
7329 aMesh->SetMeshElementOnShape( newElem, aShapeId );
7330 rmElemIds.push_back(elem->GetID());
7334 rmElemIds.push_back(elem->GetID());
7345 // TODO not all the possible cases are solved. Find something more generic?
7346 switch ( nbNodes ) {
7347 case 2: ///////////////////////////////////// EDGE
7348 isOk = false; break;
7349 case 3: ///////////////////////////////////// TRIANGLE
7350 isOk = false; break;
7352 if ( elem->GetType() == SMDSAbs_Volume ) // TETRAHEDRON
7354 else { //////////////////////////////////// QUADRANGLE
7355 if ( nbUniqueNodes < 3 )
7357 else if ( nbRepl == 2 && iRepl[ 1 ] - iRepl[ 0 ] == 2 )
7358 isOk = false; // opposite nodes stick
7359 //MESSAGE("isOk " << isOk);
7362 case 6: ///////////////////////////////////// PENTAHEDRON
7363 if ( nbUniqueNodes == 4 ) {
7364 // ---------------------------------> tetrahedron
7366 iRepl[ 0 ] > 2 && iRepl[ 1 ] > 2 && iRepl[ 2 ] > 2 ) {
7367 // all top nodes stick: reverse a bottom
7368 uniqueNodes[ 0 ] = curNodes [ 1 ];
7369 uniqueNodes[ 1 ] = curNodes [ 0 ];
7371 else if (nbRepl == 3 &&
7372 iRepl[ 0 ] < 3 && iRepl[ 1 ] < 3 && iRepl[ 2 ] < 3 ) {
7373 // all bottom nodes stick: set a top before
7374 uniqueNodes[ 3 ] = uniqueNodes [ 0 ];
7375 uniqueNodes[ 0 ] = curNodes [ 3 ];
7376 uniqueNodes[ 1 ] = curNodes [ 4 ];
7377 uniqueNodes[ 2 ] = curNodes [ 5 ];
7379 else if (nbRepl == 4 &&
7380 iRepl[ 2 ] - iRepl [ 0 ] == 3 && iRepl[ 3 ] - iRepl [ 1 ] == 3 ) {
7381 // a lateral face turns into a line: reverse a bottom
7382 uniqueNodes[ 0 ] = curNodes [ 1 ];
7383 uniqueNodes[ 1 ] = curNodes [ 0 ];
7388 else if ( nbUniqueNodes == 5 ) {
7389 // PENTAHEDRON --------------------> 2 tetrahedrons
7390 if ( nbRepl == 2 && iRepl[ 1 ] - iRepl [ 0 ] == 3 ) {
7391 // a bottom node sticks with a linked top one
7393 SMDS_MeshElement* newElem =
7394 aMesh->AddVolume(curNodes[ 3 ],
7397 curNodes[ iRepl[ 0 ] == 2 ? 1 : 2 ]);
7398 myLastCreatedElems.Append(newElem);
7400 aMesh->SetMeshElementOnShape( newElem, aShapeId );
7401 // 2. : reverse a bottom
7402 uniqueNodes[ 0 ] = curNodes [ 1 ];
7403 uniqueNodes[ 1 ] = curNodes [ 0 ];
7413 if(elem->IsQuadratic()) { // Quadratic quadrangle
7425 MESSAGE("nbRepl=2: " << iRepl[0] << " " << iRepl[1]);
7428 MESSAGE("nbRepl=3: " << iRepl[0] << " " << iRepl[1] << " " << iRepl[2]);
7430 if( iRepl[0]==0 && iRepl[1]==1 && iRepl[2]==4 ) {
7431 uniqueNodes[0] = curNodes[0];
7432 uniqueNodes[1] = curNodes[2];
7433 uniqueNodes[2] = curNodes[3];
7434 uniqueNodes[3] = curNodes[5];
7435 uniqueNodes[4] = curNodes[6];
7436 uniqueNodes[5] = curNodes[7];
7439 if( iRepl[0]==0 && iRepl[1]==3 && iRepl[2]==7 ) {
7440 uniqueNodes[0] = curNodes[0];
7441 uniqueNodes[1] = curNodes[1];
7442 uniqueNodes[2] = curNodes[2];
7443 uniqueNodes[3] = curNodes[4];
7444 uniqueNodes[4] = curNodes[5];
7445 uniqueNodes[5] = curNodes[6];
7448 if( iRepl[0]==0 && iRepl[1]==4 && iRepl[2]==7 ) {
7449 uniqueNodes[0] = curNodes[1];
7450 uniqueNodes[1] = curNodes[2];
7451 uniqueNodes[2] = curNodes[3];
7452 uniqueNodes[3] = curNodes[5];
7453 uniqueNodes[4] = curNodes[6];
7454 uniqueNodes[5] = curNodes[0];
7457 if( iRepl[0]==1 && iRepl[1]==2 && iRepl[2]==5 ) {
7458 uniqueNodes[0] = curNodes[0];
7459 uniqueNodes[1] = curNodes[1];
7460 uniqueNodes[2] = curNodes[3];
7461 uniqueNodes[3] = curNodes[4];
7462 uniqueNodes[4] = curNodes[6];
7463 uniqueNodes[5] = curNodes[7];
7466 if( iRepl[0]==1 && iRepl[1]==4 && iRepl[2]==5 ) {
7467 uniqueNodes[0] = curNodes[0];
7468 uniqueNodes[1] = curNodes[2];
7469 uniqueNodes[2] = curNodes[3];
7470 uniqueNodes[3] = curNodes[1];
7471 uniqueNodes[4] = curNodes[6];
7472 uniqueNodes[5] = curNodes[7];
7475 if( iRepl[0]==2 && iRepl[1]==3 && iRepl[2]==6 ) {
7476 uniqueNodes[0] = curNodes[0];
7477 uniqueNodes[1] = curNodes[1];
7478 uniqueNodes[2] = curNodes[2];
7479 uniqueNodes[3] = curNodes[4];
7480 uniqueNodes[4] = curNodes[5];
7481 uniqueNodes[5] = curNodes[7];
7484 if( iRepl[0]==2 && iRepl[1]==5 && iRepl[2]==6 ) {
7485 uniqueNodes[0] = curNodes[0];
7486 uniqueNodes[1] = curNodes[1];
7487 uniqueNodes[2] = curNodes[3];
7488 uniqueNodes[3] = curNodes[4];
7489 uniqueNodes[4] = curNodes[2];
7490 uniqueNodes[5] = curNodes[7];
7493 if( iRepl[0]==3 && iRepl[1]==6 && iRepl[2]==7 ) {
7494 uniqueNodes[0] = curNodes[0];
7495 uniqueNodes[1] = curNodes[1];
7496 uniqueNodes[2] = curNodes[2];
7497 uniqueNodes[3] = curNodes[4];
7498 uniqueNodes[4] = curNodes[5];
7499 uniqueNodes[5] = curNodes[3];
7504 MESSAGE("nbRepl=4: " << iRepl[0] << " " << iRepl[1] << " " << iRepl[2] << " " << iRepl[3]);
7507 MESSAGE("nbRepl=5: " << iRepl[0] << " " << iRepl[1] << " " << iRepl[2] << " " << iRepl[3] << " " << iRepl[4]);
7511 //////////////////////////////////// HEXAHEDRON
7513 SMDS_VolumeTool hexa (elem);
7514 hexa.SetExternalNormal();
7515 if ( nbUniqueNodes == 4 && nbRepl == 4 ) {
7516 //////////////////////// HEX ---> 1 tetrahedron
7517 for ( int iFace = 0; iFace < 6; iFace++ ) {
7518 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7519 if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7520 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7521 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7522 // one face turns into a point ...
7523 int iOppFace = hexa.GetOppFaceIndex( iFace );
7524 ind = hexa.GetFaceNodesIndices( iOppFace );
7526 for ( iCur = 0; iCur < 4 && nbStick < 2; iCur++ ) {
7527 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7530 if ( nbStick == 1 ) {
7531 // ... and the opposite one - into a triangle.
7533 ind = hexa.GetFaceNodesIndices( iFace );
7534 uniqueNodes[ 3 ] = curNodes[ind[ 0 ]];
7541 else if ( nbUniqueNodes == 6 && nbRepl == 2 ) {
7542 //////////////////////// HEX ---> 1 prism
7543 int nbTria = 0, iTria[3];
7544 const int *ind; // indices of face nodes
7545 // look for triangular faces
7546 for ( int iFace = 0; iFace < 6 && nbTria < 3; iFace++ ) {
7547 ind = hexa.GetFaceNodesIndices( iFace );
7548 TIDSortedNodeSet faceNodes;
7549 for ( iCur = 0; iCur < 4; iCur++ )
7550 faceNodes.insert( curNodes[ind[iCur]] );
7551 if ( faceNodes.size() == 3 )
7552 iTria[ nbTria++ ] = iFace;
7554 // check if triangles are opposite
7555 if ( nbTria == 2 && iTria[0] == hexa.GetOppFaceIndex( iTria[1] ))
7558 // set nodes of the bottom triangle
7559 ind = hexa.GetFaceNodesIndices( iTria[ 0 ]);
7561 for ( iCur = 0; iCur < 4; iCur++ )
7562 if ( ind[iCur] != iRepl[0] && ind[iCur] != iRepl[1])
7563 indB.push_back( ind[iCur] );
7564 if ( !hexa.IsForward() )
7565 std::swap( indB[0], indB[2] );
7566 for ( iCur = 0; iCur < 3; iCur++ )
7567 uniqueNodes[ iCur ] = curNodes[indB[iCur]];
7568 // set nodes of the top triangle
7569 const int *indT = hexa.GetFaceNodesIndices( iTria[ 1 ]);
7570 for ( iCur = 0; iCur < 3; ++iCur )
7571 for ( int j = 0; j < 4; ++j )
7572 if ( hexa.IsLinked( indB[ iCur ], indT[ j ] ))
7574 uniqueNodes[ iCur + 3 ] = curNodes[ indT[ j ]];
7580 else if (nbUniqueNodes == 5 && nbRepl == 4 ) {
7581 //////////////////// HEXAHEDRON ---> 2 tetrahedrons
7582 for ( int iFace = 0; iFace < 6; iFace++ ) {
7583 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7584 if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7585 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7586 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7587 // one face turns into a point ...
7588 int iOppFace = hexa.GetOppFaceIndex( iFace );
7589 ind = hexa.GetFaceNodesIndices( iOppFace );
7591 iUnique = 2; // reverse a tetrahedron 1 bottom
7592 for ( iCur = 0; iCur < 4 && nbStick == 0; iCur++ ) {
7593 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7595 else if ( iUnique >= 0 )
7596 uniqueNodes[ iUnique-- ] = curNodes[ind[ iCur ]];
7598 if ( nbStick == 0 ) {
7599 // ... and the opposite one is a quadrangle
7601 const int* indTop = hexa.GetFaceNodesIndices( iFace );
7602 uniqueNodes[ 3 ] = curNodes[indTop[ 0 ]];
7605 SMDS_MeshElement* newElem =
7606 aMesh->AddVolume(curNodes[ind[ 0 ]],
7609 curNodes[indTop[ 0 ]]);
7610 myLastCreatedElems.Append(newElem);
7612 aMesh->SetMeshElementOnShape( newElem, aShapeId );
7619 else if ( nbUniqueNodes == 6 && nbRepl == 4 ) {
7620 ////////////////// HEXAHEDRON ---> 2 tetrahedrons or 1 prism
7621 // find indices of quad and tri faces
7622 int iQuadFace[ 6 ], iTriFace[ 6 ], nbQuad = 0, nbTri = 0, iFace;
7623 for ( iFace = 0; iFace < 6; iFace++ ) {
7624 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7626 for ( iCur = 0; iCur < 4; iCur++ )
7627 nodeSet.insert( curNodes[ind[ iCur ]] );
7628 nbUniqueNodes = nodeSet.size();
7629 if ( nbUniqueNodes == 3 )
7630 iTriFace[ nbTri++ ] = iFace;
7631 else if ( nbUniqueNodes == 4 )
7632 iQuadFace[ nbQuad++ ] = iFace;
7634 if (nbQuad == 2 && nbTri == 4 &&
7635 hexa.GetOppFaceIndex( iQuadFace[ 0 ] ) == iQuadFace[ 1 ]) {
7636 // 2 opposite quadrangles stuck with a diagonal;
7637 // sample groups of merged indices: (0-4)(2-6)
7638 // --------------------------------------------> 2 tetrahedrons
7639 const int *ind1 = hexa.GetFaceNodesIndices( iQuadFace[ 0 ]); // indices of quad1 nodes
7640 const int *ind2 = hexa.GetFaceNodesIndices( iQuadFace[ 1 ]);
7641 int i0, i1d, i2, i3d, i0t, i2t; // d-daigonal, t-top
7642 if (curNodes[ind1[ 0 ]] == curNodes[ind2[ 0 ]] &&
7643 curNodes[ind1[ 2 ]] == curNodes[ind2[ 2 ]]) {
7644 // stuck with 0-2 diagonal
7652 else if (curNodes[ind1[ 1 ]] == curNodes[ind2[ 3 ]] &&
7653 curNodes[ind1[ 3 ]] == curNodes[ind2[ 1 ]]) {
7654 // stuck with 1-3 diagonal
7666 uniqueNodes[ 0 ] = curNodes [ i0 ];
7667 uniqueNodes[ 1 ] = curNodes [ i1d ];
7668 uniqueNodes[ 2 ] = curNodes [ i3d ];
7669 uniqueNodes[ 3 ] = curNodes [ i0t ];
7672 SMDS_MeshElement* newElem = aMesh->AddVolume(curNodes[ i1d ],
7676 myLastCreatedElems.Append(newElem);
7678 aMesh->SetMeshElementOnShape( newElem, aShapeId );
7681 else if (( nbTri == 2 && nbQuad == 3 ) || // merged (0-4)(1-5)
7682 ( nbTri == 4 && nbQuad == 2 )) { // merged (7-4)(1-5)
7683 // --------------------------------------------> prism
7684 // find 2 opposite triangles
7686 for ( iFace = 0; iFace + 1 < nbTri; iFace++ ) {
7687 if ( hexa.GetOppFaceIndex( iTriFace[ iFace ] ) == iTriFace[ iFace + 1 ]) {
7688 // find indices of kept and replaced nodes
7689 // and fill unique nodes of 2 opposite triangles
7690 const int *ind1 = hexa.GetFaceNodesIndices( iTriFace[ iFace ]);
7691 const int *ind2 = hexa.GetFaceNodesIndices( iTriFace[ iFace + 1 ]);
7692 const SMDS_MeshNode** hexanodes = hexa.GetNodes();
7693 // fill unique nodes
7696 for ( iCur = 0; iCur < 4 && isOk; iCur++ ) {
7697 const SMDS_MeshNode* n = curNodes[ind1[ iCur ]];
7698 const SMDS_MeshNode* nInit = hexanodes[ind1[ iCur ]];
7700 // iCur of a linked node of the opposite face (make normals co-directed):
7701 int iCurOpp = ( iCur == 1 || iCur == 3 ) ? 4 - iCur : iCur;
7702 // check that correspondent corners of triangles are linked
7703 if ( !hexa.IsLinked( ind1[ iCur ], ind2[ iCurOpp ] ))
7706 uniqueNodes[ iUnique ] = n;
7707 uniqueNodes[ iUnique + 3 ] = curNodes[ind2[ iCurOpp ]];
7716 } // if ( nbUniqueNodes == 6 && nbRepl == 4 )
7719 MESSAGE("MergeNodes() removes hexahedron "<< elem);
7726 } // switch ( nbNodes )
7728 } // if ( nbNodes != nbUniqueNodes ) // some nodes stick
7730 if ( isOk ) // the non-poly elem remains valid after sticking nodes
7732 elemType.Init( elem ).SetID( elem->GetID() );
7734 SMESHDS_SubMesh * sm = aShapeId > 0 ? aMesh->MeshElements(aShapeId) : 0;
7735 aMesh->RemoveFreeElement(elem, sm, /*fromGroups=*/false);
7737 uniqueNodes.resize(nbUniqueNodes);
7738 SMDS_MeshElement* newElem = this->AddElement( uniqueNodes, elemType );
7739 if ( sm && newElem )
7740 sm->AddElement( newElem );
7741 if ( elem != newElem )
7742 ReplaceElemInGroups( elem, newElem, aMesh );
7745 // Remove invalid regular element or invalid polygon
7746 rmElemIds.push_back( elem->GetID() );
7749 } // loop on elements
7751 // Remove bad elements, then equal nodes (order important)
7753 Remove( rmElemIds, false );
7754 Remove( rmNodeIds, true );
7760 // ========================================================
7761 // class : SortableElement
7762 // purpose : allow sorting elements basing on their nodes
7763 // ========================================================
7764 class SortableElement : public set <const SMDS_MeshElement*>
7768 SortableElement( const SMDS_MeshElement* theElem )
7771 SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator();
7772 while ( nodeIt->more() )
7773 this->insert( nodeIt->next() );
7776 const SMDS_MeshElement* Get() const
7779 void Set(const SMDS_MeshElement* e) const
7784 mutable const SMDS_MeshElement* myElem;
7787 //=======================================================================
7788 //function : FindEqualElements
7789 //purpose : Return list of group of elements built on the same nodes.
7790 // Search among theElements or in the whole mesh if theElements is empty
7791 //=======================================================================
7793 void SMESH_MeshEditor::FindEqualElements(TIDSortedElemSet & theElements,
7794 TListOfListOfElementsID & theGroupsOfElementsID)
7796 myLastCreatedElems.Clear();
7797 myLastCreatedNodes.Clear();
7799 typedef map< SortableElement, int > TMapOfNodeSet;
7800 typedef list<int> TGroupOfElems;
7802 if ( theElements.empty() )
7803 { // get all elements in the mesh
7804 SMDS_ElemIteratorPtr eIt = GetMeshDS()->elementsIterator();
7805 while ( eIt->more() )
7806 theElements.insert( theElements.end(), eIt->next() );
7809 vector< TGroupOfElems > arrayOfGroups;
7810 TGroupOfElems groupOfElems;
7811 TMapOfNodeSet mapOfNodeSet;
7813 TIDSortedElemSet::iterator elemIt = theElements.begin();
7814 for ( int i = 0; elemIt != theElements.end(); ++elemIt )
7816 const SMDS_MeshElement* curElem = *elemIt;
7817 SortableElement SE(curElem);
7819 pair< TMapOfNodeSet::iterator, bool> pp = mapOfNodeSet.insert(make_pair(SE, i));
7820 if ( !pp.second ) { // one more coincident elem
7821 TMapOfNodeSet::iterator& itSE = pp.first;
7822 int ind = (*itSE).second;
7823 arrayOfGroups[ind].push_back( curElem->GetID() );
7826 arrayOfGroups.push_back( groupOfElems );
7827 arrayOfGroups.back().push_back( curElem->GetID() );
7832 groupOfElems.clear();
7833 vector< TGroupOfElems >::iterator groupIt = arrayOfGroups.begin();
7834 for ( ; groupIt != arrayOfGroups.end(); ++groupIt )
7836 if ( groupIt->size() > 1 ) {
7837 //groupOfElems.sort(); -- theElements is sorted already
7838 theGroupsOfElementsID.push_back( groupOfElems );
7839 theGroupsOfElementsID.back().splice( theGroupsOfElementsID.back().end(), *groupIt );
7844 //=======================================================================
7845 //function : MergeElements
7846 //purpose : In each given group, substitute all elements by the first one.
7847 //=======================================================================
7849 void SMESH_MeshEditor::MergeElements(TListOfListOfElementsID & theGroupsOfElementsID)
7851 myLastCreatedElems.Clear();
7852 myLastCreatedNodes.Clear();
7854 typedef list<int> TListOfIDs;
7855 TListOfIDs rmElemIds; // IDs of elems to remove
7857 SMESHDS_Mesh* aMesh = GetMeshDS();
7859 TListOfListOfElementsID::iterator groupsIt = theGroupsOfElementsID.begin();
7860 while ( groupsIt != theGroupsOfElementsID.end() ) {
7861 TListOfIDs& aGroupOfElemID = *groupsIt;
7862 aGroupOfElemID.sort();
7863 int elemIDToKeep = aGroupOfElemID.front();
7864 const SMDS_MeshElement* elemToKeep = aMesh->FindElement(elemIDToKeep);
7865 aGroupOfElemID.pop_front();
7866 TListOfIDs::iterator idIt = aGroupOfElemID.begin();
7867 while ( idIt != aGroupOfElemID.end() ) {
7868 int elemIDToRemove = *idIt;
7869 const SMDS_MeshElement* elemToRemove = aMesh->FindElement(elemIDToRemove);
7870 // add the kept element in groups of removed one (PAL15188)
7871 AddToSameGroups( elemToKeep, elemToRemove, aMesh );
7872 rmElemIds.push_back( elemIDToRemove );
7878 Remove( rmElemIds, false );
7881 //=======================================================================
7882 //function : MergeEqualElements
7883 //purpose : Remove all but one of elements built on the same nodes.
7884 //=======================================================================
7886 void SMESH_MeshEditor::MergeEqualElements()
7888 TIDSortedElemSet aMeshElements; /* empty input ==
7889 to merge equal elements in the whole mesh */
7890 TListOfListOfElementsID aGroupsOfElementsID;
7891 FindEqualElements(aMeshElements, aGroupsOfElementsID);
7892 MergeElements(aGroupsOfElementsID);
7895 //=======================================================================
7896 //function : findAdjacentFace
7898 //=======================================================================
7900 static const SMDS_MeshElement* findAdjacentFace(const SMDS_MeshNode* n1,
7901 const SMDS_MeshNode* n2,
7902 const SMDS_MeshElement* elem)
7904 TIDSortedElemSet elemSet, avoidSet;
7906 avoidSet.insert ( elem );
7907 return SMESH_MeshAlgos::FindFaceInSet( n1, n2, elemSet, avoidSet );
7910 //=======================================================================
7911 //function : FindFreeBorder
7913 //=======================================================================
7915 #define ControlFreeBorder SMESH::Controls::FreeEdges::IsFreeEdge
7917 bool SMESH_MeshEditor::FindFreeBorder (const SMDS_MeshNode* theFirstNode,
7918 const SMDS_MeshNode* theSecondNode,
7919 const SMDS_MeshNode* theLastNode,
7920 list< const SMDS_MeshNode* > & theNodes,
7921 list< const SMDS_MeshElement* >& theFaces)
7923 if ( !theFirstNode || !theSecondNode )
7925 // find border face between theFirstNode and theSecondNode
7926 const SMDS_MeshElement* curElem = findAdjacentFace( theFirstNode, theSecondNode, 0 );
7930 theFaces.push_back( curElem );
7931 theNodes.push_back( theFirstNode );
7932 theNodes.push_back( theSecondNode );
7934 //vector<const SMDS_MeshNode*> nodes;
7935 const SMDS_MeshNode *nIgnore = theFirstNode, *nStart = theSecondNode;
7936 TIDSortedElemSet foundElems;
7937 bool needTheLast = ( theLastNode != 0 );
7939 while ( nStart != theLastNode ) {
7940 if ( nStart == theFirstNode )
7941 return !needTheLast;
7943 // find all free border faces sharing form nStart
7945 list< const SMDS_MeshElement* > curElemList;
7946 list< const SMDS_MeshNode* > nStartList;
7947 SMDS_ElemIteratorPtr invElemIt = nStart->GetInverseElementIterator(SMDSAbs_Face);
7948 while ( invElemIt->more() ) {
7949 const SMDS_MeshElement* e = invElemIt->next();
7950 if ( e == curElem || foundElems.insert( e ).second ) {
7952 int iNode = 0, nbNodes = e->NbNodes();
7953 //const SMDS_MeshNode* nodes[nbNodes+1];
7954 vector<const SMDS_MeshNode*> nodes(nbNodes+1);
7956 if(e->IsQuadratic()) {
7957 const SMDS_VtkFace* F =
7958 dynamic_cast<const SMDS_VtkFace*>(e);
7959 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
7960 // use special nodes iterator
7961 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
7962 while( anIter->more() ) {
7963 nodes[ iNode++ ] = cast2Node(anIter->next());
7967 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
7968 while ( nIt->more() )
7969 nodes[ iNode++ ] = static_cast<const SMDS_MeshNode*>( nIt->next() );
7971 nodes[ iNode ] = nodes[ 0 ];
7973 for ( iNode = 0; iNode < nbNodes; iNode++ )
7974 if (((nodes[ iNode ] == nStart && nodes[ iNode + 1] != nIgnore ) ||
7975 (nodes[ iNode + 1] == nStart && nodes[ iNode ] != nIgnore )) &&
7976 ControlFreeBorder( &nodes[ iNode ], e->GetID() ))
7978 nStartList.push_back( nodes[ iNode + ( nodes[ iNode ] == nStart ? 1 : 0 )]);
7979 curElemList.push_back( e );
7983 // analyse the found
7985 int nbNewBorders = curElemList.size();
7986 if ( nbNewBorders == 0 ) {
7987 // no free border furthermore
7988 return !needTheLast;
7990 else if ( nbNewBorders == 1 ) {
7991 // one more element found
7993 nStart = nStartList.front();
7994 curElem = curElemList.front();
7995 theFaces.push_back( curElem );
7996 theNodes.push_back( nStart );
7999 // several continuations found
8000 list< const SMDS_MeshElement* >::iterator curElemIt;
8001 list< const SMDS_MeshNode* >::iterator nStartIt;
8002 // check if one of them reached the last node
8003 if ( needTheLast ) {
8004 for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
8005 curElemIt!= curElemList.end();
8006 curElemIt++, nStartIt++ )
8007 if ( *nStartIt == theLastNode ) {
8008 theFaces.push_back( *curElemIt );
8009 theNodes.push_back( *nStartIt );
8013 // find the best free border by the continuations
8014 list<const SMDS_MeshNode*> contNodes[ 2 ], *cNL;
8015 list<const SMDS_MeshElement*> contFaces[ 2 ], *cFL;
8016 for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
8017 curElemIt!= curElemList.end();
8018 curElemIt++, nStartIt++ )
8020 cNL = & contNodes[ contNodes[0].empty() ? 0 : 1 ];
8021 cFL = & contFaces[ contFaces[0].empty() ? 0 : 1 ];
8022 // find one more free border
8023 if ( ! SMESH_MeshEditor::FindFreeBorder( nStart, *nStartIt, theLastNode, *cNL, *cFL )) {
8027 else if ( !contNodes[0].empty() && !contNodes[1].empty() ) {
8028 // choice: clear a worse one
8029 int iLongest = ( contNodes[0].size() < contNodes[1].size() ? 1 : 0 );
8030 int iWorse = ( needTheLast ? 1 - iLongest : iLongest );
8031 contNodes[ iWorse ].clear();
8032 contFaces[ iWorse ].clear();
8035 if ( contNodes[0].empty() && contNodes[1].empty() )
8038 // append the best free border
8039 cNL = & contNodes[ contNodes[0].empty() ? 1 : 0 ];
8040 cFL = & contFaces[ contFaces[0].empty() ? 1 : 0 ];
8041 theNodes.pop_back(); // remove nIgnore
8042 theNodes.pop_back(); // remove nStart
8043 theFaces.pop_back(); // remove curElem
8044 list< const SMDS_MeshNode* >::iterator nIt = cNL->begin();
8045 list< const SMDS_MeshElement* >::iterator fIt = cFL->begin();
8046 for ( ; nIt != cNL->end(); nIt++ ) theNodes.push_back( *nIt );
8047 for ( ; fIt != cFL->end(); fIt++ ) theFaces.push_back( *fIt );
8050 } // several continuations found
8051 } // while ( nStart != theLastNode )
8056 //=======================================================================
8057 //function : CheckFreeBorderNodes
8058 //purpose : Return true if the tree nodes are on a free border
8059 //=======================================================================
8061 bool SMESH_MeshEditor::CheckFreeBorderNodes(const SMDS_MeshNode* theNode1,
8062 const SMDS_MeshNode* theNode2,
8063 const SMDS_MeshNode* theNode3)
8065 list< const SMDS_MeshNode* > nodes;
8066 list< const SMDS_MeshElement* > faces;
8067 return FindFreeBorder( theNode1, theNode2, theNode3, nodes, faces);
8070 //=======================================================================
8071 //function : SewFreeBorder
8073 //=======================================================================
8075 SMESH_MeshEditor::Sew_Error
8076 SMESH_MeshEditor::SewFreeBorder (const SMDS_MeshNode* theBordFirstNode,
8077 const SMDS_MeshNode* theBordSecondNode,
8078 const SMDS_MeshNode* theBordLastNode,
8079 const SMDS_MeshNode* theSideFirstNode,
8080 const SMDS_MeshNode* theSideSecondNode,
8081 const SMDS_MeshNode* theSideThirdNode,
8082 const bool theSideIsFreeBorder,
8083 const bool toCreatePolygons,
8084 const bool toCreatePolyedrs)
8086 myLastCreatedElems.Clear();
8087 myLastCreatedNodes.Clear();
8089 MESSAGE("::SewFreeBorder()");
8090 Sew_Error aResult = SEW_OK;
8092 // ====================================
8093 // find side nodes and elements
8094 // ====================================
8096 list< const SMDS_MeshNode* > nSide[ 2 ];
8097 list< const SMDS_MeshElement* > eSide[ 2 ];
8098 list< const SMDS_MeshNode* >::iterator nIt[ 2 ];
8099 list< const SMDS_MeshElement* >::iterator eIt[ 2 ];
8103 if (!FindFreeBorder(theBordFirstNode,theBordSecondNode,theBordLastNode,
8104 nSide[0], eSide[0])) {
8105 MESSAGE(" Free Border 1 not found " );
8106 aResult = SEW_BORDER1_NOT_FOUND;
8108 if (theSideIsFreeBorder) {
8111 if (!FindFreeBorder(theSideFirstNode, theSideSecondNode, theSideThirdNode,
8112 nSide[1], eSide[1])) {
8113 MESSAGE(" Free Border 2 not found " );
8114 aResult = ( aResult != SEW_OK ? SEW_BOTH_BORDERS_NOT_FOUND : SEW_BORDER2_NOT_FOUND );
8117 if ( aResult != SEW_OK )
8120 if (!theSideIsFreeBorder) {
8124 // -------------------------------------------------------------------------
8126 // 1. If nodes to merge are not coincident, move nodes of the free border
8127 // from the coord sys defined by the direction from the first to last
8128 // nodes of the border to the correspondent sys of the side 2
8129 // 2. On the side 2, find the links most co-directed with the correspondent
8130 // links of the free border
8131 // -------------------------------------------------------------------------
8133 // 1. Since sewing may break if there are volumes to split on the side 2,
8134 // we wont move nodes but just compute new coordinates for them
8135 typedef map<const SMDS_MeshNode*, gp_XYZ> TNodeXYZMap;
8136 TNodeXYZMap nBordXYZ;
8137 list< const SMDS_MeshNode* >& bordNodes = nSide[ 0 ];
8138 list< const SMDS_MeshNode* >::iterator nBordIt;
8140 gp_XYZ Pb1( theBordFirstNode->X(), theBordFirstNode->Y(), theBordFirstNode->Z() );
8141 gp_XYZ Pb2( theBordLastNode->X(), theBordLastNode->Y(), theBordLastNode->Z() );
8142 gp_XYZ Ps1( theSideFirstNode->X(), theSideFirstNode->Y(), theSideFirstNode->Z() );
8143 gp_XYZ Ps2( theSideSecondNode->X(), theSideSecondNode->Y(), theSideSecondNode->Z() );
8144 double tol2 = 1.e-8;
8145 gp_Vec Vbs1( Pb1 - Ps1 ),Vbs2( Pb2 - Ps2 );
8146 if ( Vbs1.SquareMagnitude() > tol2 || Vbs2.SquareMagnitude() > tol2 ) {
8147 // Need node movement.
8149 // find X and Z axes to create trsf
8150 gp_Vec Zb( Pb1 - Pb2 ), Zs( Ps1 - Ps2 );
8152 if ( X.SquareMagnitude() <= gp::Resolution() * gp::Resolution() )
8154 X = gp_Ax2( gp::Origin(), Zb ).XDirection();
8157 gp_Ax3 toBordAx( Pb1, Zb, X );
8158 gp_Ax3 fromSideAx( Ps1, Zs, X );
8159 gp_Ax3 toGlobalAx( gp::Origin(), gp::DZ(), gp::DX() );
8161 gp_Trsf toBordSys, fromSide2Sys;
8162 toBordSys.SetTransformation( toBordAx );
8163 fromSide2Sys.SetTransformation( fromSideAx, toGlobalAx );
8164 fromSide2Sys.SetScaleFactor( Zs.Magnitude() / Zb.Magnitude() );
8167 for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
8168 const SMDS_MeshNode* n = *nBordIt;
8169 gp_XYZ xyz( n->X(),n->Y(),n->Z() );
8170 toBordSys.Transforms( xyz );
8171 fromSide2Sys.Transforms( xyz );
8172 nBordXYZ.insert( TNodeXYZMap::value_type( n, xyz ));
8176 // just insert nodes XYZ in the nBordXYZ map
8177 for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
8178 const SMDS_MeshNode* n = *nBordIt;
8179 nBordXYZ.insert( TNodeXYZMap::value_type( n, gp_XYZ( n->X(),n->Y(),n->Z() )));
8183 // 2. On the side 2, find the links most co-directed with the correspondent
8184 // links of the free border
8186 list< const SMDS_MeshElement* >& sideElems = eSide[ 1 ];
8187 list< const SMDS_MeshNode* >& sideNodes = nSide[ 1 ];
8188 sideNodes.push_back( theSideFirstNode );
8190 bool hasVolumes = false;
8191 LinkID_Gen aLinkID_Gen( GetMeshDS() );
8192 set<long> foundSideLinkIDs, checkedLinkIDs;
8193 SMDS_VolumeTool volume;
8194 //const SMDS_MeshNode* faceNodes[ 4 ];
8196 const SMDS_MeshNode* sideNode;
8197 const SMDS_MeshElement* sideElem;
8198 const SMDS_MeshNode* prevSideNode = theSideFirstNode;
8199 const SMDS_MeshNode* prevBordNode = theBordFirstNode;
8200 nBordIt = bordNodes.begin();
8202 // border node position and border link direction to compare with
8203 gp_XYZ bordPos = nBordXYZ[ *nBordIt ];
8204 gp_XYZ bordDir = bordPos - nBordXYZ[ prevBordNode ];
8205 // choose next side node by link direction or by closeness to
8206 // the current border node:
8207 bool searchByDir = ( *nBordIt != theBordLastNode );
8209 // find the next node on the Side 2
8211 double maxDot = -DBL_MAX, minDist = DBL_MAX;
8213 checkedLinkIDs.clear();
8214 gp_XYZ prevXYZ( prevSideNode->X(), prevSideNode->Y(), prevSideNode->Z() );
8216 // loop on inverse elements of current node (prevSideNode) on the Side 2
8217 SMDS_ElemIteratorPtr invElemIt = prevSideNode->GetInverseElementIterator();
8218 while ( invElemIt->more() )
8220 const SMDS_MeshElement* elem = invElemIt->next();
8221 // prepare data for a loop on links coming to prevSideNode, of a face or a volume
8222 int iPrevNode, iNode = 0, nbNodes = elem->NbNodes();
8223 vector< const SMDS_MeshNode* > faceNodes( nbNodes, (const SMDS_MeshNode*)0 );
8224 bool isVolume = volume.Set( elem );
8225 const SMDS_MeshNode** nodes = isVolume ? volume.GetNodes() : & faceNodes[0];
8226 if ( isVolume ) // --volume
8228 else if ( elem->GetType()==SMDSAbs_Face ) { // --face
8229 // retrieve all face nodes and find iPrevNode - an index of the prevSideNode
8230 if(elem->IsQuadratic()) {
8231 const SMDS_VtkFace* F =
8232 dynamic_cast<const SMDS_VtkFace*>(elem);
8233 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8234 // use special nodes iterator
8235 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8236 while( anIter->more() ) {
8237 nodes[ iNode ] = cast2Node(anIter->next());
8238 if ( nodes[ iNode++ ] == prevSideNode )
8239 iPrevNode = iNode - 1;
8243 SMDS_ElemIteratorPtr nIt = elem->nodesIterator();
8244 while ( nIt->more() ) {
8245 nodes[ iNode ] = cast2Node( nIt->next() );
8246 if ( nodes[ iNode++ ] == prevSideNode )
8247 iPrevNode = iNode - 1;
8250 // there are 2 links to check
8255 // loop on links, to be precise, on the second node of links
8256 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
8257 const SMDS_MeshNode* n = nodes[ iNode ];
8259 if ( !volume.IsLinked( n, prevSideNode ))
8263 if ( iNode ) // a node before prevSideNode
8264 n = nodes[ iPrevNode == 0 ? elem->NbNodes() - 1 : iPrevNode - 1 ];
8265 else // a node after prevSideNode
8266 n = nodes[ iPrevNode + 1 == elem->NbNodes() ? 0 : iPrevNode + 1 ];
8268 // check if this link was already used
8269 long iLink = aLinkID_Gen.GetLinkID( prevSideNode, n );
8270 bool isJustChecked = !checkedLinkIDs.insert( iLink ).second;
8271 if (!isJustChecked &&
8272 foundSideLinkIDs.find( iLink ) == foundSideLinkIDs.end() )
8274 // test a link geometrically
8275 gp_XYZ nextXYZ ( n->X(), n->Y(), n->Z() );
8276 bool linkIsBetter = false;
8277 double dot = 0.0, dist = 0.0;
8278 if ( searchByDir ) { // choose most co-directed link
8279 dot = bordDir * ( nextXYZ - prevXYZ ).Normalized();
8280 linkIsBetter = ( dot > maxDot );
8282 else { // choose link with the node closest to bordPos
8283 dist = ( nextXYZ - bordPos ).SquareModulus();
8284 linkIsBetter = ( dist < minDist );
8286 if ( linkIsBetter ) {
8295 } // loop on inverse elements of prevSideNode
8298 MESSAGE(" Cant find path by links of the Side 2 ");
8299 return SEW_BAD_SIDE_NODES;
8301 sideNodes.push_back( sideNode );
8302 sideElems.push_back( sideElem );
8303 foundSideLinkIDs.insert ( linkID );
8304 prevSideNode = sideNode;
8306 if ( *nBordIt == theBordLastNode )
8307 searchByDir = false;
8309 // find the next border link to compare with
8310 gp_XYZ sidePos( sideNode->X(), sideNode->Y(), sideNode->Z() );
8311 searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8312 // move to next border node if sideNode is before forward border node (bordPos)
8313 while ( *nBordIt != theBordLastNode && !searchByDir ) {
8314 prevBordNode = *nBordIt;
8316 bordPos = nBordXYZ[ *nBordIt ];
8317 bordDir = bordPos - nBordXYZ[ prevBordNode ];
8318 searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8322 while ( sideNode != theSideSecondNode );
8324 if ( hasVolumes && sideNodes.size () != bordNodes.size() && !toCreatePolyedrs) {
8325 MESSAGE("VOLUME SPLITTING IS FORBIDDEN");
8326 return SEW_VOLUMES_TO_SPLIT; // volume splitting is forbidden
8328 } // end nodes search on the side 2
8330 // ============================
8331 // sew the border to the side 2
8332 // ============================
8334 int nbNodes[] = { nSide[0].size(), nSide[1].size() };
8335 int maxNbNodes = Max( nbNodes[0], nbNodes[1] );
8337 TListOfListOfNodes nodeGroupsToMerge;
8338 if ( nbNodes[0] == nbNodes[1] ||
8339 ( theSideIsFreeBorder && !theSideThirdNode)) {
8341 // all nodes are to be merged
8343 for (nIt[0] = nSide[0].begin(), nIt[1] = nSide[1].begin();
8344 nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end();
8345 nIt[0]++, nIt[1]++ )
8347 nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8348 nodeGroupsToMerge.back().push_back( *nIt[1] ); // to keep
8349 nodeGroupsToMerge.back().push_back( *nIt[0] ); // to remove
8354 // insert new nodes into the border and the side to get equal nb of segments
8356 // get normalized parameters of nodes on the borders
8357 //double param[ 2 ][ maxNbNodes ];
8359 param[0] = new double [ maxNbNodes ];
8360 param[1] = new double [ maxNbNodes ];
8362 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8363 list< const SMDS_MeshNode* >& nodes = nSide[ iBord ];
8364 list< const SMDS_MeshNode* >::iterator nIt = nodes.begin();
8365 const SMDS_MeshNode* nPrev = *nIt;
8366 double bordLength = 0;
8367 for ( iNode = 0; nIt != nodes.end(); nIt++, iNode++ ) { // loop on border nodes
8368 const SMDS_MeshNode* nCur = *nIt;
8369 gp_XYZ segment (nCur->X() - nPrev->X(),
8370 nCur->Y() - nPrev->Y(),
8371 nCur->Z() - nPrev->Z());
8372 double segmentLen = segment.Modulus();
8373 bordLength += segmentLen;
8374 param[ iBord ][ iNode ] = bordLength;
8377 // normalize within [0,1]
8378 for ( iNode = 0; iNode < nbNodes[ iBord ]; iNode++ ) {
8379 param[ iBord ][ iNode ] /= bordLength;
8383 // loop on border segments
8384 const SMDS_MeshNode *nPrev[ 2 ] = { 0, 0 };
8385 int i[ 2 ] = { 0, 0 };
8386 nIt[0] = nSide[0].begin(); eIt[0] = eSide[0].begin();
8387 nIt[1] = nSide[1].begin(); eIt[1] = eSide[1].begin();
8389 TElemOfNodeListMap insertMap;
8390 TElemOfNodeListMap::iterator insertMapIt;
8392 // key: elem to insert nodes into
8393 // value: 2 nodes to insert between + nodes to be inserted
8395 bool next[ 2 ] = { false, false };
8397 // find min adjacent segment length after sewing
8398 double nextParam = 10., prevParam = 0;
8399 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8400 if ( i[ iBord ] + 1 < nbNodes[ iBord ])
8401 nextParam = Min( nextParam, param[iBord][ i[iBord] + 1 ]);
8402 if ( i[ iBord ] > 0 )
8403 prevParam = Max( prevParam, param[iBord][ i[iBord] - 1 ]);
8405 double minParam = Min( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8406 double maxParam = Max( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8407 double minSegLen = Min( nextParam - minParam, maxParam - prevParam );
8409 // choose to insert or to merge nodes
8410 double du = param[ 1 ][ i[1] ] - param[ 0 ][ i[0] ];
8411 if ( Abs( du ) <= minSegLen * 0.2 ) {
8414 nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8415 const SMDS_MeshNode* n0 = *nIt[0];
8416 const SMDS_MeshNode* n1 = *nIt[1];
8417 nodeGroupsToMerge.back().push_back( n1 );
8418 nodeGroupsToMerge.back().push_back( n0 );
8419 // position of node of the border changes due to merge
8420 param[ 0 ][ i[0] ] += du;
8421 // move n1 for the sake of elem shape evaluation during insertion.
8422 // n1 will be removed by MergeNodes() anyway
8423 const_cast<SMDS_MeshNode*>( n0 )->setXYZ( n1->X(), n1->Y(), n1->Z() );
8424 next[0] = next[1] = true;
8429 int intoBord = ( du < 0 ) ? 0 : 1;
8430 const SMDS_MeshElement* elem = *eIt[ intoBord ];
8431 const SMDS_MeshNode* n1 = nPrev[ intoBord ];
8432 const SMDS_MeshNode* n2 = *nIt[ intoBord ];
8433 const SMDS_MeshNode* nIns = *nIt[ 1 - intoBord ];
8434 if ( intoBord == 1 ) {
8435 // move node of the border to be on a link of elem of the side
8436 gp_XYZ p1 (n1->X(), n1->Y(), n1->Z());
8437 gp_XYZ p2 (n2->X(), n2->Y(), n2->Z());
8438 double ratio = du / ( param[ 1 ][ i[1] ] - param[ 1 ][ i[1]-1 ]);
8439 gp_XYZ p = p2 * ( 1 - ratio ) + p1 * ratio;
8440 GetMeshDS()->MoveNode( nIns, p.X(), p.Y(), p.Z() );
8442 insertMapIt = insertMap.find( elem );
8443 bool notFound = ( insertMapIt == insertMap.end() );
8444 bool otherLink = ( !notFound && (*insertMapIt).second.front() != n1 );
8446 // insert into another link of the same element:
8447 // 1. perform insertion into the other link of the elem
8448 list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8449 const SMDS_MeshNode* n12 = nodeList.front(); nodeList.pop_front();
8450 const SMDS_MeshNode* n22 = nodeList.front(); nodeList.pop_front();
8451 InsertNodesIntoLink( elem, n12, n22, nodeList, toCreatePolygons );
8452 // 2. perform insertion into the link of adjacent faces
8454 const SMDS_MeshElement* adjElem = findAdjacentFace( n12, n22, elem );
8456 InsertNodesIntoLink( adjElem, n12, n22, nodeList, toCreatePolygons );
8460 if (toCreatePolyedrs) {
8461 // perform insertion into the links of adjacent volumes
8462 UpdateVolumes(n12, n22, nodeList);
8464 // 3. find an element appeared on n1 and n2 after the insertion
8465 insertMap.erase( elem );
8466 elem = findAdjacentFace( n1, n2, 0 );
8468 if ( notFound || otherLink ) {
8469 // add element and nodes of the side into the insertMap
8470 insertMapIt = insertMap.insert
8471 ( TElemOfNodeListMap::value_type( elem, list<const SMDS_MeshNode*>() )).first;
8472 (*insertMapIt).second.push_back( n1 );
8473 (*insertMapIt).second.push_back( n2 );
8475 // add node to be inserted into elem
8476 (*insertMapIt).second.push_back( nIns );
8477 next[ 1 - intoBord ] = true;
8480 // go to the next segment
8481 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8482 if ( next[ iBord ] ) {
8483 if ( i[ iBord ] != 0 && eIt[ iBord ] != eSide[ iBord ].end())
8485 nPrev[ iBord ] = *nIt[ iBord ];
8486 nIt[ iBord ]++; i[ iBord ]++;
8490 while ( nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end());
8492 // perform insertion of nodes into elements
8494 for (insertMapIt = insertMap.begin();
8495 insertMapIt != insertMap.end();
8498 const SMDS_MeshElement* elem = (*insertMapIt).first;
8499 list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8500 const SMDS_MeshNode* n1 = nodeList.front(); nodeList.pop_front();
8501 const SMDS_MeshNode* n2 = nodeList.front(); nodeList.pop_front();
8503 InsertNodesIntoLink( elem, n1, n2, nodeList, toCreatePolygons );
8505 if ( !theSideIsFreeBorder ) {
8506 // look for and insert nodes into the faces adjacent to elem
8508 const SMDS_MeshElement* adjElem = findAdjacentFace( n1, n2, elem );
8510 InsertNodesIntoLink( adjElem, n1, n2, nodeList, toCreatePolygons );
8515 if (toCreatePolyedrs) {
8516 // perform insertion into the links of adjacent volumes
8517 UpdateVolumes(n1, n2, nodeList);
8523 } // end: insert new nodes
8525 MergeNodes ( nodeGroupsToMerge );
8530 //=======================================================================
8531 //function : InsertNodesIntoLink
8532 //purpose : insert theNodesToInsert into theFace between theBetweenNode1
8533 // and theBetweenNode2 and split theElement
8534 //=======================================================================
8536 void SMESH_MeshEditor::InsertNodesIntoLink(const SMDS_MeshElement* theFace,
8537 const SMDS_MeshNode* theBetweenNode1,
8538 const SMDS_MeshNode* theBetweenNode2,
8539 list<const SMDS_MeshNode*>& theNodesToInsert,
8540 const bool toCreatePoly)
8542 if ( theFace->GetType() != SMDSAbs_Face ) return;
8544 // find indices of 2 link nodes and of the rest nodes
8545 int iNode = 0, il1, il2, i3, i4;
8546 il1 = il2 = i3 = i4 = -1;
8547 //const SMDS_MeshNode* nodes[ theFace->NbNodes() ];
8548 vector<const SMDS_MeshNode*> nodes( theFace->NbNodes() );
8550 if(theFace->IsQuadratic()) {
8551 const SMDS_VtkFace* F =
8552 dynamic_cast<const SMDS_VtkFace*>(theFace);
8553 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8554 // use special nodes iterator
8555 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8556 while( anIter->more() ) {
8557 const SMDS_MeshNode* n = cast2Node(anIter->next());
8558 if ( n == theBetweenNode1 )
8560 else if ( n == theBetweenNode2 )
8566 nodes[ iNode++ ] = n;
8570 SMDS_ElemIteratorPtr nodeIt = theFace->nodesIterator();
8571 while ( nodeIt->more() ) {
8572 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8573 if ( n == theBetweenNode1 )
8575 else if ( n == theBetweenNode2 )
8581 nodes[ iNode++ ] = n;
8584 if ( il1 < 0 || il2 < 0 || i3 < 0 )
8587 // arrange link nodes to go one after another regarding the face orientation
8588 bool reverse = ( Abs( il2 - il1 ) == 1 ? il2 < il1 : il1 < il2 );
8589 list<const SMDS_MeshNode *> aNodesToInsert = theNodesToInsert;
8594 aNodesToInsert.reverse();
8596 // check that not link nodes of a quadrangles are in good order
8597 int nbFaceNodes = theFace->NbNodes();
8598 if ( nbFaceNodes == 4 && i4 - i3 != 1 ) {
8604 if (toCreatePoly || theFace->IsPoly()) {
8607 vector<const SMDS_MeshNode *> poly_nodes (nbFaceNodes + aNodesToInsert.size());
8609 // add nodes of face up to first node of link
8612 if(theFace->IsQuadratic()) {
8613 const SMDS_VtkFace* F =
8614 dynamic_cast<const SMDS_VtkFace*>(theFace);
8615 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8616 // use special nodes iterator
8617 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8618 while( anIter->more() && !isFLN ) {
8619 const SMDS_MeshNode* n = cast2Node(anIter->next());
8620 poly_nodes[iNode++] = n;
8621 if (n == nodes[il1]) {
8625 // add nodes to insert
8626 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8627 for (; nIt != aNodesToInsert.end(); nIt++) {
8628 poly_nodes[iNode++] = *nIt;
8630 // add nodes of face starting from last node of link
8631 while ( anIter->more() ) {
8632 poly_nodes[iNode++] = cast2Node(anIter->next());
8636 SMDS_ElemIteratorPtr nodeIt = theFace->nodesIterator();
8637 while ( nodeIt->more() && !isFLN ) {
8638 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8639 poly_nodes[iNode++] = n;
8640 if (n == nodes[il1]) {
8644 // add nodes to insert
8645 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8646 for (; nIt != aNodesToInsert.end(); nIt++) {
8647 poly_nodes[iNode++] = *nIt;
8649 // add nodes of face starting from last node of link
8650 while ( nodeIt->more() ) {
8651 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8652 poly_nodes[iNode++] = n;
8656 // edit or replace the face
8657 SMESHDS_Mesh *aMesh = GetMeshDS();
8659 if (theFace->IsPoly()) {
8660 aMesh->ChangePolygonNodes(theFace, poly_nodes);
8663 int aShapeId = FindShape( theFace );
8665 SMDS_MeshElement* newElem = aMesh->AddPolygonalFace(poly_nodes);
8666 myLastCreatedElems.Append(newElem);
8667 if ( aShapeId && newElem )
8668 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8670 aMesh->RemoveElement(theFace);
8675 SMESHDS_Mesh *aMesh = GetMeshDS();
8676 if( !theFace->IsQuadratic() ) {
8678 // put aNodesToInsert between theBetweenNode1 and theBetweenNode2
8679 int nbLinkNodes = 2 + aNodesToInsert.size();
8680 //const SMDS_MeshNode* linkNodes[ nbLinkNodes ];
8681 vector<const SMDS_MeshNode*> linkNodes( nbLinkNodes );
8682 linkNodes[ 0 ] = nodes[ il1 ];
8683 linkNodes[ nbLinkNodes - 1 ] = nodes[ il2 ];
8684 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8685 for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
8686 linkNodes[ iNode++ ] = *nIt;
8688 // decide how to split a quadrangle: compare possible variants
8689 // and choose which of splits to be a quadrangle
8690 int i1, i2, iSplit, nbSplits = nbLinkNodes - 1, iBestQuad;
8691 if ( nbFaceNodes == 3 ) {
8692 iBestQuad = nbSplits;
8695 else if ( nbFaceNodes == 4 ) {
8696 SMESH::Controls::NumericalFunctorPtr aCrit( new SMESH::Controls::AspectRatio);
8697 double aBestRate = DBL_MAX;
8698 for ( int iQuad = 0; iQuad < nbSplits; iQuad++ ) {
8700 double aBadRate = 0;
8701 // evaluate elements quality
8702 for ( iSplit = 0; iSplit < nbSplits; iSplit++ ) {
8703 if ( iSplit == iQuad ) {
8704 SMDS_FaceOfNodes quad (linkNodes[ i1++ ],
8708 aBadRate += getBadRate( &quad, aCrit );
8711 SMDS_FaceOfNodes tria (linkNodes[ i1++ ],
8713 nodes[ iSplit < iQuad ? i4 : i3 ]);
8714 aBadRate += getBadRate( &tria, aCrit );
8718 if ( aBadRate < aBestRate ) {
8720 aBestRate = aBadRate;
8725 // create new elements
8726 int aShapeId = FindShape( theFace );
8729 for ( iSplit = 0; iSplit < nbSplits - 1; iSplit++ ) {
8730 SMDS_MeshElement* newElem = 0;
8731 if ( iSplit == iBestQuad )
8732 newElem = aMesh->AddFace (linkNodes[ i1++ ],
8737 newElem = aMesh->AddFace (linkNodes[ i1++ ],
8739 nodes[ iSplit < iBestQuad ? i4 : i3 ]);
8740 myLastCreatedElems.Append(newElem);
8741 if ( aShapeId && newElem )
8742 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8745 // change nodes of theFace
8746 const SMDS_MeshNode* newNodes[ 4 ];
8747 newNodes[ 0 ] = linkNodes[ i1 ];
8748 newNodes[ 1 ] = linkNodes[ i2 ];
8749 newNodes[ 2 ] = nodes[ iSplit >= iBestQuad ? i3 : i4 ];
8750 newNodes[ 3 ] = nodes[ i4 ];
8751 //aMesh->ChangeElementNodes( theFace, newNodes, iSplit == iBestQuad ? 4 : 3 );
8752 const SMDS_MeshElement* newElem = 0;
8753 if (iSplit == iBestQuad)
8754 newElem = aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2], newNodes[3] );
8756 newElem = aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2] );
8757 myLastCreatedElems.Append(newElem);
8758 if ( aShapeId && newElem )
8759 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8760 } // end if(!theFace->IsQuadratic())
8761 else { // theFace is quadratic
8762 // we have to split theFace on simple triangles and one simple quadrangle
8764 int nbshift = tmp*2;
8765 // shift nodes in nodes[] by nbshift
8767 for(i=0; i<nbshift; i++) {
8768 const SMDS_MeshNode* n = nodes[0];
8769 for(j=0; j<nbFaceNodes-1; j++) {
8770 nodes[j] = nodes[j+1];
8772 nodes[nbFaceNodes-1] = n;
8774 il1 = il1 - nbshift;
8775 // now have to insert nodes between n0 and n1 or n1 and n2 (see below)
8776 // n0 n1 n2 n0 n1 n2
8777 // +-----+-----+ +-----+-----+
8786 // create new elements
8787 int aShapeId = FindShape( theFace );
8790 if(nbFaceNodes==6) { // quadratic triangle
8791 SMDS_MeshElement* newElem =
8792 aMesh->AddFace(nodes[3],nodes[4],nodes[5]);
8793 myLastCreatedElems.Append(newElem);
8794 if ( aShapeId && newElem )
8795 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8796 if(theFace->IsMediumNode(nodes[il1])) {
8797 // create quadrangle
8798 newElem = aMesh->AddFace(nodes[0],nodes[1],nodes[3],nodes[5]);
8799 myLastCreatedElems.Append(newElem);
8800 if ( aShapeId && newElem )
8801 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8807 // create quadrangle
8808 newElem = aMesh->AddFace(nodes[1],nodes[2],nodes[3],nodes[5]);
8809 myLastCreatedElems.Append(newElem);
8810 if ( aShapeId && newElem )
8811 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8817 else { // nbFaceNodes==8 - quadratic quadrangle
8818 SMDS_MeshElement* newElem =
8819 aMesh->AddFace(nodes[3],nodes[4],nodes[5]);
8820 myLastCreatedElems.Append(newElem);
8821 if ( aShapeId && newElem )
8822 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8823 newElem = aMesh->AddFace(nodes[5],nodes[6],nodes[7]);
8824 myLastCreatedElems.Append(newElem);
8825 if ( aShapeId && newElem )
8826 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8827 newElem = aMesh->AddFace(nodes[5],nodes[7],nodes[3]);
8828 myLastCreatedElems.Append(newElem);
8829 if ( aShapeId && newElem )
8830 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8831 if(theFace->IsMediumNode(nodes[il1])) {
8832 // create quadrangle
8833 newElem = aMesh->AddFace(nodes[0],nodes[1],nodes[3],nodes[7]);
8834 myLastCreatedElems.Append(newElem);
8835 if ( aShapeId && newElem )
8836 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8842 // create quadrangle
8843 newElem = aMesh->AddFace(nodes[1],nodes[2],nodes[3],nodes[7]);
8844 myLastCreatedElems.Append(newElem);
8845 if ( aShapeId && newElem )
8846 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8852 // create needed triangles using n1,n2,n3 and inserted nodes
8853 int nbn = 2 + aNodesToInsert.size();
8854 //const SMDS_MeshNode* aNodes[nbn];
8855 vector<const SMDS_MeshNode*> aNodes(nbn);
8856 aNodes[0] = nodes[n1];
8857 aNodes[nbn-1] = nodes[n2];
8858 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8859 for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
8860 aNodes[iNode++] = *nIt;
8862 for(i=1; i<nbn; i++) {
8863 SMDS_MeshElement* newElem =
8864 aMesh->AddFace(aNodes[i-1],aNodes[i],nodes[n3]);
8865 myLastCreatedElems.Append(newElem);
8866 if ( aShapeId && newElem )
8867 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8871 aMesh->RemoveElement(theFace);
8874 //=======================================================================
8875 //function : UpdateVolumes
8877 //=======================================================================
8878 void SMESH_MeshEditor::UpdateVolumes (const SMDS_MeshNode* theBetweenNode1,
8879 const SMDS_MeshNode* theBetweenNode2,
8880 list<const SMDS_MeshNode*>& theNodesToInsert)
8882 myLastCreatedElems.Clear();
8883 myLastCreatedNodes.Clear();
8885 SMDS_ElemIteratorPtr invElemIt = theBetweenNode1->GetInverseElementIterator(SMDSAbs_Volume);
8886 while (invElemIt->more()) { // loop on inverse elements of theBetweenNode1
8887 const SMDS_MeshElement* elem = invElemIt->next();
8889 // check, if current volume has link theBetweenNode1 - theBetweenNode2
8890 SMDS_VolumeTool aVolume (elem);
8891 if (!aVolume.IsLinked(theBetweenNode1, theBetweenNode2))
8894 // insert new nodes in all faces of the volume, sharing link theBetweenNode1 - theBetweenNode2
8895 int iface, nbFaces = aVolume.NbFaces();
8896 vector<const SMDS_MeshNode *> poly_nodes;
8897 vector<int> quantities (nbFaces);
8899 for (iface = 0; iface < nbFaces; iface++) {
8900 int nbFaceNodes = aVolume.NbFaceNodes(iface), nbInserted = 0;
8901 // faceNodes will contain (nbFaceNodes + 1) nodes, last = first
8902 const SMDS_MeshNode** faceNodes = aVolume.GetFaceNodes(iface);
8904 for (int inode = 0; inode < nbFaceNodes; inode++) {
8905 poly_nodes.push_back(faceNodes[inode]);
8907 if (nbInserted == 0) {
8908 if (faceNodes[inode] == theBetweenNode1) {
8909 if (faceNodes[inode + 1] == theBetweenNode2) {
8910 nbInserted = theNodesToInsert.size();
8912 // add nodes to insert
8913 list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.begin();
8914 for (; nIt != theNodesToInsert.end(); nIt++) {
8915 poly_nodes.push_back(*nIt);
8919 else if (faceNodes[inode] == theBetweenNode2) {
8920 if (faceNodes[inode + 1] == theBetweenNode1) {
8921 nbInserted = theNodesToInsert.size();
8923 // add nodes to insert in reversed order
8924 list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.end();
8926 for (; nIt != theNodesToInsert.begin(); nIt--) {
8927 poly_nodes.push_back(*nIt);
8929 poly_nodes.push_back(*nIt);
8936 quantities[iface] = nbFaceNodes + nbInserted;
8939 // Replace or update the volume
8940 SMESHDS_Mesh *aMesh = GetMeshDS();
8942 if (elem->IsPoly()) {
8943 aMesh->ChangePolyhedronNodes(elem, poly_nodes, quantities);
8947 int aShapeId = FindShape( elem );
8949 SMDS_MeshElement* newElem =
8950 aMesh->AddPolyhedralVolume(poly_nodes, quantities);
8951 myLastCreatedElems.Append(newElem);
8952 if (aShapeId && newElem)
8953 aMesh->SetMeshElementOnShape(newElem, aShapeId);
8955 aMesh->RemoveElement(elem);
8962 //================================================================================
8964 * \brief Transform any volume into data of SMDSEntity_Polyhedra
8966 //================================================================================
8968 void volumeToPolyhedron( const SMDS_MeshElement* elem,
8969 vector<const SMDS_MeshNode *> & nodes,
8970 vector<int> & nbNodeInFaces )
8973 nbNodeInFaces.clear();
8974 SMDS_VolumeTool vTool ( elem );
8975 for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
8977 const SMDS_MeshNode** fNodes = vTool.GetFaceNodes( iF );
8978 nodes.insert( nodes.end(), fNodes, fNodes + vTool.NbFaceNodes( iF ));
8979 nbNodeInFaces.push_back( vTool.NbFaceNodes( iF ));
8984 //=======================================================================
8986 * \brief Convert elements contained in a sub-mesh to quadratic
8987 * \return int - nb of checked elements
8989 //=======================================================================
8991 int SMESH_MeshEditor::convertElemToQuadratic(SMESHDS_SubMesh * theSm,
8992 SMESH_MesherHelper& theHelper,
8993 const bool theForce3d)
8996 if( !theSm ) return nbElem;
8998 vector<int> nbNodeInFaces;
8999 vector<const SMDS_MeshNode *> nodes;
9000 SMDS_ElemIteratorPtr ElemItr = theSm->GetElements();
9001 while(ElemItr->more())
9004 const SMDS_MeshElement* elem = ElemItr->next();
9005 if( !elem ) continue;
9007 // analyse a necessity of conversion
9008 const SMDSAbs_ElementType aType = elem->GetType();
9009 if ( aType < SMDSAbs_Edge || aType > SMDSAbs_Volume )
9011 const SMDSAbs_EntityType aGeomType = elem->GetEntityType();
9012 bool hasCentralNodes = false;
9013 if ( elem->IsQuadratic() )
9016 switch ( aGeomType ) {
9017 case SMDSEntity_Quad_Triangle:
9018 case SMDSEntity_Quad_Quadrangle:
9019 case SMDSEntity_Quad_Hexa:
9020 alreadyOK = !theHelper.GetIsBiQuadratic(); break;
9022 case SMDSEntity_BiQuad_Triangle:
9023 case SMDSEntity_BiQuad_Quadrangle:
9024 case SMDSEntity_TriQuad_Hexa:
9025 alreadyOK = theHelper.GetIsBiQuadratic();
9026 hasCentralNodes = true;
9031 // take into account already present modium nodes
9033 case SMDSAbs_Volume:
9034 theHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( elem )); break;
9036 theHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( elem )); break;
9038 theHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( elem )); break;
9044 // get elem data needed to re-create it
9046 const int id = elem->GetID();
9047 const int nbNodes = elem->NbCornerNodes();
9048 nodes.assign(elem->begin_nodes(), elem->end_nodes());
9049 if ( aGeomType == SMDSEntity_Polyhedra )
9050 nbNodeInFaces = static_cast<const SMDS_VtkVolume* >( elem )->GetQuantities();
9051 else if ( aGeomType == SMDSEntity_Hexagonal_Prism )
9052 volumeToPolyhedron( elem, nodes, nbNodeInFaces );
9054 // remove a linear element
9055 GetMeshDS()->RemoveFreeElement(elem, theSm, /*fromGroups=*/false);
9057 // remove central nodes of biquadratic elements (biquad->quad convertion)
9058 if ( hasCentralNodes )
9059 for ( size_t i = nbNodes * 2; i < nodes.size(); ++i )
9060 if ( nodes[i]->NbInverseElements() == 0 )
9061 GetMeshDS()->RemoveFreeNode( nodes[i], theSm, /*fromGroups=*/true );
9063 const SMDS_MeshElement* NewElem = 0;
9069 NewElem = theHelper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9077 NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
9080 NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9083 NewElem = theHelper.AddPolygonalFace(nodes, id, theForce3d);
9087 case SMDSAbs_Volume :
9091 case SMDSEntity_Tetra:
9092 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9094 case SMDSEntity_Pyramid:
9095 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], id, theForce3d);
9097 case SMDSEntity_Penta:
9098 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], nodes[5], id, theForce3d);
9100 case SMDSEntity_Hexa:
9101 case SMDSEntity_Quad_Hexa:
9102 case SMDSEntity_TriQuad_Hexa:
9103 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9104 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9106 case SMDSEntity_Hexagonal_Prism:
9108 NewElem = theHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9115 ReplaceElemInGroups( elem, NewElem, GetMeshDS());
9116 if( NewElem && NewElem->getshapeId() < 1 )
9117 theSm->AddElement( NewElem );
9121 //=======================================================================
9122 //function : ConvertToQuadratic
9124 //=======================================================================
9126 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d, const bool theToBiQuad)
9128 SMESHDS_Mesh* meshDS = GetMeshDS();
9130 SMESH_MesherHelper aHelper(*myMesh);
9132 aHelper.SetIsQuadratic( true );
9133 aHelper.SetIsBiQuadratic( theToBiQuad );
9134 aHelper.SetElementsOnShape(true);
9135 aHelper.ToFixNodeParameters( true );
9137 // convert elements assigned to sub-meshes
9138 int nbCheckedElems = 0;
9139 if ( myMesh->HasShapeToMesh() )
9141 if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9143 SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9144 while ( smIt->more() ) {
9145 SMESH_subMesh* sm = smIt->next();
9146 if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() ) {
9147 aHelper.SetSubShape( sm->GetSubShape() );
9148 nbCheckedElems += convertElemToQuadratic(smDS, aHelper, theForce3d);
9154 // convert elements NOT assigned to sub-meshes
9155 int totalNbElems = meshDS->NbEdges() + meshDS->NbFaces() + meshDS->NbVolumes();
9156 if ( nbCheckedElems < totalNbElems ) // not all elements are in sub-meshes
9158 aHelper.SetElementsOnShape(false);
9159 SMESHDS_SubMesh *smDS = 0;
9162 SMDS_EdgeIteratorPtr aEdgeItr = meshDS->edgesIterator();
9163 while( aEdgeItr->more() )
9165 const SMDS_MeshEdge* edge = aEdgeItr->next();
9166 if ( !edge->IsQuadratic() )
9168 int id = edge->GetID();
9169 const SMDS_MeshNode* n1 = edge->GetNode(0);
9170 const SMDS_MeshNode* n2 = edge->GetNode(1);
9172 meshDS->RemoveFreeElement(edge, smDS, /*fromGroups=*/false);
9174 const SMDS_MeshEdge* NewEdge = aHelper.AddEdge(n1, n2, id, theForce3d);
9175 ReplaceElemInGroups( edge, NewEdge, GetMeshDS());
9179 aHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( edge ));
9184 SMDS_FaceIteratorPtr aFaceItr = meshDS->facesIterator();
9185 while( aFaceItr->more() )
9187 const SMDS_MeshFace* face = aFaceItr->next();
9188 if ( !face ) continue;
9190 const SMDSAbs_EntityType type = face->GetEntityType();
9194 case SMDSEntity_Quad_Triangle:
9195 case SMDSEntity_Quad_Quadrangle:
9196 alreadyOK = !theToBiQuad;
9197 aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
9199 case SMDSEntity_BiQuad_Triangle:
9200 case SMDSEntity_BiQuad_Quadrangle:
9201 alreadyOK = theToBiQuad;
9202 aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
9204 default: alreadyOK = false;
9209 const int id = face->GetID();
9210 vector<const SMDS_MeshNode *> nodes ( face->begin_nodes(), face->end_nodes());
9212 meshDS->RemoveFreeElement(face, smDS, /*fromGroups=*/false);
9214 SMDS_MeshFace * NewFace = 0;
9217 case SMDSEntity_Triangle:
9218 case SMDSEntity_Quad_Triangle:
9219 case SMDSEntity_BiQuad_Triangle:
9220 NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
9221 if ( nodes.size() == 7 && nodes[6]->NbInverseElements() == 0 ) // rm a central node
9222 GetMeshDS()->RemoveFreeNode( nodes[6], /*sm=*/0, /*fromGroups=*/true );
9225 case SMDSEntity_Quadrangle:
9226 case SMDSEntity_Quad_Quadrangle:
9227 case SMDSEntity_BiQuad_Quadrangle:
9228 NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9229 if ( nodes.size() == 9 && nodes[8]->NbInverseElements() == 0 ) // rm a central node
9230 GetMeshDS()->RemoveFreeNode( nodes[8], /*sm=*/0, /*fromGroups=*/true );
9234 NewFace = aHelper.AddPolygonalFace(nodes, id, theForce3d);
9236 ReplaceElemInGroups( face, NewFace, GetMeshDS());
9240 vector<int> nbNodeInFaces;
9241 SMDS_VolumeIteratorPtr aVolumeItr = meshDS->volumesIterator();
9242 while(aVolumeItr->more())
9244 const SMDS_MeshVolume* volume = aVolumeItr->next();
9245 if ( !volume ) continue;
9247 const SMDSAbs_EntityType type = volume->GetEntityType();
9248 if ( volume->IsQuadratic() )
9253 case SMDSEntity_Quad_Hexa: alreadyOK = !theToBiQuad; break;
9254 case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; break;
9255 default: alreadyOK = true;
9259 aHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( volume ));
9263 const int id = volume->GetID();
9264 vector<const SMDS_MeshNode *> nodes (volume->begin_nodes(), volume->end_nodes());
9265 if ( type == SMDSEntity_Polyhedra )
9266 nbNodeInFaces = static_cast<const SMDS_VtkVolume* >(volume)->GetQuantities();
9267 else if ( type == SMDSEntity_Hexagonal_Prism )
9268 volumeToPolyhedron( volume, nodes, nbNodeInFaces );
9270 meshDS->RemoveFreeElement(volume, smDS, /*fromGroups=*/false);
9272 SMDS_MeshVolume * NewVolume = 0;
9275 case SMDSEntity_Tetra:
9276 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d );
9278 case SMDSEntity_Hexa:
9279 case SMDSEntity_Quad_Hexa:
9280 case SMDSEntity_TriQuad_Hexa:
9281 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9282 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9283 for ( size_t i = 20; i < nodes.size(); ++i ) // rm central nodes
9284 if ( nodes[i]->NbInverseElements() == 0 )
9285 GetMeshDS()->RemoveFreeNode( nodes[i], /*sm=*/0, /*fromGroups=*/true );
9287 case SMDSEntity_Pyramid:
9288 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9289 nodes[3], nodes[4], id, theForce3d);
9291 case SMDSEntity_Penta:
9292 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9293 nodes[3], nodes[4], nodes[5], id, theForce3d);
9295 case SMDSEntity_Hexagonal_Prism:
9297 NewVolume = aHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9299 ReplaceElemInGroups(volume, NewVolume, meshDS);
9304 { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9305 // aHelper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9306 // aHelper.FixQuadraticElements(myError);
9307 SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
9311 //================================================================================
9313 * \brief Makes given elements quadratic
9314 * \param theForce3d - if true, the medium nodes will be placed in the middle of link
9315 * \param theElements - elements to make quadratic
9317 //================================================================================
9319 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d,
9320 TIDSortedElemSet& theElements,
9321 const bool theToBiQuad)
9323 if ( theElements.empty() ) return;
9325 // we believe that all theElements are of the same type
9326 const SMDSAbs_ElementType elemType = (*theElements.begin())->GetType();
9328 // get all nodes shared by theElements
9329 TIDSortedNodeSet allNodes;
9330 TIDSortedElemSet::iterator eIt = theElements.begin();
9331 for ( ; eIt != theElements.end(); ++eIt )
9332 allNodes.insert( (*eIt)->begin_nodes(), (*eIt)->end_nodes() );
9334 // complete theElements with elements of lower dim whose all nodes are in allNodes
9336 TIDSortedElemSet quadAdjacentElems [ SMDSAbs_NbElementTypes ]; // quadratic adjacent elements
9337 TIDSortedElemSet checkedAdjacentElems [ SMDSAbs_NbElementTypes ];
9338 TIDSortedNodeSet::iterator nIt = allNodes.begin();
9339 for ( ; nIt != allNodes.end(); ++nIt )
9341 const SMDS_MeshNode* n = *nIt;
9342 SMDS_ElemIteratorPtr invIt = n->GetInverseElementIterator();
9343 while ( invIt->more() )
9345 const SMDS_MeshElement* e = invIt->next();
9346 const SMDSAbs_ElementType type = e->GetType();
9347 if ( e->IsQuadratic() )
9349 quadAdjacentElems[ type ].insert( e );
9352 switch ( e->GetEntityType() ) {
9353 case SMDSEntity_Quad_Triangle:
9354 case SMDSEntity_Quad_Quadrangle:
9355 case SMDSEntity_Quad_Hexa: alreadyOK = !theToBiQuad; break;
9356 case SMDSEntity_BiQuad_Triangle:
9357 case SMDSEntity_BiQuad_Quadrangle:
9358 case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; break;
9359 default: alreadyOK = true;
9364 if ( type >= elemType )
9365 continue; // same type or more complex linear element
9367 if ( !checkedAdjacentElems[ type ].insert( e ).second )
9368 continue; // e is already checked
9372 SMDS_NodeIteratorPtr nodeIt = e->nodeIterator();
9373 while ( nodeIt->more() && allIn )
9374 allIn = allNodes.count( nodeIt->next() );
9376 theElements.insert(e );
9380 SMESH_MesherHelper helper(*myMesh);
9381 helper.SetIsQuadratic( true );
9382 helper.SetIsBiQuadratic( theToBiQuad );
9384 // add links of quadratic adjacent elements to the helper
9386 if ( !quadAdjacentElems[SMDSAbs_Edge].empty() )
9387 for ( eIt = quadAdjacentElems[SMDSAbs_Edge].begin();
9388 eIt != quadAdjacentElems[SMDSAbs_Edge].end(); ++eIt )
9390 helper.AddTLinks( static_cast< const SMDS_MeshEdge*> (*eIt) );
9392 if ( !quadAdjacentElems[SMDSAbs_Face].empty() )
9393 for ( eIt = quadAdjacentElems[SMDSAbs_Face].begin();
9394 eIt != quadAdjacentElems[SMDSAbs_Face].end(); ++eIt )
9396 helper.AddTLinks( static_cast< const SMDS_MeshFace*> (*eIt) );
9398 if ( !quadAdjacentElems[SMDSAbs_Volume].empty() )
9399 for ( eIt = quadAdjacentElems[SMDSAbs_Volume].begin();
9400 eIt != quadAdjacentElems[SMDSAbs_Volume].end(); ++eIt )
9402 helper.AddTLinks( static_cast< const SMDS_MeshVolume*> (*eIt) );
9405 // make quadratic (or bi-tri-quadratic) elements instead of linear ones
9407 SMESHDS_Mesh* meshDS = GetMeshDS();
9408 SMESHDS_SubMesh* smDS = 0;
9409 for ( eIt = theElements.begin(); eIt != theElements.end(); ++eIt )
9411 const SMDS_MeshElement* elem = *eIt;
9414 int nbCentralNodes = 0;
9415 switch ( elem->GetEntityType() ) {
9416 // linear convertible
9417 case SMDSEntity_Edge:
9418 case SMDSEntity_Triangle:
9419 case SMDSEntity_Quadrangle:
9420 case SMDSEntity_Tetra:
9421 case SMDSEntity_Pyramid:
9422 case SMDSEntity_Hexa:
9423 case SMDSEntity_Penta: alreadyOK = false; nbCentralNodes = 0; break;
9424 // quadratic that can become bi-quadratic
9425 case SMDSEntity_Quad_Triangle:
9426 case SMDSEntity_Quad_Quadrangle:
9427 case SMDSEntity_Quad_Hexa: alreadyOK =!theToBiQuad; nbCentralNodes = 0; break;
9429 case SMDSEntity_BiQuad_Triangle:
9430 case SMDSEntity_BiQuad_Quadrangle: alreadyOK = theToBiQuad; nbCentralNodes = 1; break;
9431 case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; nbCentralNodes = 7; break;
9433 default: alreadyOK = true;
9435 if ( alreadyOK ) continue;
9437 const SMDSAbs_ElementType type = elem->GetType();
9438 const int id = elem->GetID();
9439 const int nbNodes = elem->NbCornerNodes();
9440 vector<const SMDS_MeshNode *> nodes ( elem->begin_nodes(), elem->end_nodes());
9442 helper.SetSubShape( elem->getshapeId() );
9444 if ( !smDS || !smDS->Contains( elem ))
9445 smDS = meshDS->MeshElements( elem->getshapeId() );
9446 meshDS->RemoveFreeElement(elem, smDS, /*fromGroups=*/false);
9448 SMDS_MeshElement * newElem = 0;
9451 case 4: // cases for most frequently used element types go first (for optimization)
9452 if ( type == SMDSAbs_Volume )
9453 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9455 newElem = helper.AddFace (nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9458 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9459 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9462 newElem = helper.AddFace (nodes[0], nodes[1], nodes[2], id, theForce3d);
9465 newElem = helper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9468 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9469 nodes[4], id, theForce3d);
9472 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9473 nodes[4], nodes[5], id, theForce3d);
9477 ReplaceElemInGroups( elem, newElem, meshDS);
9478 if( newElem && smDS )
9479 smDS->AddElement( newElem );
9481 // remove central nodes
9482 for ( size_t i = nodes.size() - nbCentralNodes; i < nodes.size(); ++i )
9483 if ( nodes[i]->NbInverseElements() == 0 )
9484 meshDS->RemoveFreeNode( nodes[i], smDS, /*fromGroups=*/true );
9486 } // loop on theElements
9489 { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9490 // helper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9491 // helper.FixQuadraticElements( myError );
9492 SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
9496 //=======================================================================
9498 * \brief Convert quadratic elements to linear ones and remove quadratic nodes
9499 * \return int - nb of checked elements
9501 //=======================================================================
9503 int SMESH_MeshEditor::removeQuadElem(SMESHDS_SubMesh * theSm,
9504 SMDS_ElemIteratorPtr theItr,
9505 const int theShapeID)
9508 SMESHDS_Mesh* meshDS = GetMeshDS();
9509 ElemFeatures elemType;
9510 vector<const SMDS_MeshNode *> nodes;
9512 while( theItr->more() )
9514 const SMDS_MeshElement* elem = theItr->next();
9516 if( elem && elem->IsQuadratic())
9519 int nbCornerNodes = elem->NbCornerNodes();
9520 nodes.assign( elem->begin_nodes(), elem->end_nodes() );
9522 elemType.Init( elem, /*basicOnly=*/false ).SetID( elem->GetID() ).SetQuad( false );
9524 //remove a quadratic element
9525 if ( !theSm || !theSm->Contains( elem ))
9526 theSm = meshDS->MeshElements( elem->getshapeId() );
9527 meshDS->RemoveFreeElement( elem, theSm, /*fromGroups=*/false );
9529 // remove medium nodes
9530 for ( size_t i = nbCornerNodes; i < nodes.size(); ++i )
9531 if ( nodes[i]->NbInverseElements() == 0 )
9532 meshDS->RemoveFreeNode( nodes[i], theSm );
9534 // add a linear element
9535 nodes.resize( nbCornerNodes );
9536 SMDS_MeshElement * newElem = AddElement( nodes, elemType );
9537 ReplaceElemInGroups(elem, newElem, meshDS);
9538 if( theSm && newElem )
9539 theSm->AddElement( newElem );
9545 //=======================================================================
9546 //function : ConvertFromQuadratic
9548 //=======================================================================
9550 bool SMESH_MeshEditor::ConvertFromQuadratic()
9552 int nbCheckedElems = 0;
9553 if ( myMesh->HasShapeToMesh() )
9555 if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9557 SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9558 while ( smIt->more() ) {
9559 SMESH_subMesh* sm = smIt->next();
9560 if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() )
9561 nbCheckedElems += removeQuadElem( smDS, smDS->GetElements(), sm->GetId() );
9567 GetMeshDS()->NbEdges() + GetMeshDS()->NbFaces() + GetMeshDS()->NbVolumes();
9568 if ( nbCheckedElems < totalNbElems ) // not all elements are in submeshes
9570 SMESHDS_SubMesh *aSM = 0;
9571 removeQuadElem( aSM, GetMeshDS()->elementsIterator(), 0 );
9579 //================================================================================
9581 * \brief Return true if all medium nodes of the element are in the node set
9583 //================================================================================
9585 bool allMediumNodesIn(const SMDS_MeshElement* elem, TIDSortedNodeSet& nodeSet )
9587 for ( int i = elem->NbCornerNodes(); i < elem->NbNodes(); ++i )
9588 if ( !nodeSet.count( elem->GetNode(i) ))
9594 //================================================================================
9596 * \brief Makes given elements linear
9598 //================================================================================
9600 void SMESH_MeshEditor::ConvertFromQuadratic(TIDSortedElemSet& theElements)
9602 if ( theElements.empty() ) return;
9604 // collect IDs of medium nodes of theElements; some of these nodes will be removed
9605 set<int> mediumNodeIDs;
9606 TIDSortedElemSet::iterator eIt = theElements.begin();
9607 for ( ; eIt != theElements.end(); ++eIt )
9609 const SMDS_MeshElement* e = *eIt;
9610 for ( int i = e->NbCornerNodes(); i < e->NbNodes(); ++i )
9611 mediumNodeIDs.insert( e->GetNode(i)->GetID() );
9614 // replace given elements by linear ones
9615 SMDS_ElemIteratorPtr elemIt = elemSetIterator( theElements );
9616 removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9618 // we need to convert remaining elements whose all medium nodes are in mediumNodeIDs
9619 // except those elements sharing medium nodes of quadratic element whose medium nodes
9620 // are not all in mediumNodeIDs
9622 // get remaining medium nodes
9623 TIDSortedNodeSet mediumNodes;
9624 set<int>::iterator nIdsIt = mediumNodeIDs.begin();
9625 for ( ; nIdsIt != mediumNodeIDs.end(); ++nIdsIt )
9626 if ( const SMDS_MeshNode* n = GetMeshDS()->FindNode( *nIdsIt ))
9627 mediumNodes.insert( mediumNodes.end(), n );
9629 // find more quadratic elements to convert
9630 TIDSortedElemSet moreElemsToConvert;
9631 TIDSortedNodeSet::iterator nIt = mediumNodes.begin();
9632 for ( ; nIt != mediumNodes.end(); ++nIt )
9634 SMDS_ElemIteratorPtr invIt = (*nIt)->GetInverseElementIterator();
9635 while ( invIt->more() )
9637 const SMDS_MeshElement* e = invIt->next();
9638 if ( e->IsQuadratic() && allMediumNodesIn( e, mediumNodes ))
9640 // find a more complex element including e and
9641 // whose medium nodes are not in mediumNodes
9642 bool complexFound = false;
9643 for ( int type = e->GetType() + 1; type < SMDSAbs_0DElement; ++type )
9645 SMDS_ElemIteratorPtr invIt2 =
9646 (*nIt)->GetInverseElementIterator( SMDSAbs_ElementType( type ));
9647 while ( invIt2->more() )
9649 const SMDS_MeshElement* eComplex = invIt2->next();
9650 if ( eComplex->IsQuadratic() && !allMediumNodesIn( eComplex, mediumNodes))
9652 int nbCommonNodes = SMESH_MeshAlgos::GetCommonNodes( e, eComplex ).size();
9653 if ( nbCommonNodes == e->NbNodes())
9655 complexFound = true;
9656 type = SMDSAbs_NbElementTypes; // to quit from the outer loop
9662 if ( !complexFound )
9663 moreElemsToConvert.insert( e );
9667 elemIt = elemSetIterator( moreElemsToConvert );
9668 removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9671 //=======================================================================
9672 //function : SewSideElements
9674 //=======================================================================
9676 SMESH_MeshEditor::Sew_Error
9677 SMESH_MeshEditor::SewSideElements (TIDSortedElemSet& theSide1,
9678 TIDSortedElemSet& theSide2,
9679 const SMDS_MeshNode* theFirstNode1,
9680 const SMDS_MeshNode* theFirstNode2,
9681 const SMDS_MeshNode* theSecondNode1,
9682 const SMDS_MeshNode* theSecondNode2)
9684 myLastCreatedElems.Clear();
9685 myLastCreatedNodes.Clear();
9687 MESSAGE ("::::SewSideElements()");
9688 if ( theSide1.size() != theSide2.size() )
9689 return SEW_DIFF_NB_OF_ELEMENTS;
9691 Sew_Error aResult = SEW_OK;
9693 // 1. Build set of faces representing each side
9694 // 2. Find which nodes of the side 1 to merge with ones on the side 2
9695 // 3. Replace nodes in elements of the side 1 and remove replaced nodes
9697 // =======================================================================
9698 // 1. Build set of faces representing each side:
9699 // =======================================================================
9700 // a. build set of nodes belonging to faces
9701 // b. complete set of faces: find missing faces whose nodes are in set of nodes
9702 // c. create temporary faces representing side of volumes if correspondent
9703 // face does not exist
9705 SMESHDS_Mesh* aMesh = GetMeshDS();
9706 // TODO algoritm not OK with vtkUnstructuredGrid: 2 meshes can't share nodes
9707 //SMDS_Mesh aTmpFacesMesh; // try to use the same mesh
9708 TIDSortedElemSet faceSet1, faceSet2;
9709 set<const SMDS_MeshElement*> volSet1, volSet2;
9710 set<const SMDS_MeshNode*> nodeSet1, nodeSet2;
9711 TIDSortedElemSet * faceSetPtr[] = { &faceSet1, &faceSet2 };
9712 set<const SMDS_MeshElement*> * volSetPtr[] = { &volSet1, &volSet2 };
9713 set<const SMDS_MeshNode*> * nodeSetPtr[] = { &nodeSet1, &nodeSet2 };
9714 TIDSortedElemSet * elemSetPtr[] = { &theSide1, &theSide2 };
9715 int iSide, iFace, iNode;
9717 list<const SMDS_MeshElement* > tempFaceList;
9718 for ( iSide = 0; iSide < 2; iSide++ ) {
9719 set<const SMDS_MeshNode*> * nodeSet = nodeSetPtr[ iSide ];
9720 TIDSortedElemSet * elemSet = elemSetPtr[ iSide ];
9721 TIDSortedElemSet * faceSet = faceSetPtr[ iSide ];
9722 set<const SMDS_MeshElement*> * volSet = volSetPtr [ iSide ];
9723 set<const SMDS_MeshElement*>::iterator vIt;
9724 TIDSortedElemSet::iterator eIt;
9725 set<const SMDS_MeshNode*>::iterator nIt;
9727 // check that given nodes belong to given elements
9728 const SMDS_MeshNode* n1 = ( iSide == 0 ) ? theFirstNode1 : theFirstNode2;
9729 const SMDS_MeshNode* n2 = ( iSide == 0 ) ? theSecondNode1 : theSecondNode2;
9730 int firstIndex = -1, secondIndex = -1;
9731 for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9732 const SMDS_MeshElement* elem = *eIt;
9733 if ( firstIndex < 0 ) firstIndex = elem->GetNodeIndex( n1 );
9734 if ( secondIndex < 0 ) secondIndex = elem->GetNodeIndex( n2 );
9735 if ( firstIndex > -1 && secondIndex > -1 ) break;
9737 if ( firstIndex < 0 || secondIndex < 0 ) {
9738 // we can simply return until temporary faces created
9739 return (iSide == 0 ) ? SEW_BAD_SIDE1_NODES : SEW_BAD_SIDE2_NODES;
9742 // -----------------------------------------------------------
9743 // 1a. Collect nodes of existing faces
9744 // and build set of face nodes in order to detect missing
9745 // faces corresponding to sides of volumes
9746 // -----------------------------------------------------------
9748 set< set <const SMDS_MeshNode*> > setOfFaceNodeSet;
9750 // loop on the given element of a side
9751 for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9752 //const SMDS_MeshElement* elem = *eIt;
9753 const SMDS_MeshElement* elem = *eIt;
9754 if ( elem->GetType() == SMDSAbs_Face ) {
9755 faceSet->insert( elem );
9756 set <const SMDS_MeshNode*> faceNodeSet;
9757 SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
9758 while ( nodeIt->more() ) {
9759 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9760 nodeSet->insert( n );
9761 faceNodeSet.insert( n );
9763 setOfFaceNodeSet.insert( faceNodeSet );
9765 else if ( elem->GetType() == SMDSAbs_Volume )
9766 volSet->insert( elem );
9768 // ------------------------------------------------------------------------------
9769 // 1b. Complete set of faces: find missing faces whose nodes are in set of nodes
9770 // ------------------------------------------------------------------------------
9772 for ( nIt = nodeSet->begin(); nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
9773 SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
9774 while ( fIt->more() ) { // loop on faces sharing a node
9775 const SMDS_MeshElement* f = fIt->next();
9776 if ( faceSet->find( f ) == faceSet->end() ) {
9777 // check if all nodes are in nodeSet and
9778 // complete setOfFaceNodeSet if they are
9779 set <const SMDS_MeshNode*> faceNodeSet;
9780 SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
9781 bool allInSet = true;
9782 while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
9783 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9784 if ( nodeSet->find( n ) == nodeSet->end() )
9787 faceNodeSet.insert( n );
9790 faceSet->insert( f );
9791 setOfFaceNodeSet.insert( faceNodeSet );
9797 // -------------------------------------------------------------------------
9798 // 1c. Create temporary faces representing sides of volumes if correspondent
9799 // face does not exist
9800 // -------------------------------------------------------------------------
9802 if ( !volSet->empty() ) {
9803 //int nodeSetSize = nodeSet->size();
9805 // loop on given volumes
9806 for ( vIt = volSet->begin(); vIt != volSet->end(); vIt++ ) {
9807 SMDS_VolumeTool vol (*vIt);
9808 // loop on volume faces: find free faces
9809 // --------------------------------------
9810 list<const SMDS_MeshElement* > freeFaceList;
9811 for ( iFace = 0; iFace < vol.NbFaces(); iFace++ ) {
9812 if ( !vol.IsFreeFace( iFace ))
9814 // check if there is already a face with same nodes in a face set
9815 const SMDS_MeshElement* aFreeFace = 0;
9816 const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iFace );
9817 int nbNodes = vol.NbFaceNodes( iFace );
9818 set <const SMDS_MeshNode*> faceNodeSet;
9819 vol.GetFaceNodes( iFace, faceNodeSet );
9820 bool isNewFace = setOfFaceNodeSet.insert( faceNodeSet ).second;
9822 // no such a face is given but it still can exist, check it
9823 vector<const SMDS_MeshNode *> nodes ( fNodes, fNodes + nbNodes);
9824 aFreeFace = aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false );
9827 // create a temporary face
9828 if ( nbNodes == 3 ) {
9829 //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2] );
9830 aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2] );
9832 else if ( nbNodes == 4 ) {
9833 //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
9834 aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
9837 vector<const SMDS_MeshNode *> poly_nodes ( fNodes, & fNodes[nbNodes]);
9838 //aFreeFace = aTmpFacesMesh.AddPolygonalFace(poly_nodes);
9839 aFreeFace = aMesh->AddPolygonalFace(poly_nodes);
9842 tempFaceList.push_back( aFreeFace );
9846 freeFaceList.push_back( aFreeFace );
9848 } // loop on faces of a volume
9850 // choose one of several free faces of a volume
9851 // --------------------------------------------
9852 if ( freeFaceList.size() > 1 ) {
9853 // choose a face having max nb of nodes shared by other elems of a side
9854 int maxNbNodes = -1;
9855 list<const SMDS_MeshElement* >::iterator fIt = freeFaceList.begin();
9856 while ( fIt != freeFaceList.end() ) { // loop on free faces
9857 int nbSharedNodes = 0;
9858 SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
9859 while ( nodeIt->more() ) { // loop on free face nodes
9860 const SMDS_MeshNode* n =
9861 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9862 SMDS_ElemIteratorPtr invElemIt = n->GetInverseElementIterator();
9863 while ( invElemIt->more() ) {
9864 const SMDS_MeshElement* e = invElemIt->next();
9865 nbSharedNodes += faceSet->count( e );
9866 nbSharedNodes += elemSet->count( e );
9869 if ( nbSharedNodes > maxNbNodes ) {
9870 maxNbNodes = nbSharedNodes;
9871 freeFaceList.erase( freeFaceList.begin(), fIt++ );
9873 else if ( nbSharedNodes == maxNbNodes ) {
9877 freeFaceList.erase( fIt++ ); // here fIt++ occurs before erase
9880 if ( freeFaceList.size() > 1 )
9882 // could not choose one face, use another way
9883 // choose a face most close to the bary center of the opposite side
9884 gp_XYZ aBC( 0., 0., 0. );
9885 set <const SMDS_MeshNode*> addedNodes;
9886 TIDSortedElemSet * elemSet2 = elemSetPtr[ 1 - iSide ];
9887 eIt = elemSet2->begin();
9888 for ( eIt = elemSet2->begin(); eIt != elemSet2->end(); eIt++ ) {
9889 SMDS_ElemIteratorPtr nodeIt = (*eIt)->nodesIterator();
9890 while ( nodeIt->more() ) { // loop on free face nodes
9891 const SMDS_MeshNode* n =
9892 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9893 if ( addedNodes.insert( n ).second )
9894 aBC += gp_XYZ( n->X(),n->Y(),n->Z() );
9897 aBC /= addedNodes.size();
9898 double minDist = DBL_MAX;
9899 fIt = freeFaceList.begin();
9900 while ( fIt != freeFaceList.end() ) { // loop on free faces
9902 SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
9903 while ( nodeIt->more() ) { // loop on free face nodes
9904 const SMDS_MeshNode* n =
9905 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9906 gp_XYZ p( n->X(),n->Y(),n->Z() );
9907 dist += ( aBC - p ).SquareModulus();
9909 if ( dist < minDist ) {
9911 freeFaceList.erase( freeFaceList.begin(), fIt++ );
9914 fIt = freeFaceList.erase( fIt++ );
9917 } // choose one of several free faces of a volume
9919 if ( freeFaceList.size() == 1 ) {
9920 const SMDS_MeshElement* aFreeFace = freeFaceList.front();
9921 faceSet->insert( aFreeFace );
9922 // complete a node set with nodes of a found free face
9923 // for ( iNode = 0; iNode < ; iNode++ )
9924 // nodeSet->insert( fNodes[ iNode ] );
9927 } // loop on volumes of a side
9929 // // complete a set of faces if new nodes in a nodeSet appeared
9930 // // ----------------------------------------------------------
9931 // if ( nodeSetSize != nodeSet->size() ) {
9932 // for ( ; nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
9933 // SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
9934 // while ( fIt->more() ) { // loop on faces sharing a node
9935 // const SMDS_MeshElement* f = fIt->next();
9936 // if ( faceSet->find( f ) == faceSet->end() ) {
9937 // // check if all nodes are in nodeSet and
9938 // // complete setOfFaceNodeSet if they are
9939 // set <const SMDS_MeshNode*> faceNodeSet;
9940 // SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
9941 // bool allInSet = true;
9942 // while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
9943 // const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9944 // if ( nodeSet->find( n ) == nodeSet->end() )
9945 // allInSet = false;
9947 // faceNodeSet.insert( n );
9949 // if ( allInSet ) {
9950 // faceSet->insert( f );
9951 // setOfFaceNodeSet.insert( faceNodeSet );
9957 } // Create temporary faces, if there are volumes given
9960 if ( faceSet1.size() != faceSet2.size() ) {
9961 // delete temporary faces: they are in reverseElements of actual nodes
9962 // SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
9963 // while ( tmpFaceIt->more() )
9964 // aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
9965 // list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
9966 // for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
9967 // aMesh->RemoveElement(*tmpFaceIt);
9968 MESSAGE("Diff nb of faces");
9969 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9972 // ============================================================
9973 // 2. Find nodes to merge:
9974 // bind a node to remove to a node to put instead
9975 // ============================================================
9977 TNodeNodeMap nReplaceMap; // bind a node to remove to a node to put instead
9978 if ( theFirstNode1 != theFirstNode2 )
9979 nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
9980 if ( theSecondNode1 != theSecondNode2 )
9981 nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
9983 LinkID_Gen aLinkID_Gen( GetMeshDS() );
9984 set< long > linkIdSet; // links to process
9985 linkIdSet.insert( aLinkID_Gen.GetLinkID( theFirstNode1, theSecondNode1 ));
9987 typedef pair< const SMDS_MeshNode*, const SMDS_MeshNode* > NLink;
9988 list< NLink > linkList[2];
9989 linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
9990 linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
9991 // loop on links in linkList; find faces by links and append links
9992 // of the found faces to linkList
9993 list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
9994 for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ )
9996 NLink link[] = { *linkIt[0], *linkIt[1] };
9997 long linkID = aLinkID_Gen.GetLinkID( link[0].first, link[0].second );
9998 if ( !linkIdSet.count( linkID ) )
10001 // by links, find faces in the face sets,
10002 // and find indices of link nodes in the found faces;
10003 // in a face set, there is only one or no face sharing a link
10004 // ---------------------------------------------------------------
10006 const SMDS_MeshElement* face[] = { 0, 0 };
10007 vector<const SMDS_MeshNode*> fnodes[2];
10008 int iLinkNode[2][2];
10009 TIDSortedElemSet avoidSet;
10010 for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
10011 const SMDS_MeshNode* n1 = link[iSide].first;
10012 const SMDS_MeshNode* n2 = link[iSide].second;
10013 //cout << "Side " << iSide << " ";
10014 //cout << "L( " << n1->GetID() << ", " << n2->GetID() << " ) " << endl;
10015 // find a face by two link nodes
10016 face[ iSide ] = SMESH_MeshAlgos::FindFaceInSet( n1, n2,
10017 *faceSetPtr[ iSide ], avoidSet,
10018 &iLinkNode[iSide][0],
10019 &iLinkNode[iSide][1] );
10020 if ( face[ iSide ])
10022 //cout << " F " << face[ iSide]->GetID() <<endl;
10023 faceSetPtr[ iSide ]->erase( face[ iSide ]);
10024 // put face nodes to fnodes
10025 if ( face[ iSide ]->IsQuadratic() )
10027 // use interlaced nodes iterator
10028 const SMDS_VtkFace* F = dynamic_cast<const SMDS_VtkFace*>( face[ iSide ]);
10029 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
10030 SMDS_ElemIteratorPtr nIter = F->interlacedNodesElemIterator();
10031 while ( nIter->more() )
10032 fnodes[ iSide ].push_back( cast2Node( nIter->next() ));
10036 fnodes[ iSide ].assign( face[ iSide ]->begin_nodes(),
10037 face[ iSide ]->end_nodes() );
10039 fnodes[ iSide ].push_back( fnodes[ iSide ].front());
10043 // check similarity of elements of the sides
10044 if (aResult == SEW_OK && (( face[0] && !face[1] ) || ( !face[0] && face[1] ))) {
10045 MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
10046 if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
10047 aResult = ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10050 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10052 break; // do not return because it's necessary to remove tmp faces
10055 // set nodes to merge
10056 // -------------------
10058 if ( face[0] && face[1] ) {
10059 const int nbNodes = face[0]->NbNodes();
10060 if ( nbNodes != face[1]->NbNodes() ) {
10061 MESSAGE("Diff nb of face nodes");
10062 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10063 break; // do not return because it s necessary to remove tmp faces
10065 bool reverse[] = { false, false }; // order of nodes in the link
10066 for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
10067 // analyse link orientation in faces
10068 int i1 = iLinkNode[ iSide ][ 0 ];
10069 int i2 = iLinkNode[ iSide ][ 1 ];
10070 reverse[ iSide ] = Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1;
10072 int di1 = reverse[0] ? -1 : +1, i1 = iLinkNode[0][1] + di1;
10073 int di2 = reverse[1] ? -1 : +1, i2 = iLinkNode[1][1] + di2;
10074 for ( int i = nbNodes - 2; i > 0; --i, i1 += di1, i2 += di2 )
10076 nReplaceMap.insert ( make_pair ( fnodes[0][ ( i1 + nbNodes ) % nbNodes ],
10077 fnodes[1][ ( i2 + nbNodes ) % nbNodes ]));
10080 // add other links of the faces to linkList
10081 // -----------------------------------------
10083 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
10084 linkID = aLinkID_Gen.GetLinkID( fnodes[0][iNode], fnodes[0][iNode+1] );
10085 pair< set<long>::iterator, bool > iter_isnew = linkIdSet.insert( linkID );
10086 if ( !iter_isnew.second ) { // already in a set: no need to process
10087 linkIdSet.erase( iter_isnew.first );
10089 else // new in set == encountered for the first time: add
10091 const SMDS_MeshNode* n1 = fnodes[0][ iNode ];
10092 const SMDS_MeshNode* n2 = fnodes[0][ iNode + 1];
10093 linkList[0].push_back ( NLink( n1, n2 ));
10094 linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10099 if ( faceSetPtr[0]->empty() || faceSetPtr[1]->empty() )
10102 } // loop on link lists
10104 if ( aResult == SEW_OK &&
10105 ( //linkIt[0] != linkList[0].end() ||
10106 !faceSetPtr[0]->empty() || !faceSetPtr[1]->empty() )) {
10107 MESSAGE( (linkIt[0] != linkList[0].end()) <<" "<< (faceSetPtr[0]->empty()) <<
10108 " " << (faceSetPtr[1]->empty()));
10109 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10112 // ====================================================================
10113 // 3. Replace nodes in elements of the side 1 and remove replaced nodes
10114 // ====================================================================
10116 // delete temporary faces
10117 // SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
10118 // while ( tmpFaceIt->more() )
10119 // aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
10120 list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
10121 for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
10122 aMesh->RemoveElement(*tmpFaceIt);
10124 if ( aResult != SEW_OK)
10127 list< int > nodeIDsToRemove;
10128 vector< const SMDS_MeshNode*> nodes;
10129 ElemFeatures elemType;
10131 // loop on nodes replacement map
10132 TNodeNodeMap::iterator nReplaceMapIt = nReplaceMap.begin(), nnIt;
10133 for ( ; nReplaceMapIt != nReplaceMap.end(); nReplaceMapIt++ )
10134 if ( (*nReplaceMapIt).first != (*nReplaceMapIt).second )
10136 const SMDS_MeshNode* nToRemove = (*nReplaceMapIt).first;
10137 nodeIDsToRemove.push_back( nToRemove->GetID() );
10138 // loop on elements sharing nToRemove
10139 SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
10140 while ( invElemIt->more() ) {
10141 const SMDS_MeshElement* e = invElemIt->next();
10142 // get a new suite of nodes: make replacement
10143 int nbReplaced = 0, i = 0, nbNodes = e->NbNodes();
10144 nodes.resize( nbNodes );
10145 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
10146 while ( nIt->more() ) {
10147 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nIt->next() );
10148 nnIt = nReplaceMap.find( n );
10149 if ( nnIt != nReplaceMap.end() ) {
10151 n = (*nnIt).second;
10155 // if ( nbReplaced == nbNodes && e->GetType() == SMDSAbs_Face )
10156 // elemIDsToRemove.push_back( e->GetID() );
10160 elemType.Init( e, /*basicOnly=*/false ).SetID( e->GetID() );
10161 aMesh->RemoveElement( e );
10163 if ( SMDS_MeshElement* newElem = this->AddElement( nodes, elemType ))
10165 AddToSameGroups( newElem, e, aMesh );
10166 if ( int aShapeId = e->getshapeId() )
10167 aMesh->SetMeshElementOnShape( newElem, aShapeId );
10173 Remove( nodeIDsToRemove, true );
10178 //================================================================================
10180 * \brief Find corresponding nodes in two sets of faces
10181 * \param theSide1 - first face set
10182 * \param theSide2 - second first face
10183 * \param theFirstNode1 - a boundary node of set 1
10184 * \param theFirstNode2 - a node of set 2 corresponding to theFirstNode1
10185 * \param theSecondNode1 - a boundary node of set 1 linked with theFirstNode1
10186 * \param theSecondNode2 - a node of set 2 corresponding to theSecondNode1
10187 * \param nReplaceMap - output map of corresponding nodes
10188 * \return bool - is a success or not
10190 //================================================================================
10193 //#define DEBUG_MATCHING_NODES
10196 SMESH_MeshEditor::Sew_Error
10197 SMESH_MeshEditor::FindMatchingNodes(set<const SMDS_MeshElement*>& theSide1,
10198 set<const SMDS_MeshElement*>& theSide2,
10199 const SMDS_MeshNode* theFirstNode1,
10200 const SMDS_MeshNode* theFirstNode2,
10201 const SMDS_MeshNode* theSecondNode1,
10202 const SMDS_MeshNode* theSecondNode2,
10203 TNodeNodeMap & nReplaceMap)
10205 set<const SMDS_MeshElement*> * faceSetPtr[] = { &theSide1, &theSide2 };
10207 nReplaceMap.clear();
10208 if ( theFirstNode1 != theFirstNode2 )
10209 nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
10210 if ( theSecondNode1 != theSecondNode2 )
10211 nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
10213 set< SMESH_TLink > linkSet; // set of nodes where order of nodes is ignored
10214 linkSet.insert( SMESH_TLink( theFirstNode1, theSecondNode1 ));
10216 list< NLink > linkList[2];
10217 linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
10218 linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
10220 // loop on links in linkList; find faces by links and append links
10221 // of the found faces to linkList
10222 list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
10223 for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ ) {
10224 NLink link[] = { *linkIt[0], *linkIt[1] };
10225 if ( linkSet.find( link[0] ) == linkSet.end() )
10228 // by links, find faces in the face sets,
10229 // and find indices of link nodes in the found faces;
10230 // in a face set, there is only one or no face sharing a link
10231 // ---------------------------------------------------------------
10233 const SMDS_MeshElement* face[] = { 0, 0 };
10234 list<const SMDS_MeshNode*> notLinkNodes[2];
10235 //bool reverse[] = { false, false }; // order of notLinkNodes
10237 for ( int iSide = 0; iSide < 2; iSide++ ) // loop on 2 sides
10239 const SMDS_MeshNode* n1 = link[iSide].first;
10240 const SMDS_MeshNode* n2 = link[iSide].second;
10241 set<const SMDS_MeshElement*> * faceSet = faceSetPtr[ iSide ];
10242 set< const SMDS_MeshElement* > facesOfNode1;
10243 for ( int iNode = 0; iNode < 2; iNode++ ) // loop on 2 nodes of a link
10245 // during a loop of the first node, we find all faces around n1,
10246 // during a loop of the second node, we find one face sharing both n1 and n2
10247 const SMDS_MeshNode* n = iNode ? n1 : n2; // a node of a link
10248 SMDS_ElemIteratorPtr fIt = n->GetInverseElementIterator(SMDSAbs_Face);
10249 while ( fIt->more() ) { // loop on faces sharing a node
10250 const SMDS_MeshElement* f = fIt->next();
10251 if (faceSet->find( f ) != faceSet->end() && // f is in face set
10252 ! facesOfNode1.insert( f ).second ) // f encounters twice
10254 if ( face[ iSide ] ) {
10255 MESSAGE( "2 faces per link " );
10256 return ( iSide ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10259 faceSet->erase( f );
10261 // get not link nodes
10262 int nbN = f->NbNodes();
10263 if ( f->IsQuadratic() )
10265 nbNodes[ iSide ] = nbN;
10266 list< const SMDS_MeshNode* > & nodes = notLinkNodes[ iSide ];
10267 int i1 = f->GetNodeIndex( n1 );
10268 int i2 = f->GetNodeIndex( n2 );
10269 int iEnd = nbN, iBeg = -1, iDelta = 1;
10270 bool reverse = ( Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1 );
10272 std::swap( iEnd, iBeg ); iDelta = -1;
10277 if ( i == iEnd ) i = iBeg + iDelta;
10278 if ( i == i1 ) break;
10279 nodes.push_back ( f->GetNode( i ) );
10285 // check similarity of elements of the sides
10286 if (( face[0] && !face[1] ) || ( !face[0] && face[1] )) {
10287 MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
10288 if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
10289 return ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10292 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10296 // set nodes to merge
10297 // -------------------
10299 if ( face[0] && face[1] ) {
10300 if ( nbNodes[0] != nbNodes[1] ) {
10301 MESSAGE("Diff nb of face nodes");
10302 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10304 #ifdef DEBUG_MATCHING_NODES
10305 MESSAGE ( " Link 1: " << link[0].first->GetID() <<" "<< link[0].second->GetID()
10306 << " F 1: " << face[0] << "| Link 2: " << link[1].first->GetID() <<" "
10307 << link[1].second->GetID() << " F 2: " << face[1] << " | Bind: " ) ;
10309 int nbN = nbNodes[0];
10311 list<const SMDS_MeshNode*>::iterator n1 = notLinkNodes[0].begin();
10312 list<const SMDS_MeshNode*>::iterator n2 = notLinkNodes[1].begin();
10313 for ( int i = 0 ; i < nbN - 2; ++i ) {
10314 #ifdef DEBUG_MATCHING_NODES
10315 MESSAGE ( (*n1)->GetID() << " to " << (*n2)->GetID() );
10317 nReplaceMap.insert( make_pair( *(n1++), *(n2++) ));
10321 // add other links of the face 1 to linkList
10322 // -----------------------------------------
10324 const SMDS_MeshElement* f0 = face[0];
10325 const SMDS_MeshNode* n1 = f0->GetNode( nbN - 1 );
10326 for ( int i = 0; i < nbN; i++ )
10328 const SMDS_MeshNode* n2 = f0->GetNode( i );
10329 pair< set< SMESH_TLink >::iterator, bool > iter_isnew =
10330 linkSet.insert( SMESH_TLink( n1, n2 ));
10331 if ( !iter_isnew.second ) { // already in a set: no need to process
10332 linkSet.erase( iter_isnew.first );
10334 else // new in set == encountered for the first time: add
10336 #ifdef DEBUG_MATCHING_NODES
10337 MESSAGE ( "Add link 1: " << n1->GetID() << " " << n2->GetID() << " "
10338 << " | link 2: " << nReplaceMap[n1]->GetID() << " " << nReplaceMap[n2]->GetID() << " " );
10340 linkList[0].push_back ( NLink( n1, n2 ));
10341 linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10346 } // loop on link lists
10351 //================================================================================
10353 * \brief Create elements equal (on same nodes) to given ones
10354 * \param [in] theElements - a set of elems to duplicate. If it is empty, all
10355 * elements of the uppest dimension are duplicated.
10357 //================================================================================
10359 void SMESH_MeshEditor::DoubleElements( const TIDSortedElemSet& theElements )
10361 ClearLastCreated();
10362 SMESHDS_Mesh* mesh = GetMeshDS();
10364 // get an element type and an iterator over elements
10366 SMDSAbs_ElementType type;
10367 SMDS_ElemIteratorPtr elemIt;
10368 vector< const SMDS_MeshElement* > allElems;
10369 if ( theElements.empty() )
10371 if ( mesh->NbNodes() == 0 )
10373 // get most complex type
10374 SMDSAbs_ElementType types[SMDSAbs_NbElementTypes] = {
10375 SMDSAbs_Volume, SMDSAbs_Face, SMDSAbs_Edge,
10376 SMDSAbs_0DElement, SMDSAbs_Ball, SMDSAbs_Node
10378 for ( int i = 0; i < SMDSAbs_NbElementTypes; ++i )
10379 if ( mesh->GetMeshInfo().NbElements( types[i] ))
10384 // put all elements in the vector <allElems>
10385 allElems.reserve( mesh->GetMeshInfo().NbElements( type ));
10386 elemIt = mesh->elementsIterator( type );
10387 while ( elemIt->more() )
10388 allElems.push_back( elemIt->next());
10389 elemIt = elemSetIterator( allElems );
10393 type = (*theElements.begin())->GetType();
10394 elemIt = elemSetIterator( theElements );
10397 // duplicate elements
10399 ElemFeatures elemType;
10401 vector< const SMDS_MeshNode* > nodes;
10402 while ( elemIt->more() )
10404 const SMDS_MeshElement* elem = elemIt->next();
10405 if ( elem->GetType() != type )
10408 elemType.Init( elem, /*basicOnly=*/false );
10409 nodes.assign( elem->begin_nodes(), elem->end_nodes() );
10411 AddElement( nodes, elemType );
10415 //================================================================================
10417 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10418 \param theElems - the list of elements (edges or faces) to be replicated
10419 The nodes for duplication could be found from these elements
10420 \param theNodesNot - list of nodes to NOT replicate
10421 \param theAffectedElems - the list of elements (cells and edges) to which the
10422 replicated nodes should be associated to.
10423 \return TRUE if operation has been completed successfully, FALSE otherwise
10425 //================================================================================
10427 bool SMESH_MeshEditor::DoubleNodes( const TIDSortedElemSet& theElems,
10428 const TIDSortedElemSet& theNodesNot,
10429 const TIDSortedElemSet& theAffectedElems )
10431 myLastCreatedElems.Clear();
10432 myLastCreatedNodes.Clear();
10434 if ( theElems.size() == 0 )
10437 SMESHDS_Mesh* aMeshDS = GetMeshDS();
10442 TNodeNodeMap anOldNodeToNewNode;
10443 // duplicate elements and nodes
10444 res = doubleNodes( aMeshDS, theElems, theNodesNot, anOldNodeToNewNode, true );
10445 // replce nodes by duplications
10446 res = doubleNodes( aMeshDS, theAffectedElems, theNodesNot, anOldNodeToNewNode, false );
10450 //================================================================================
10452 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10453 \param theMeshDS - mesh instance
10454 \param theElems - the elements replicated or modified (nodes should be changed)
10455 \param theNodesNot - nodes to NOT replicate
10456 \param theNodeNodeMap - relation of old node to new created node
10457 \param theIsDoubleElem - flag os to replicate element or modify
10458 \return TRUE if operation has been completed successfully, FALSE otherwise
10460 //================================================================================
10462 bool SMESH_MeshEditor::doubleNodes(SMESHDS_Mesh* theMeshDS,
10463 const TIDSortedElemSet& theElems,
10464 const TIDSortedElemSet& theNodesNot,
10465 TNodeNodeMap& theNodeNodeMap,
10466 const bool theIsDoubleElem )
10468 MESSAGE("doubleNodes");
10469 // iterate through element and duplicate them (by nodes duplication)
10471 std::vector<const SMDS_MeshNode*> newNodes;
10472 ElemFeatures elemType;
10474 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10475 for ( ; elemItr != theElems.end(); ++elemItr )
10477 const SMDS_MeshElement* anElem = *elemItr;
10481 // duplicate nodes to duplicate element
10482 bool isDuplicate = false;
10483 newNodes.resize( anElem->NbNodes() );
10484 SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
10486 while ( anIter->more() )
10488 const SMDS_MeshNode* aCurrNode = static_cast<const SMDS_MeshNode*>( anIter->next() );
10489 const SMDS_MeshNode* aNewNode = aCurrNode;
10490 TNodeNodeMap::iterator n2n = theNodeNodeMap.find( aCurrNode );
10491 if ( n2n != theNodeNodeMap.end() )
10493 aNewNode = n2n->second;
10495 else if ( theIsDoubleElem && !theNodesNot.count( aCurrNode ))
10498 aNewNode = theMeshDS->AddNode( aCurrNode->X(), aCurrNode->Y(), aCurrNode->Z() );
10499 copyPosition( aCurrNode, aNewNode );
10500 theNodeNodeMap[ aCurrNode ] = aNewNode;
10501 myLastCreatedNodes.Append( aNewNode );
10503 isDuplicate |= (aCurrNode != aNewNode);
10504 newNodes[ ind++ ] = aNewNode;
10506 if ( !isDuplicate )
10509 if ( theIsDoubleElem )
10510 AddElement( newNodes, elemType.Init( anElem, /*basicOnly=*/false ));
10512 theMeshDS->ChangeElementNodes( anElem, &newNodes[ 0 ], newNodes.size() );
10519 //================================================================================
10521 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10522 \param theNodes - identifiers of nodes to be doubled
10523 \param theModifiedElems - identifiers of elements to be updated by the new (doubled)
10524 nodes. If list of element identifiers is empty then nodes are doubled but
10525 they not assigned to elements
10526 \return TRUE if operation has been completed successfully, FALSE otherwise
10528 //================================================================================
10530 bool SMESH_MeshEditor::DoubleNodes( const std::list< int >& theListOfNodes,
10531 const std::list< int >& theListOfModifiedElems )
10533 MESSAGE("DoubleNodes");
10534 myLastCreatedElems.Clear();
10535 myLastCreatedNodes.Clear();
10537 if ( theListOfNodes.size() == 0 )
10540 SMESHDS_Mesh* aMeshDS = GetMeshDS();
10544 // iterate through nodes and duplicate them
10546 std::map< const SMDS_MeshNode*, const SMDS_MeshNode* > anOldNodeToNewNode;
10548 std::list< int >::const_iterator aNodeIter;
10549 for ( aNodeIter = theListOfNodes.begin(); aNodeIter != theListOfNodes.end(); ++aNodeIter )
10551 int aCurr = *aNodeIter;
10552 SMDS_MeshNode* aNode = (SMDS_MeshNode*)aMeshDS->FindNode( aCurr );
10558 const SMDS_MeshNode* aNewNode = aMeshDS->AddNode( aNode->X(), aNode->Y(), aNode->Z() );
10561 copyPosition( aNode, aNewNode );
10562 anOldNodeToNewNode[ aNode ] = aNewNode;
10563 myLastCreatedNodes.Append( aNewNode );
10567 // Create map of new nodes for modified elements
10569 std::map< SMDS_MeshElement*, vector<const SMDS_MeshNode*> > anElemToNodes;
10571 std::list< int >::const_iterator anElemIter;
10572 for ( anElemIter = theListOfModifiedElems.begin();
10573 anElemIter != theListOfModifiedElems.end(); ++anElemIter )
10575 int aCurr = *anElemIter;
10576 SMDS_MeshElement* anElem = (SMDS_MeshElement*)aMeshDS->FindElement( aCurr );
10580 vector<const SMDS_MeshNode*> aNodeArr( anElem->NbNodes() );
10582 SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
10584 while ( anIter->more() )
10586 SMDS_MeshNode* aCurrNode = (SMDS_MeshNode*)anIter->next();
10587 if ( aCurr && anOldNodeToNewNode.find( aCurrNode ) != anOldNodeToNewNode.end() )
10589 const SMDS_MeshNode* aNewNode = anOldNodeToNewNode[ aCurrNode ];
10590 aNodeArr[ ind++ ] = aNewNode;
10593 aNodeArr[ ind++ ] = aCurrNode;
10595 anElemToNodes[ anElem ] = aNodeArr;
10598 // Change nodes of elements
10600 std::map< SMDS_MeshElement*, vector<const SMDS_MeshNode*> >::iterator
10601 anElemToNodesIter = anElemToNodes.begin();
10602 for ( ; anElemToNodesIter != anElemToNodes.end(); ++anElemToNodesIter )
10604 const SMDS_MeshElement* anElem = anElemToNodesIter->first;
10605 vector<const SMDS_MeshNode*> aNodeArr = anElemToNodesIter->second;
10608 MESSAGE("ChangeElementNodes");
10609 aMeshDS->ChangeElementNodes( anElem, &aNodeArr[ 0 ], anElem->NbNodes() );
10618 //================================================================================
10620 \brief Check if element located inside shape
10621 \return TRUE if IN or ON shape, FALSE otherwise
10623 //================================================================================
10625 template<class Classifier>
10626 bool isInside(const SMDS_MeshElement* theElem,
10627 Classifier& theClassifier,
10628 const double theTol)
10630 gp_XYZ centerXYZ (0, 0, 0);
10631 SMDS_ElemIteratorPtr aNodeItr = theElem->nodesIterator();
10632 while (aNodeItr->more())
10633 centerXYZ += SMESH_TNodeXYZ(cast2Node( aNodeItr->next()));
10635 gp_Pnt aPnt = centerXYZ / theElem->NbNodes();
10636 theClassifier.Perform(aPnt, theTol);
10637 TopAbs_State aState = theClassifier.State();
10638 return (aState == TopAbs_IN || aState == TopAbs_ON );
10641 //================================================================================
10643 * \brief Classifier of the 3D point on the TopoDS_Face
10644 * with interaface suitable for isInside()
10646 //================================================================================
10648 struct _FaceClassifier
10650 Extrema_ExtPS _extremum;
10651 BRepAdaptor_Surface _surface;
10652 TopAbs_State _state;
10654 _FaceClassifier(const TopoDS_Face& face):_extremum(),_surface(face),_state(TopAbs_OUT)
10656 _extremum.Initialize( _surface,
10657 _surface.FirstUParameter(), _surface.LastUParameter(),
10658 _surface.FirstVParameter(), _surface.LastVParameter(),
10659 _surface.Tolerance(), _surface.Tolerance() );
10661 void Perform(const gp_Pnt& aPnt, double theTol)
10664 _state = TopAbs_OUT;
10665 _extremum.Perform(aPnt);
10666 if ( _extremum.IsDone() )
10667 for ( int iSol = 1; iSol <= _extremum.NbExt() && _state == TopAbs_OUT; ++iSol)
10668 _state = ( _extremum.SquareDistance(iSol) <= theTol ? TopAbs_IN : TopAbs_OUT );
10670 TopAbs_State State() const
10677 //================================================================================
10679 \brief Identify the elements that will be affected by node duplication (actual duplication is not performed).
10680 This method is the first step of DoubleNodeElemGroupsInRegion.
10681 \param theElems - list of groups of elements (edges or faces) to be replicated
10682 \param theNodesNot - list of groups of nodes not to replicated
10683 \param theShape - shape to detect affected elements (element which geometric center
10684 located on or inside shape). If the shape is null, detection is done on faces orientations
10685 (select elements with a gravity center on the side given by faces normals).
10686 This mode (null shape) is faster, but works only when theElems are faces, with coherents orientations.
10687 The replicated nodes should be associated to affected elements.
10688 \return groups of affected elements
10689 \sa DoubleNodeElemGroupsInRegion()
10691 //================================================================================
10693 bool SMESH_MeshEditor::AffectedElemGroupsInRegion( const TIDSortedElemSet& theElems,
10694 const TIDSortedElemSet& theNodesNot,
10695 const TopoDS_Shape& theShape,
10696 TIDSortedElemSet& theAffectedElems)
10698 if ( theShape.IsNull() )
10700 std::set<const SMDS_MeshNode*> alreadyCheckedNodes;
10701 std::set<const SMDS_MeshElement*> alreadyCheckedElems;
10702 std::set<const SMDS_MeshElement*> edgesToCheck;
10703 alreadyCheckedNodes.clear();
10704 alreadyCheckedElems.clear();
10705 edgesToCheck.clear();
10707 // --- iterates on elements to be replicated and get elements by back references from their nodes
10709 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10711 for ( ielem=1; elemItr != theElems.end(); ++elemItr )
10713 SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
10714 if (!anElem || (anElem->GetType() != SMDSAbs_Face))
10717 SMESH_MeshAlgos::FaceNormal( anElem, normal, /*normalized=*/true );
10718 MESSAGE("element " << ielem++ << " normal " << normal.X() << " " << normal.Y() << " " << normal.Z());
10719 std::set<const SMDS_MeshNode*> nodesElem;
10721 SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
10722 while ( nodeItr->more() )
10724 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10725 nodesElem.insert(aNode);
10727 std::set<const SMDS_MeshNode*>::iterator nodit = nodesElem.begin();
10728 for (; nodit != nodesElem.end(); nodit++)
10730 MESSAGE(" noeud ");
10731 const SMDS_MeshNode* aNode = *nodit;
10732 if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
10734 if (alreadyCheckedNodes.find(aNode) != alreadyCheckedNodes.end())
10736 alreadyCheckedNodes.insert(aNode);
10737 SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
10738 while ( backElemItr->more() )
10740 MESSAGE(" backelem ");
10741 const SMDS_MeshElement* curElem = backElemItr->next();
10742 if (alreadyCheckedElems.find(curElem) != alreadyCheckedElems.end())
10744 if (theElems.find(curElem) != theElems.end())
10746 alreadyCheckedElems.insert(curElem);
10747 double x=0, y=0, z=0;
10749 SMDS_ElemIteratorPtr nodeItr2 = curElem->nodesIterator();
10750 while ( nodeItr2->more() )
10752 const SMDS_MeshNode* anotherNode = cast2Node(nodeItr2->next());
10753 x += anotherNode->X();
10754 y += anotherNode->Y();
10755 z += anotherNode->Z();
10759 p.SetCoord( x/nb -aNode->X(),
10761 z/nb -aNode->Z() );
10762 MESSAGE(" check " << p.X() << " " << p.Y() << " " << p.Z());
10765 MESSAGE(" --- inserted")
10766 theAffectedElems.insert( curElem );
10768 else if (curElem->GetType() == SMDSAbs_Edge)
10769 edgesToCheck.insert(curElem);
10773 // --- add also edges lying on the set of faces (all nodes in alreadyCheckedNodes)
10774 std::set<const SMDS_MeshElement*>::iterator eit = edgesToCheck.begin();
10775 for( ; eit != edgesToCheck.end(); eit++)
10777 bool onside = true;
10778 const SMDS_MeshElement* anEdge = *eit;
10779 SMDS_ElemIteratorPtr nodeItr = anEdge->nodesIterator();
10780 while ( nodeItr->more() )
10782 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10783 if (alreadyCheckedNodes.find(aNode) == alreadyCheckedNodes.end())
10791 MESSAGE(" --- edge onside inserted")
10792 theAffectedElems.insert(anEdge);
10798 const double aTol = Precision::Confusion();
10799 auto_ptr< BRepClass3d_SolidClassifier> bsc3d;
10800 auto_ptr<_FaceClassifier> aFaceClassifier;
10801 if ( theShape.ShapeType() == TopAbs_SOLID )
10803 bsc3d.reset( new BRepClass3d_SolidClassifier(theShape));;
10804 bsc3d->PerformInfinitePoint(aTol);
10806 else if (theShape.ShapeType() == TopAbs_FACE )
10808 aFaceClassifier.reset( new _FaceClassifier(TopoDS::Face(theShape)));
10811 // iterates on indicated elements and get elements by back references from their nodes
10812 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10814 for ( ielem = 1; elemItr != theElems.end(); ++elemItr )
10816 MESSAGE("element " << ielem++);
10817 SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
10820 SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
10821 while ( nodeItr->more() )
10823 MESSAGE(" noeud ");
10824 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10825 if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
10827 SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
10828 while ( backElemItr->more() )
10830 MESSAGE(" backelem ");
10831 const SMDS_MeshElement* curElem = backElemItr->next();
10832 if ( curElem && theElems.find(curElem) == theElems.end() &&
10834 isInside( curElem, *bsc3d, aTol ) :
10835 isInside( curElem, *aFaceClassifier, aTol )))
10836 theAffectedElems.insert( curElem );
10844 //================================================================================
10846 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10847 \param theElems - group of of elements (edges or faces) to be replicated
10848 \param theNodesNot - group of nodes not to replicate
10849 \param theShape - shape to detect affected elements (element which geometric center
10850 located on or inside shape).
10851 The replicated nodes should be associated to affected elements.
10852 \return TRUE if operation has been completed successfully, FALSE otherwise
10854 //================================================================================
10856 bool SMESH_MeshEditor::DoubleNodesInRegion( const TIDSortedElemSet& theElems,
10857 const TIDSortedElemSet& theNodesNot,
10858 const TopoDS_Shape& theShape )
10860 if ( theShape.IsNull() )
10863 const double aTol = Precision::Confusion();
10864 auto_ptr< BRepClass3d_SolidClassifier> bsc3d;
10865 auto_ptr<_FaceClassifier> aFaceClassifier;
10866 if ( theShape.ShapeType() == TopAbs_SOLID )
10868 bsc3d.reset( new BRepClass3d_SolidClassifier(theShape));;
10869 bsc3d->PerformInfinitePoint(aTol);
10871 else if (theShape.ShapeType() == TopAbs_FACE )
10873 aFaceClassifier.reset( new _FaceClassifier(TopoDS::Face(theShape)));
10876 // iterates on indicated elements and get elements by back references from their nodes
10877 TIDSortedElemSet anAffected;
10878 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10879 for ( ; elemItr != theElems.end(); ++elemItr )
10881 SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
10885 SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
10886 while ( nodeItr->more() )
10888 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10889 if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
10891 SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
10892 while ( backElemItr->more() )
10894 const SMDS_MeshElement* curElem = backElemItr->next();
10895 if ( curElem && theElems.find(curElem) == theElems.end() &&
10897 isInside( curElem, *bsc3d, aTol ) :
10898 isInside( curElem, *aFaceClassifier, aTol )))
10899 anAffected.insert( curElem );
10903 return DoubleNodes( theElems, theNodesNot, anAffected );
10907 * \brief compute an oriented angle between two planes defined by four points.
10908 * The vector (p0,p1) defines the intersection of the 2 planes (p0,p1,g1) and (p0,p1,g2)
10909 * @param p0 base of the rotation axe
10910 * @param p1 extremity of the rotation axe
10911 * @param g1 belongs to the first plane
10912 * @param g2 belongs to the second plane
10914 double SMESH_MeshEditor::OrientedAngle(const gp_Pnt& p0, const gp_Pnt& p1, const gp_Pnt& g1, const gp_Pnt& g2)
10916 // MESSAGE(" p0: " << p0.X() << " " << p0.Y() << " " << p0.Z());
10917 // MESSAGE(" p1: " << p1.X() << " " << p1.Y() << " " << p1.Z());
10918 // MESSAGE(" g1: " << g1.X() << " " << g1.Y() << " " << g1.Z());
10919 // MESSAGE(" g2: " << g2.X() << " " << g2.Y() << " " << g2.Z());
10920 gp_Vec vref(p0, p1);
10923 gp_Vec n1 = vref.Crossed(v1);
10924 gp_Vec n2 = vref.Crossed(v2);
10926 return n2.AngleWithRef(n1, vref);
10928 catch ( Standard_Failure ) {
10930 return Max( v1.Magnitude(), v2.Magnitude() );
10934 * \brief Double nodes on shared faces between groups of volumes and create flat elements on demand.
10935 * The list of groups must contain at least two groups. The groups have to be disjoint: no common element into two different groups.
10936 * The nodes of the internal faces at the boundaries of the groups are doubled. Optionally, the internal faces are replaced by flat elements.
10937 * Triangles are transformed into prisms, and quadrangles into hexahedrons.
10938 * The flat elements are stored in groups of volumes. These groups are named according to the position of the group in the list:
10939 * the group j_n_p is the group of the flat elements that are built between the group #n and the group #p in the list.
10940 * If there is no shared faces between the group #n and the group #p in the list, the group j_n_p is not created.
10941 * All the flat elements are gathered into the group named "joints3D" (or "joints2D" in 2D situation).
10942 * The flat element of the multiple junctions between the simple junction are stored in a group named "jointsMultiples".
10943 * \param theElems - list of groups of volumes, where a group of volume is a set of
10944 * SMDS_MeshElements sorted by Id.
10945 * \param createJointElems - if TRUE, create the elements
10946 * \param onAllBoundaries - if TRUE, the nodes and elements are also created on
10947 * the boundary between \a theDomains and the rest mesh
10948 * \return TRUE if operation has been completed successfully, FALSE otherwise
10950 bool SMESH_MeshEditor::DoubleNodesOnGroupBoundaries( const std::vector<TIDSortedElemSet>& theElems,
10951 bool createJointElems,
10952 bool onAllBoundaries)
10954 MESSAGE("----------------------------------------------");
10955 MESSAGE("SMESH_MeshEditor::doubleNodesOnGroupBoundaries");
10956 MESSAGE("----------------------------------------------");
10958 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
10959 meshDS->BuildDownWardConnectivity(true);
10961 SMDS_UnstructuredGrid *grid = meshDS->getGrid();
10963 // --- build the list of faces shared by 2 domains (group of elements), with their domain and volume indexes
10964 // build the list of cells with only a node or an edge on the border, with their domain and volume indexes
10965 // build the list of nodes shared by 2 or more domains, with their domain indexes
10967 std::map<DownIdType, std::map<int,int>, DownIdCompare> faceDomains; // face --> (id domain --> id volume)
10968 std::map<int,int>celldom; // cell vtkId --> domain
10969 std::map<DownIdType, std::map<int,int>, DownIdCompare> cellDomains; // oldNode --> (id domain --> id cell)
10970 std::map<int, std::map<int,int> > nodeDomains; // oldId --> (domainId --> newId)
10971 faceDomains.clear();
10973 cellDomains.clear();
10974 nodeDomains.clear();
10975 std::map<int,int> emptyMap;
10976 std::set<int> emptySet;
10979 MESSAGE(".. Number of domains :"<<theElems.size());
10981 TIDSortedElemSet theRestDomElems;
10982 const int iRestDom = -1;
10983 const int idom0 = onAllBoundaries ? iRestDom : 0;
10984 const int nbDomains = theElems.size();
10986 // Check if the domains do not share an element
10987 for (int idom = 0; idom < nbDomains-1; idom++)
10989 // MESSAGE("... Check of domain #" << idom);
10990 const TIDSortedElemSet& domain = theElems[idom];
10991 TIDSortedElemSet::const_iterator elemItr = domain.begin();
10992 for (; elemItr != domain.end(); ++elemItr)
10994 const SMDS_MeshElement* anElem = *elemItr;
10995 int idombisdeb = idom + 1 ;
10996 for (int idombis = idombisdeb; idombis < theElems.size(); idombis++) // check if the element belongs to a domain further in the list
10998 const TIDSortedElemSet& domainbis = theElems[idombis];
10999 if ( domainbis.count(anElem) )
11001 MESSAGE(".... Domain #" << idom);
11002 MESSAGE(".... Domain #" << idombis);
11003 throw SALOME_Exception("The domains are not disjoint.");
11010 for (int idom = 0; idom < nbDomains; idom++)
11013 // --- build a map (face to duplicate --> volume to modify)
11014 // with all the faces shared by 2 domains (group of elements)
11015 // and corresponding volume of this domain, for each shared face.
11016 // a volume has a face shared by 2 domains if it has a neighbor which is not in his domain.
11018 MESSAGE("... Neighbors of domain #" << idom);
11019 const TIDSortedElemSet& domain = theElems[idom];
11020 TIDSortedElemSet::const_iterator elemItr = domain.begin();
11021 for (; elemItr != domain.end(); ++elemItr)
11023 const SMDS_MeshElement* anElem = *elemItr;
11026 int vtkId = anElem->getVtkId();
11027 //MESSAGE(" vtkId " << vtkId << " smdsId " << anElem->GetID());
11028 int neighborsVtkIds[NBMAXNEIGHBORS];
11029 int downIds[NBMAXNEIGHBORS];
11030 unsigned char downTypes[NBMAXNEIGHBORS];
11031 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
11032 for (int n = 0; n < nbNeighbors; n++)
11034 int smdsId = meshDS->fromVtkToSmds(neighborsVtkIds[n]);
11035 const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
11036 if (elem && ! domain.count(elem)) // neighbor is in another domain : face is shared
11039 for (int idombis = 0; idombis < theElems.size() && !ok; idombis++) // check if the neighbor belongs to another domain of the list
11041 // MESSAGE("Domain " << idombis);
11042 const TIDSortedElemSet& domainbis = theElems[idombis];
11043 if ( domainbis.count(elem)) ok = true ; // neighbor is in a correct domain : face is kept
11045 if ( ok || onAllBoundaries ) // the characteristics of the face is stored
11047 DownIdType face(downIds[n], downTypes[n]);
11048 if (!faceDomains[face].count(idom))
11050 faceDomains[face][idom] = vtkId; // volume associated to face in this domain
11051 celldom[vtkId] = idom;
11052 //MESSAGE(" cell with a border " << vtkId << " domain " << idom);
11056 theRestDomElems.insert( elem );
11057 faceDomains[face][iRestDom] = neighborsVtkIds[n];
11058 celldom[neighborsVtkIds[n]] = iRestDom;
11066 //MESSAGE("Number of shared faces " << faceDomains.size());
11067 std::map<DownIdType, std::map<int, int>, DownIdCompare>::iterator itface;
11069 // --- explore the shared faces domain by domain,
11070 // explore the nodes of the face and see if they belong to a cell in the domain,
11071 // which has only a node or an edge on the border (not a shared face)
11073 for (int idomain = idom0; idomain < nbDomains; idomain++)
11075 //MESSAGE("Domain " << idomain);
11076 const TIDSortedElemSet& domain = (idomain == iRestDom) ? theRestDomElems : theElems[idomain];
11077 itface = faceDomains.begin();
11078 for (; itface != faceDomains.end(); ++itface)
11080 const std::map<int, int>& domvol = itface->second;
11081 if (!domvol.count(idomain))
11083 DownIdType face = itface->first;
11084 //MESSAGE(" --- face " << face.cellId);
11085 std::set<int> oldNodes;
11087 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11088 std::set<int>::iterator itn = oldNodes.begin();
11089 for (; itn != oldNodes.end(); ++itn)
11092 //MESSAGE(" node " << oldId);
11093 vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
11094 for (int i=0; i<l.ncells; i++)
11096 int vtkId = l.cells[i];
11097 const SMDS_MeshElement* anElem = GetMeshDS()->FindElement(GetMeshDS()->fromVtkToSmds(vtkId));
11098 if (!domain.count(anElem))
11100 int vtkType = grid->GetCellType(vtkId);
11101 int downId = grid->CellIdToDownId(vtkId);
11104 MESSAGE("doubleNodesOnGroupBoundaries: internal algorithm problem");
11105 continue; // not OK at this stage of the algorithm:
11106 //no cells created after BuildDownWardConnectivity
11108 DownIdType aCell(downId, vtkType);
11109 cellDomains[aCell][idomain] = vtkId;
11110 celldom[vtkId] = idomain;
11111 //MESSAGE(" cell " << vtkId << " domain " << idomain);
11117 // --- explore the shared faces domain by domain, to duplicate the nodes in a coherent way
11118 // for each shared face, get the nodes
11119 // for each node, for each domain of the face, create a clone of the node
11121 // --- edges at the intersection of 3 or 4 domains, with the order of domains to build
11122 // junction elements of type prism or hexa. the key is the pair of nodesId (lower first)
11123 // the value is the ordered domain ids. (more than 4 domains not taken into account)
11125 std::map<std::vector<int>, std::vector<int> > edgesMultiDomains; // nodes of edge --> ordered domains
11126 std::map<int, std::vector<int> > mutipleNodes; // nodes multi domains with domain order
11127 std::map<int, std::vector<int> > mutipleNodesToFace; // nodes multi domains with domain order to transform in Face (junction between 3 or more 2D domains)
11129 MESSAGE(".. Duplication of the nodes");
11130 for (int idomain = idom0; idomain < nbDomains; idomain++)
11132 itface = faceDomains.begin();
11133 for (; itface != faceDomains.end(); ++itface)
11135 const std::map<int, int>& domvol = itface->second;
11136 if (!domvol.count(idomain))
11138 DownIdType face = itface->first;
11139 //MESSAGE(" --- face " << face.cellId);
11140 std::set<int> oldNodes;
11142 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11143 std::set<int>::iterator itn = oldNodes.begin();
11144 for (; itn != oldNodes.end(); ++itn)
11147 if (nodeDomains[oldId].empty())
11149 nodeDomains[oldId][idomain] = oldId; // keep the old node in the first domain
11150 //MESSAGE("-+-+-b oldNode " << oldId << " domain " << idomain);
11152 std::map<int, int>::const_iterator itdom = domvol.begin();
11153 for (; itdom != domvol.end(); ++itdom)
11155 int idom = itdom->first;
11156 //MESSAGE(" domain " << idom);
11157 if (!nodeDomains[oldId].count(idom)) // --- node to clone
11159 if (nodeDomains[oldId].size() >= 2) // a multiple node
11161 vector<int> orderedDoms;
11162 //MESSAGE("multiple node " << oldId);
11163 if (mutipleNodes.count(oldId))
11164 orderedDoms = mutipleNodes[oldId];
11167 map<int,int>::iterator it = nodeDomains[oldId].begin();
11168 for (; it != nodeDomains[oldId].end(); ++it)
11169 orderedDoms.push_back(it->first);
11171 orderedDoms.push_back(idom); // TODO order ==> push_front or back
11172 //stringstream txt;
11173 //for (int i=0; i<orderedDoms.size(); i++)
11174 // txt << orderedDoms[i] << " ";
11175 //MESSAGE("orderedDoms " << txt.str());
11176 mutipleNodes[oldId] = orderedDoms;
11178 double *coords = grid->GetPoint(oldId);
11179 SMDS_MeshNode *newNode = meshDS->AddNode(coords[0], coords[1], coords[2]);
11180 copyPosition( meshDS->FindNodeVtk( oldId ), newNode );
11181 int newId = newNode->getVtkId();
11182 nodeDomains[oldId][idom] = newId; // cloned node for other domains
11183 //MESSAGE("-+-+-c oldNode " << oldId << " domain " << idomain << " newNode " << newId << " domain " << idom << " size=" <<nodeDomains[oldId].size());
11190 MESSAGE(".. Creation of elements");
11191 for (int idomain = idom0; idomain < nbDomains; idomain++)
11193 itface = faceDomains.begin();
11194 for (; itface != faceDomains.end(); ++itface)
11196 std::map<int, int> domvol = itface->second;
11197 if (!domvol.count(idomain))
11199 DownIdType face = itface->first;
11200 //MESSAGE(" --- face " << face.cellId);
11201 std::set<int> oldNodes;
11203 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11204 int nbMultipleNodes = 0;
11205 std::set<int>::iterator itn = oldNodes.begin();
11206 for (; itn != oldNodes.end(); ++itn)
11209 if (mutipleNodes.count(oldId))
11212 if (nbMultipleNodes > 1) // check if an edge of the face is shared between 3 or more domains
11214 //MESSAGE("multiple Nodes detected on a shared face");
11215 int downId = itface->first.cellId;
11216 unsigned char cellType = itface->first.cellType;
11217 // --- shared edge or shared face ?
11218 if ((cellType == VTK_LINE) || (cellType == VTK_QUADRATIC_EDGE)) // shared edge (between two faces)
11221 int nbNodes = grid->getDownArray(cellType)->getNodes(downId, nodes);
11222 for (int i=0; i< nbNodes; i=i+nbNodes-1) // i=0 , i=nbNodes-1
11223 if (mutipleNodes.count(nodes[i]))
11224 if (!mutipleNodesToFace.count(nodes[i]))
11225 mutipleNodesToFace[nodes[i]] = mutipleNodes[nodes[i]];
11227 else // shared face (between two volumes)
11229 int nbEdges = grid->getDownArray(cellType)->getNumberOfDownCells(downId);
11230 const int* downEdgeIds = grid->getDownArray(cellType)->getDownCells(downId);
11231 const unsigned char* edgeType = grid->getDownArray(cellType)->getDownTypes(downId);
11232 for (int ie =0; ie < nbEdges; ie++)
11235 int nbNodes = grid->getDownArray(edgeType[ie])->getNodes(downEdgeIds[ie], nodes);
11236 if (mutipleNodes.count(nodes[0]) && mutipleNodes.count(nodes[nbNodes-1]))
11238 vector<int> vn0 = mutipleNodes[nodes[0]];
11239 vector<int> vn1 = mutipleNodes[nodes[nbNodes - 1]];
11241 for (int i0 = 0; i0 < vn0.size(); i0++)
11242 for (int i1 = 0; i1 < vn1.size(); i1++)
11243 if (vn0[i0] == vn1[i1])
11244 doms.push_back(vn0[i0]);
11245 if (doms.size() >2)
11247 //MESSAGE(" detect edgesMultiDomains " << nodes[0] << " " << nodes[nbNodes - 1]);
11248 double *coords = grid->GetPoint(nodes[0]);
11249 gp_Pnt p0(coords[0], coords[1], coords[2]);
11250 coords = grid->GetPoint(nodes[nbNodes - 1]);
11251 gp_Pnt p1(coords[0], coords[1], coords[2]);
11253 int vtkVolIds[1000]; // an edge can belong to a lot of volumes
11254 map<int, SMDS_VtkVolume*> domvol; // domain --> a volume with the edge
11255 map<int, double> angleDom; // oriented angles between planes defined by edge and volume centers
11256 int nbvol = grid->GetParentVolumes(vtkVolIds, downEdgeIds[ie], edgeType[ie]);
11257 for (int id=0; id < doms.size(); id++)
11259 int idom = doms[id];
11260 const TIDSortedElemSet& domain = (idom == iRestDom) ? theRestDomElems : theElems[idom];
11261 for (int ivol=0; ivol<nbvol; ivol++)
11263 int smdsId = meshDS->fromVtkToSmds(vtkVolIds[ivol]);
11264 SMDS_MeshElement* elem = (SMDS_MeshElement*)meshDS->FindElement(smdsId);
11265 if (domain.count(elem))
11267 SMDS_VtkVolume* svol = dynamic_cast<SMDS_VtkVolume*>(elem);
11268 domvol[idom] = svol;
11269 //MESSAGE(" domain " << idom << " volume " << elem->GetID());
11271 vtkIdType npts = 0;
11272 vtkIdType* pts = 0;
11273 grid->GetCellPoints(vtkVolIds[ivol], npts, pts);
11274 SMDS_VtkVolume::gravityCenter(grid, pts, npts, values);
11277 gref.SetXYZ(gp_XYZ(values[0], values[1], values[2]));
11278 angleDom[idom] = 0;
11282 gp_Pnt g(values[0], values[1], values[2]);
11283 angleDom[idom] = OrientedAngle(p0, p1, gref, g); // -pi<angle<+pi
11284 //MESSAGE(" angle=" << angleDom[idom]);
11290 map<double, int> sortedDom; // sort domains by angle
11291 for (map<int, double>::iterator ia = angleDom.begin(); ia != angleDom.end(); ++ia)
11292 sortedDom[ia->second] = ia->first;
11293 vector<int> vnodes;
11295 for (map<double, int>::iterator ib = sortedDom.begin(); ib != sortedDom.end(); ++ib)
11297 vdom.push_back(ib->second);
11298 //MESSAGE(" ordered domain " << ib->second << " angle " << ib->first);
11300 for (int ino = 0; ino < nbNodes; ino++)
11301 vnodes.push_back(nodes[ino]);
11302 edgesMultiDomains[vnodes] = vdom; // nodes vector --> ordered domains
11311 // --- iterate on shared faces (volumes to modify, face to extrude)
11312 // get node id's of the face (id SMDS = id VTK)
11313 // create flat element with old and new nodes if requested
11315 // --- new quad nodes on flat quad elements: oldId --> ((domain1 X domain2) --> newId)
11316 // (domain1 X domain2) = domain1 + MAXINT*domain2
11318 std::map<int, std::map<long,int> > nodeQuadDomains;
11319 std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11321 MESSAGE(".. Creation of elements: simple junction");
11322 if (createJointElems)
11325 string joints2DName = "joints2D";
11326 mapOfJunctionGroups[joints2DName] = this->myMesh->AddGroup(SMDSAbs_Face, joints2DName.c_str(), idg);
11327 SMESHDS_Group *joints2DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints2DName]->GetGroupDS());
11328 string joints3DName = "joints3D";
11329 mapOfJunctionGroups[joints3DName] = this->myMesh->AddGroup(SMDSAbs_Volume, joints3DName.c_str(), idg);
11330 SMESHDS_Group *joints3DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints3DName]->GetGroupDS());
11332 itface = faceDomains.begin();
11333 for (; itface != faceDomains.end(); ++itface)
11335 DownIdType face = itface->first;
11336 std::set<int> oldNodes;
11337 std::set<int>::iterator itn;
11339 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11341 std::map<int, int> domvol = itface->second;
11342 std::map<int, int>::iterator itdom = domvol.begin();
11343 int dom1 = itdom->first;
11344 int vtkVolId = itdom->second;
11346 int dom2 = itdom->first;
11347 SMDS_MeshCell *vol = grid->extrudeVolumeFromFace(vtkVolId, dom1, dom2, oldNodes, nodeDomains,
11349 stringstream grpname;
11352 grpname << dom1 << "_" << dom2;
11354 grpname << dom2 << "_" << dom1;
11355 string namegrp = grpname.str();
11356 if (!mapOfJunctionGroups.count(namegrp))
11357 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(vol->GetType(), namegrp.c_str(), idg);
11358 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11360 sgrp->Add(vol->GetID());
11361 if (vol->GetType() == SMDSAbs_Volume)
11362 joints3DGrp->Add(vol->GetID());
11363 else if (vol->GetType() == SMDSAbs_Face)
11364 joints2DGrp->Add(vol->GetID());
11368 // --- create volumes on multiple domain intersection if requested
11369 // iterate on mutipleNodesToFace
11370 // iterate on edgesMultiDomains
11372 MESSAGE(".. Creation of elements: multiple junction");
11373 if (createJointElems)
11375 // --- iterate on mutipleNodesToFace
11377 std::map<int, std::vector<int> >::iterator itn = mutipleNodesToFace.begin();
11378 for (; itn != mutipleNodesToFace.end(); ++itn)
11380 int node = itn->first;
11381 vector<int> orderDom = itn->second;
11382 vector<vtkIdType> orderedNodes;
11383 for (int idom = 0; idom <orderDom.size(); idom++)
11384 orderedNodes.push_back( nodeDomains[node][orderDom[idom]] );
11385 SMDS_MeshFace* face = this->GetMeshDS()->AddFaceFromVtkIds(orderedNodes);
11387 stringstream grpname;
11389 grpname << 0 << "_" << 0;
11391 string namegrp = grpname.str();
11392 if (!mapOfJunctionGroups.count(namegrp))
11393 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Face, namegrp.c_str(), idg);
11394 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11396 sgrp->Add(face->GetID());
11399 // --- iterate on edgesMultiDomains
11401 std::map<std::vector<int>, std::vector<int> >::iterator ite = edgesMultiDomains.begin();
11402 for (; ite != edgesMultiDomains.end(); ++ite)
11404 vector<int> nodes = ite->first;
11405 vector<int> orderDom = ite->second;
11406 vector<vtkIdType> orderedNodes;
11407 if (nodes.size() == 2)
11409 //MESSAGE(" use edgesMultiDomains " << nodes[0] << " " << nodes[1]);
11410 for (int ino=0; ino < nodes.size(); ino++)
11411 if (orderDom.size() == 3)
11412 for (int idom = 0; idom <orderDom.size(); idom++)
11413 orderedNodes.push_back( nodeDomains[nodes[ino]][orderDom[idom]] );
11415 for (int idom = orderDom.size()-1; idom >=0; idom--)
11416 orderedNodes.push_back( nodeDomains[nodes[ino]][orderDom[idom]] );
11417 SMDS_MeshVolume* vol = this->GetMeshDS()->AddVolumeFromVtkIds(orderedNodes);
11420 string namegrp = "jointsMultiples";
11421 if (!mapOfJunctionGroups.count(namegrp))
11422 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
11423 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11425 sgrp->Add(vol->GetID());
11429 INFOS("Quadratic multiple joints not implemented");
11430 // TODO quadratic nodes
11435 // --- list the explicit faces and edges of the mesh that need to be modified,
11436 // i.e. faces and edges built with one or more duplicated nodes.
11437 // associate these faces or edges to their corresponding domain.
11438 // only the first domain found is kept when a face or edge is shared
11440 std::map<DownIdType, std::map<int,int>, DownIdCompare> faceOrEdgeDom; // cellToModify --> (id domain --> id cell)
11441 std::map<int,int> feDom; // vtk id of cell to modify --> id domain
11442 faceOrEdgeDom.clear();
11445 MESSAGE(".. Modification of elements");
11446 for (int idomain = idom0; idomain < nbDomains; idomain++)
11448 std::map<int, std::map<int, int> >::const_iterator itnod = nodeDomains.begin();
11449 for (; itnod != nodeDomains.end(); ++itnod)
11451 int oldId = itnod->first;
11452 //MESSAGE(" node " << oldId);
11453 vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
11454 for (int i = 0; i < l.ncells; i++)
11456 int vtkId = l.cells[i];
11457 int vtkType = grid->GetCellType(vtkId);
11458 int downId = grid->CellIdToDownId(vtkId);
11460 continue; // new cells: not to be modified
11461 DownIdType aCell(downId, vtkType);
11462 int volParents[1000];
11463 int nbvol = grid->GetParentVolumes(volParents, vtkId);
11464 for (int j = 0; j < nbvol; j++)
11465 if (celldom.count(volParents[j]) && (celldom[volParents[j]] == idomain))
11466 if (!feDom.count(vtkId))
11468 feDom[vtkId] = idomain;
11469 faceOrEdgeDom[aCell] = emptyMap;
11470 faceOrEdgeDom[aCell][idomain] = vtkId; // affect face or edge to the first domain only
11471 //MESSAGE("affect cell " << this->GetMeshDS()->fromVtkToSmds(vtkId) << " domain " << idomain
11472 // << " type " << vtkType << " downId " << downId);
11478 // --- iterate on shared faces (volumes to modify, face to extrude)
11479 // get node id's of the face
11480 // replace old nodes by new nodes in volumes, and update inverse connectivity
11482 std::map<DownIdType, std::map<int,int>, DownIdCompare>* maps[3] = {&faceDomains, &cellDomains, &faceOrEdgeDom};
11483 for (int m=0; m<3; m++)
11485 std::map<DownIdType, std::map<int,int>, DownIdCompare>* amap = maps[m];
11486 itface = (*amap).begin();
11487 for (; itface != (*amap).end(); ++itface)
11489 DownIdType face = itface->first;
11490 std::set<int> oldNodes;
11491 std::set<int>::iterator itn;
11493 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11494 //MESSAGE("examine cell, downId " << face.cellId << " type " << int(face.cellType));
11495 std::map<int, int> localClonedNodeIds;
11497 std::map<int, int> domvol = itface->second;
11498 std::map<int, int>::iterator itdom = domvol.begin();
11499 for (; itdom != domvol.end(); ++itdom)
11501 int idom = itdom->first;
11502 int vtkVolId = itdom->second;
11503 //MESSAGE("modify nodes of cell " << this->GetMeshDS()->fromVtkToSmds(vtkVolId) << " domain " << idom);
11504 localClonedNodeIds.clear();
11505 for (itn = oldNodes.begin(); itn != oldNodes.end(); ++itn)
11508 if (nodeDomains[oldId].count(idom))
11510 localClonedNodeIds[oldId] = nodeDomains[oldId][idom];
11511 //MESSAGE(" node " << oldId << " --> " << localClonedNodeIds[oldId]);
11514 meshDS->ModifyCellNodes(vtkVolId, localClonedNodeIds);
11519 // Remove empty groups (issue 0022812)
11520 std::map<std::string, SMESH_Group*>::iterator name_group = mapOfJunctionGroups.begin();
11521 for ( ; name_group != mapOfJunctionGroups.end(); ++name_group )
11523 if ( name_group->second && name_group->second->GetGroupDS()->IsEmpty() )
11524 myMesh->RemoveGroup( name_group->second->GetGroupDS()->GetID() );
11527 meshDS->CleanDownWardConnectivity(); // Mesh has been modified, downward connectivity is no more usable, free memory
11528 grid->BuildLinks();
11536 * \brief Double nodes on some external faces and create flat elements.
11537 * Flat elements are mainly used by some types of mechanic calculations.
11539 * Each group of the list must be constituted of faces.
11540 * Triangles are transformed in prisms, and quadrangles in hexahedrons.
11541 * @param theElems - list of groups of faces, where a group of faces is a set of
11542 * SMDS_MeshElements sorted by Id.
11543 * @return TRUE if operation has been completed successfully, FALSE otherwise
11545 bool SMESH_MeshEditor::CreateFlatElementsOnFacesGroups(const std::vector<TIDSortedElemSet>& theElems)
11547 MESSAGE("-------------------------------------------------");
11548 MESSAGE("SMESH_MeshEditor::CreateFlatElementsOnFacesGroups");
11549 MESSAGE("-------------------------------------------------");
11551 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11553 // --- For each group of faces
11554 // duplicate the nodes, create a flat element based on the face
11555 // replace the nodes of the faces by their clones
11557 std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> clonedNodes;
11558 std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> intermediateNodes;
11559 clonedNodes.clear();
11560 intermediateNodes.clear();
11561 std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11562 mapOfJunctionGroups.clear();
11564 for (int idom = 0; idom < theElems.size(); idom++)
11566 const TIDSortedElemSet& domain = theElems[idom];
11567 TIDSortedElemSet::const_iterator elemItr = domain.begin();
11568 for (; elemItr != domain.end(); ++elemItr)
11570 SMDS_MeshElement* anElem = (SMDS_MeshElement*) *elemItr;
11571 SMDS_MeshFace* aFace = dynamic_cast<SMDS_MeshFace*> (anElem);
11574 // MESSAGE("aFace=" << aFace->GetID());
11575 bool isQuad = aFace->IsQuadratic();
11576 vector<const SMDS_MeshNode*> ln0, ln1, ln2, ln3, ln4;
11578 // --- clone the nodes, create intermediate nodes for non medium nodes of a quad face
11580 SMDS_ElemIteratorPtr nodeIt = aFace->nodesIterator();
11581 while (nodeIt->more())
11583 const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*> (nodeIt->next());
11584 bool isMedium = isQuad && (aFace->IsMediumNode(node));
11586 ln2.push_back(node);
11588 ln0.push_back(node);
11590 const SMDS_MeshNode* clone = 0;
11591 if (!clonedNodes.count(node))
11593 clone = meshDS->AddNode(node->X(), node->Y(), node->Z());
11594 copyPosition( node, clone );
11595 clonedNodes[node] = clone;
11598 clone = clonedNodes[node];
11601 ln3.push_back(clone);
11603 ln1.push_back(clone);
11605 const SMDS_MeshNode* inter = 0;
11606 if (isQuad && (!isMedium))
11608 if (!intermediateNodes.count(node))
11610 inter = meshDS->AddNode(node->X(), node->Y(), node->Z());
11611 copyPosition( node, inter );
11612 intermediateNodes[node] = inter;
11615 inter = intermediateNodes[node];
11616 ln4.push_back(inter);
11620 // --- extrude the face
11622 vector<const SMDS_MeshNode*> ln;
11623 SMDS_MeshVolume* vol = 0;
11624 vtkIdType aType = aFace->GetVtkType();
11628 vol = meshDS->AddVolume(ln0[2], ln0[1], ln0[0], ln1[2], ln1[1], ln1[0]);
11629 // MESSAGE("vol prism " << vol->GetID());
11630 ln.push_back(ln1[0]);
11631 ln.push_back(ln1[1]);
11632 ln.push_back(ln1[2]);
11635 vol = meshDS->AddVolume(ln0[3], ln0[2], ln0[1], ln0[0], ln1[3], ln1[2], ln1[1], ln1[0]);
11636 // MESSAGE("vol hexa " << vol->GetID());
11637 ln.push_back(ln1[0]);
11638 ln.push_back(ln1[1]);
11639 ln.push_back(ln1[2]);
11640 ln.push_back(ln1[3]);
11642 case VTK_QUADRATIC_TRIANGLE:
11643 vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln0[0], ln0[1], ln0[2], ln3[0], ln3[1], ln3[2],
11644 ln2[0], ln2[1], ln2[2], ln4[0], ln4[1], ln4[2]);
11645 // MESSAGE("vol quad prism " << vol->GetID());
11646 ln.push_back(ln1[0]);
11647 ln.push_back(ln1[1]);
11648 ln.push_back(ln1[2]);
11649 ln.push_back(ln3[0]);
11650 ln.push_back(ln3[1]);
11651 ln.push_back(ln3[2]);
11653 case VTK_QUADRATIC_QUAD:
11654 // vol = meshDS->AddVolume(ln0[0], ln0[1], ln0[2], ln0[3], ln1[0], ln1[1], ln1[2], ln1[3],
11655 // ln2[0], ln2[1], ln2[2], ln2[3], ln3[0], ln3[1], ln3[2], ln3[3],
11656 // ln4[0], ln4[1], ln4[2], ln4[3]);
11657 vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln1[3], ln0[0], ln0[1], ln0[2], ln0[3],
11658 ln3[0], ln3[1], ln3[2], ln3[3], ln2[0], ln2[1], ln2[2], ln2[3],
11659 ln4[0], ln4[1], ln4[2], ln4[3]);
11660 // MESSAGE("vol quad hexa " << vol->GetID());
11661 ln.push_back(ln1[0]);
11662 ln.push_back(ln1[1]);
11663 ln.push_back(ln1[2]);
11664 ln.push_back(ln1[3]);
11665 ln.push_back(ln3[0]);
11666 ln.push_back(ln3[1]);
11667 ln.push_back(ln3[2]);
11668 ln.push_back(ln3[3]);
11678 stringstream grpname;
11682 string namegrp = grpname.str();
11683 if (!mapOfJunctionGroups.count(namegrp))
11684 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
11685 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11687 sgrp->Add(vol->GetID());
11690 // --- modify the face
11692 aFace->ChangeNodes(&ln[0], ln.size());
11699 * \brief identify all the elements around a geom shape, get the faces delimiting the hole
11700 * Build groups of volume to remove, groups of faces to replace on the skin of the object,
11701 * groups of faces to remove inside the object, (idem edges).
11702 * Build ordered list of nodes at the border of each group of faces to replace (to be used to build a geom subshape)
11704 void SMESH_MeshEditor::CreateHoleSkin(double radius,
11705 const TopoDS_Shape& theShape,
11706 SMESH_NodeSearcher* theNodeSearcher,
11707 const char* groupName,
11708 std::vector<double>& nodesCoords,
11709 std::vector<std::vector<int> >& listOfListOfNodes)
11711 MESSAGE("--------------------------------");
11712 MESSAGE("SMESH_MeshEditor::CreateHoleSkin");
11713 MESSAGE("--------------------------------");
11715 // --- zone of volumes to remove is given :
11716 // 1 either by a geom shape (one or more vertices) and a radius,
11717 // 2 either by a group of nodes (representative of the shape)to use with the radius,
11718 // 3 either by a group of nodes where all the elements build on one of this nodes are to remove,
11719 // In the case 2, the group of nodes is an external group of nodes from another mesh,
11720 // In the case 3, the group of nodes is an internal group of the mesh (obtained for instance by a filter),
11721 // defined by it's name.
11723 SMESHDS_GroupBase* groupDS = 0;
11724 SMESH_Mesh::GroupIteratorPtr groupIt = this->myMesh->GetGroups();
11725 while ( groupIt->more() )
11728 SMESH_Group * group = groupIt->next();
11729 if ( !group ) continue;
11730 groupDS = group->GetGroupDS();
11731 if ( !groupDS || groupDS->IsEmpty() ) continue;
11732 std::string grpName = group->GetName();
11733 //MESSAGE("grpName=" << grpName);
11734 if (grpName == groupName)
11740 bool isNodeGroup = false;
11741 bool isNodeCoords = false;
11744 if (groupDS->GetType() != SMDSAbs_Node)
11746 isNodeGroup = true; // a group of nodes exists and it is in this mesh
11749 if (nodesCoords.size() > 0)
11750 isNodeCoords = true; // a list o nodes given by their coordinates
11751 //MESSAGE("---" << isNodeGroup << " " << isNodeCoords);
11753 // --- define groups to build
11755 int idg; // --- group of SMDS volumes
11756 string grpvName = groupName;
11757 grpvName += "_vol";
11758 SMESH_Group *grp = this->myMesh->AddGroup(SMDSAbs_Volume, grpvName.c_str(), idg);
11761 MESSAGE("group not created " << grpvName);
11764 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(grp->GetGroupDS());
11766 int idgs; // --- group of SMDS faces on the skin
11767 string grpsName = groupName;
11768 grpsName += "_skin";
11769 SMESH_Group *grps = this->myMesh->AddGroup(SMDSAbs_Face, grpsName.c_str(), idgs);
11772 MESSAGE("group not created " << grpsName);
11775 SMESHDS_Group *sgrps = dynamic_cast<SMESHDS_Group*>(grps->GetGroupDS());
11777 int idgi; // --- group of SMDS faces internal (several shapes)
11778 string grpiName = groupName;
11779 grpiName += "_internalFaces";
11780 SMESH_Group *grpi = this->myMesh->AddGroup(SMDSAbs_Face, grpiName.c_str(), idgi);
11783 MESSAGE("group not created " << grpiName);
11786 SMESHDS_Group *sgrpi = dynamic_cast<SMESHDS_Group*>(grpi->GetGroupDS());
11788 int idgei; // --- group of SMDS faces internal (several shapes)
11789 string grpeiName = groupName;
11790 grpeiName += "_internalEdges";
11791 SMESH_Group *grpei = this->myMesh->AddGroup(SMDSAbs_Edge, grpeiName.c_str(), idgei);
11794 MESSAGE("group not created " << grpeiName);
11797 SMESHDS_Group *sgrpei = dynamic_cast<SMESHDS_Group*>(grpei->GetGroupDS());
11799 // --- build downward connectivity
11801 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11802 meshDS->BuildDownWardConnectivity(true);
11803 SMDS_UnstructuredGrid* grid = meshDS->getGrid();
11805 // --- set of volumes detected inside
11807 std::set<int> setOfInsideVol;
11808 std::set<int> setOfVolToCheck;
11810 std::vector<gp_Pnt> gpnts;
11813 if (isNodeGroup) // --- a group of nodes is provided : find all the volumes using one or more of this nodes
11815 MESSAGE("group of nodes provided");
11816 SMDS_ElemIteratorPtr elemIt = groupDS->GetElements();
11817 while ( elemIt->more() )
11819 const SMDS_MeshElement* elem = elemIt->next();
11822 const SMDS_MeshNode* node = dynamic_cast<const SMDS_MeshNode*>(elem);
11825 SMDS_MeshElement* vol = 0;
11826 SMDS_ElemIteratorPtr volItr = node->GetInverseElementIterator(SMDSAbs_Volume);
11827 while (volItr->more())
11829 vol = (SMDS_MeshElement*)volItr->next();
11830 setOfInsideVol.insert(vol->getVtkId());
11831 sgrp->Add(vol->GetID());
11835 else if (isNodeCoords)
11837 MESSAGE("list of nodes coordinates provided");
11840 while (i < nodesCoords.size()-2)
11842 double x = nodesCoords[i++];
11843 double y = nodesCoords[i++];
11844 double z = nodesCoords[i++];
11845 gp_Pnt p = gp_Pnt(x, y ,z);
11846 gpnts.push_back(p);
11847 MESSAGE("TopoDS_Vertex " << k << " " << p.X() << " " << p.Y() << " " << p.Z());
11851 else // --- no group, no coordinates : use the vertices of the geom shape provided, and radius
11853 MESSAGE("no group of nodes provided, using vertices from geom shape, and radius");
11854 TopTools_IndexedMapOfShape vertexMap;
11855 TopExp::MapShapes( theShape, TopAbs_VERTEX, vertexMap );
11856 gp_Pnt p = gp_Pnt(0,0,0);
11857 if (vertexMap.Extent() < 1)
11860 for ( int i = 1; i <= vertexMap.Extent(); ++i )
11862 const TopoDS_Vertex& vertex = TopoDS::Vertex( vertexMap( i ));
11863 p = BRep_Tool::Pnt(vertex);
11864 gpnts.push_back(p);
11865 MESSAGE("TopoDS_Vertex " << i << " " << p.X() << " " << p.Y() << " " << p.Z());
11869 if (gpnts.size() > 0)
11872 const SMDS_MeshNode* startNode = theNodeSearcher->FindClosestTo(gpnts[0]);
11874 nodeId = startNode->GetID();
11875 MESSAGE("nodeId " << nodeId);
11877 double radius2 = radius*radius;
11878 MESSAGE("radius2 " << radius2);
11880 // --- volumes on start node
11882 setOfVolToCheck.clear();
11883 SMDS_MeshElement* startVol = 0;
11884 SMDS_ElemIteratorPtr volItr = startNode->GetInverseElementIterator(SMDSAbs_Volume);
11885 while (volItr->more())
11887 startVol = (SMDS_MeshElement*)volItr->next();
11888 setOfVolToCheck.insert(startVol->getVtkId());
11890 if (setOfVolToCheck.empty())
11892 MESSAGE("No volumes found");
11896 // --- starting with central volumes then their neighbors, check if they are inside
11897 // or outside the domain, until no more new neighbor volume is inside.
11898 // Fill the group of inside volumes
11900 std::map<int, double> mapOfNodeDistance2;
11901 mapOfNodeDistance2.clear();
11902 std::set<int> setOfOutsideVol;
11903 while (!setOfVolToCheck.empty())
11905 std::set<int>::iterator it = setOfVolToCheck.begin();
11907 MESSAGE("volume to check, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
11908 bool volInside = false;
11909 vtkIdType npts = 0;
11910 vtkIdType* pts = 0;
11911 grid->GetCellPoints(vtkId, npts, pts);
11912 for (int i=0; i<npts; i++)
11914 double distance2 = 0;
11915 if (mapOfNodeDistance2.count(pts[i]))
11917 distance2 = mapOfNodeDistance2[pts[i]];
11918 MESSAGE("point " << pts[i] << " distance2 " << distance2);
11922 double *coords = grid->GetPoint(pts[i]);
11923 gp_Pnt aPoint = gp_Pnt(coords[0], coords[1], coords[2]);
11925 for (int j=0; j<gpnts.size(); j++)
11927 double d2 = aPoint.SquareDistance(gpnts[j]);
11928 if (d2 < distance2)
11931 if (distance2 < radius2)
11935 mapOfNodeDistance2[pts[i]] = distance2;
11936 MESSAGE(" point " << pts[i] << " distance2 " << distance2 << " coords " << coords[0] << " " << coords[1] << " " << coords[2]);
11938 if (distance2 < radius2)
11940 volInside = true; // one or more nodes inside the domain
11941 sgrp->Add(meshDS->fromVtkToSmds(vtkId));
11947 setOfInsideVol.insert(vtkId);
11948 MESSAGE(" volume inside, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
11949 int neighborsVtkIds[NBMAXNEIGHBORS];
11950 int downIds[NBMAXNEIGHBORS];
11951 unsigned char downTypes[NBMAXNEIGHBORS];
11952 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
11953 for (int n = 0; n < nbNeighbors; n++)
11954 if (!setOfInsideVol.count(neighborsVtkIds[n]) ||setOfOutsideVol.count(neighborsVtkIds[n]))
11955 setOfVolToCheck.insert(neighborsVtkIds[n]);
11959 setOfOutsideVol.insert(vtkId);
11960 MESSAGE(" volume outside, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
11962 setOfVolToCheck.erase(vtkId);
11966 // --- for outside hexahedrons, check if they have more than one neighbor volume inside
11967 // If yes, add the volume to the inside set
11969 bool addedInside = true;
11970 std::set<int> setOfVolToReCheck;
11971 while (addedInside)
11973 MESSAGE(" --------------------------- re check");
11974 addedInside = false;
11975 std::set<int>::iterator itv = setOfInsideVol.begin();
11976 for (; itv != setOfInsideVol.end(); ++itv)
11979 int neighborsVtkIds[NBMAXNEIGHBORS];
11980 int downIds[NBMAXNEIGHBORS];
11981 unsigned char downTypes[NBMAXNEIGHBORS];
11982 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
11983 for (int n = 0; n < nbNeighbors; n++)
11984 if (!setOfInsideVol.count(neighborsVtkIds[n]))
11985 setOfVolToReCheck.insert(neighborsVtkIds[n]);
11987 setOfVolToCheck = setOfVolToReCheck;
11988 setOfVolToReCheck.clear();
11989 while (!setOfVolToCheck.empty())
11991 std::set<int>::iterator it = setOfVolToCheck.begin();
11993 if (grid->GetCellType(vtkId) == VTK_HEXAHEDRON)
11995 MESSAGE("volume to recheck, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
11996 int countInside = 0;
11997 int neighborsVtkIds[NBMAXNEIGHBORS];
11998 int downIds[NBMAXNEIGHBORS];
11999 unsigned char downTypes[NBMAXNEIGHBORS];
12000 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12001 for (int n = 0; n < nbNeighbors; n++)
12002 if (setOfInsideVol.count(neighborsVtkIds[n]))
12004 MESSAGE("countInside " << countInside);
12005 if (countInside > 1)
12007 MESSAGE(" volume inside, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12008 setOfInsideVol.insert(vtkId);
12009 sgrp->Add(meshDS->fromVtkToSmds(vtkId));
12010 addedInside = true;
12013 setOfVolToReCheck.insert(vtkId);
12015 setOfVolToCheck.erase(vtkId);
12019 // --- map of Downward faces at the boundary, inside the global volume
12020 // map of Downward faces on the skin of the global volume (equivalent to SMDS faces on the skin)
12021 // fill group of SMDS faces inside the volume (when several volume shapes)
12022 // fill group of SMDS faces on the skin of the global volume (if skin)
12024 std::map<DownIdType, int, DownIdCompare> boundaryFaces; // boundary faces inside the volume --> corresponding cell
12025 std::map<DownIdType, int, DownIdCompare> skinFaces; // faces on the skin of the global volume --> corresponding cell
12026 std::set<int>::iterator it = setOfInsideVol.begin();
12027 for (; it != setOfInsideVol.end(); ++it)
12030 //MESSAGE(" vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12031 int neighborsVtkIds[NBMAXNEIGHBORS];
12032 int downIds[NBMAXNEIGHBORS];
12033 unsigned char downTypes[NBMAXNEIGHBORS];
12034 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId, true);
12035 for (int n = 0; n < nbNeighbors; n++)
12037 int neighborDim = SMDS_Downward::getCellDimension(grid->GetCellType(neighborsVtkIds[n]));
12038 if (neighborDim == 3)
12040 if (! setOfInsideVol.count(neighborsVtkIds[n])) // neighbor volume is not inside : face is boundary
12042 DownIdType face(downIds[n], downTypes[n]);
12043 boundaryFaces[face] = vtkId;
12045 // if the face between to volumes is in the mesh, get it (internal face between shapes)
12046 int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12047 if (vtkFaceId >= 0)
12049 sgrpi->Add(meshDS->fromVtkToSmds(vtkFaceId));
12050 // find also the smds edges on this face
12051 int nbEdges = grid->getDownArray(downTypes[n])->getNumberOfDownCells(downIds[n]);
12052 const int* dEdges = grid->getDownArray(downTypes[n])->getDownCells(downIds[n]);
12053 const unsigned char* dTypes = grid->getDownArray(downTypes[n])->getDownTypes(downIds[n]);
12054 for (int i = 0; i < nbEdges; i++)
12056 int vtkEdgeId = grid->getDownArray(dTypes[i])->getVtkCellId(dEdges[i]);
12057 if (vtkEdgeId >= 0)
12058 sgrpei->Add(meshDS->fromVtkToSmds(vtkEdgeId));
12062 else if (neighborDim == 2) // skin of the volume
12064 DownIdType face(downIds[n], downTypes[n]);
12065 skinFaces[face] = vtkId;
12066 int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12067 if (vtkFaceId >= 0)
12068 sgrps->Add(meshDS->fromVtkToSmds(vtkFaceId));
12073 // --- identify the edges constituting the wire of each subshape on the skin
12074 // define polylines with the nodes of edges, equivalent to wires
12075 // project polylines on subshapes, and partition, to get geom faces
12077 std::map<int, std::set<int> > shapeIdToVtkIdSet; // shapeId --> set of vtkId on skin
12078 std::set<int> emptySet;
12080 std::set<int> shapeIds;
12082 SMDS_ElemIteratorPtr itelem = sgrps->GetElements();
12083 while (itelem->more())
12085 const SMDS_MeshElement *elem = itelem->next();
12086 int shapeId = elem->getshapeId();
12087 int vtkId = elem->getVtkId();
12088 if (!shapeIdToVtkIdSet.count(shapeId))
12090 shapeIdToVtkIdSet[shapeId] = emptySet;
12091 shapeIds.insert(shapeId);
12093 shapeIdToVtkIdSet[shapeId].insert(vtkId);
12096 std::map<int, std::set<DownIdType, DownIdCompare> > shapeIdToEdges; // shapeId --> set of downward edges
12097 std::set<DownIdType, DownIdCompare> emptyEdges;
12098 emptyEdges.clear();
12100 std::map<int, std::set<int> >::iterator itShape = shapeIdToVtkIdSet.begin();
12101 for (; itShape != shapeIdToVtkIdSet.end(); ++itShape)
12103 int shapeId = itShape->first;
12104 MESSAGE(" --- Shape ID --- "<< shapeId);
12105 shapeIdToEdges[shapeId] = emptyEdges;
12107 std::vector<int> nodesEdges;
12109 std::set<int>::iterator its = itShape->second.begin();
12110 for (; its != itShape->second.end(); ++its)
12113 MESSAGE(" " << vtkId);
12114 int neighborsVtkIds[NBMAXNEIGHBORS];
12115 int downIds[NBMAXNEIGHBORS];
12116 unsigned char downTypes[NBMAXNEIGHBORS];
12117 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12118 for (int n = 0; n < nbNeighbors; n++)
12120 if (neighborsVtkIds[n]<0) // only smds faces are considered as neighbors here
12122 int smdsId = meshDS->fromVtkToSmds(neighborsVtkIds[n]);
12123 const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
12124 if ( shapeIds.count(elem->getshapeId()) && !sgrps->Contains(elem)) // edge : neighbor in the set of shape, not in the group
12126 DownIdType edge(downIds[n], downTypes[n]);
12127 if (!shapeIdToEdges[shapeId].count(edge))
12129 shapeIdToEdges[shapeId].insert(edge);
12131 int nbNodes = grid->getDownArray(downTypes[n])->getNodes(downIds[n],vtkNodeId);
12132 nodesEdges.push_back(vtkNodeId[0]);
12133 nodesEdges.push_back(vtkNodeId[nbNodes-1]);
12134 MESSAGE(" --- nodes " << vtkNodeId[0]+1 << " " << vtkNodeId[nbNodes-1]+1);
12140 std::list<int> order;
12142 if (nodesEdges.size() > 0)
12144 order.push_back(nodesEdges[0]); MESSAGE(" --- back " << order.back()+1); // SMDS id = VTK id + 1;
12145 nodesEdges[0] = -1;
12146 order.push_back(nodesEdges[1]); MESSAGE(" --- back " << order.back()+1);
12147 nodesEdges[1] = -1; // do not reuse this edge
12151 int nodeTofind = order.back(); // try first to push back
12153 for (i = 0; i<nodesEdges.size(); i++)
12154 if (nodesEdges[i] == nodeTofind)
12156 if (i == nodesEdges.size())
12157 found = false; // no follower found on back
12160 if (i%2) // odd ==> use the previous one
12161 if (nodesEdges[i-1] < 0)
12165 order.push_back(nodesEdges[i-1]); MESSAGE(" --- back " << order.back()+1);
12166 nodesEdges[i-1] = -1;
12168 else // even ==> use the next one
12169 if (nodesEdges[i+1] < 0)
12173 order.push_back(nodesEdges[i+1]); MESSAGE(" --- back " << order.back()+1);
12174 nodesEdges[i+1] = -1;
12179 // try to push front
12181 nodeTofind = order.front(); // try to push front
12182 for (i = 0; i<nodesEdges.size(); i++)
12183 if (nodesEdges[i] == nodeTofind)
12185 if (i == nodesEdges.size())
12187 found = false; // no predecessor found on front
12190 if (i%2) // odd ==> use the previous one
12191 if (nodesEdges[i-1] < 0)
12195 order.push_front(nodesEdges[i-1]); MESSAGE(" --- front " << order.front()+1);
12196 nodesEdges[i-1] = -1;
12198 else // even ==> use the next one
12199 if (nodesEdges[i+1] < 0)
12203 order.push_front(nodesEdges[i+1]); MESSAGE(" --- front " << order.front()+1);
12204 nodesEdges[i+1] = -1;
12210 std::vector<int> nodes;
12211 nodes.push_back(shapeId);
12212 std::list<int>::iterator itl = order.begin();
12213 for (; itl != order.end(); itl++)
12215 nodes.push_back((*itl) + 1); // SMDS id = VTK id + 1;
12216 MESSAGE(" ordered node " << nodes[nodes.size()-1]);
12218 listOfListOfNodes.push_back(nodes);
12221 // partition geom faces with blocFissure
12222 // mesh blocFissure and geom faces of the skin (external wires given, triangle algo to choose)
12223 // mesh volume around blocFissure (skin triangles and quadrangle given, tetra algo to choose)
12229 //================================================================================
12231 * \brief Generates skin mesh (containing 2D cells) from 3D mesh
12232 * The created 2D mesh elements based on nodes of free faces of boundary volumes
12233 * \return TRUE if operation has been completed successfully, FALSE otherwise
12235 //================================================================================
12237 bool SMESH_MeshEditor::Make2DMeshFrom3D()
12239 // iterates on volume elements and detect all free faces on them
12240 SMESHDS_Mesh* aMesh = GetMeshDS();
12244 ElemFeatures faceType( SMDSAbs_Face );
12245 int nbFree = 0, nbExisted = 0, nbCreated = 0;
12246 SMDS_VolumeIteratorPtr vIt = aMesh->volumesIterator();
12249 const SMDS_MeshVolume* volume = vIt->next();
12250 SMDS_VolumeTool vTool( volume, /*ignoreCentralNodes=*/false );
12251 vTool.SetExternalNormal();
12252 const int iQuad = volume->IsQuadratic();
12253 faceType.SetQuad( iQuad );
12254 for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12256 if (!vTool.IsFreeFace(iface))
12259 vector<const SMDS_MeshNode *> nodes;
12260 int nbFaceNodes = vTool.NbFaceNodes(iface);
12261 const SMDS_MeshNode** faceNodes = vTool.GetFaceNodes(iface);
12263 for ( ; inode < nbFaceNodes; inode += iQuad+1)
12264 nodes.push_back(faceNodes[inode]);
12266 if (iQuad) // add medium nodes
12268 for ( inode = 1; inode < nbFaceNodes; inode += 2)
12269 nodes.push_back(faceNodes[inode]);
12270 if ( nbFaceNodes == 9 ) // bi-quadratic quad
12271 nodes.push_back(faceNodes[8]);
12273 // add new face based on volume nodes
12274 if (aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false) )
12276 nbExisted++; // face already exsist
12280 AddElement( nodes, faceType.SetPoly( nbFaceNodes/(iQuad+1) > 4 ));
12285 return ( nbFree == ( nbExisted + nbCreated ));
12290 inline const SMDS_MeshNode* getNodeWithSameID(SMESHDS_Mesh* mesh, const SMDS_MeshNode* node)
12292 if ( const SMDS_MeshNode* n = mesh->FindNode( node->GetID() ))
12294 return mesh->AddNodeWithID( node->X(),node->Y(),node->Z(), node->GetID() );
12297 //================================================================================
12299 * \brief Creates missing boundary elements
12300 * \param elements - elements whose boundary is to be checked
12301 * \param dimension - defines type of boundary elements to create
12302 * \param group - a group to store created boundary elements in
12303 * \param targetMesh - a mesh to store created boundary elements in
12304 * \param toCopyElements - if true, the checked elements will be copied into the targetMesh
12305 * \param toCopyExistingBoundary - if true, not only new but also pre-existing
12306 * boundary elements will be copied into the targetMesh
12307 * \param toAddExistingBondary - if true, not only new but also pre-existing
12308 * boundary elements will be added into the new group
12309 * \param aroundElements - if true, elements will be created on boundary of given
12310 * elements else, on boundary of the whole mesh.
12311 * \return nb of added boundary elements
12313 //================================================================================
12315 int SMESH_MeshEditor::MakeBoundaryMesh(const TIDSortedElemSet& elements,
12316 Bnd_Dimension dimension,
12317 SMESH_Group* group/*=0*/,
12318 SMESH_Mesh* targetMesh/*=0*/,
12319 bool toCopyElements/*=false*/,
12320 bool toCopyExistingBoundary/*=false*/,
12321 bool toAddExistingBondary/*= false*/,
12322 bool aroundElements/*= false*/)
12324 SMDSAbs_ElementType missType = (dimension == BND_2DFROM3D) ? SMDSAbs_Face : SMDSAbs_Edge;
12325 SMDSAbs_ElementType elemType = (dimension == BND_1DFROM2D) ? SMDSAbs_Face : SMDSAbs_Volume;
12326 // hope that all elements are of the same type, do not check them all
12327 if ( !elements.empty() && (*elements.begin())->GetType() != elemType )
12328 throw SALOME_Exception(LOCALIZED("wrong element type"));
12331 toCopyElements = toCopyExistingBoundary = false;
12333 SMESH_MeshEditor tgtEditor( targetMesh ? targetMesh : myMesh );
12334 SMESHDS_Mesh* aMesh = GetMeshDS(), *tgtMeshDS = tgtEditor.GetMeshDS();
12335 int nbAddedBnd = 0;
12337 // editor adding present bnd elements and optionally holding elements to add to the group
12338 SMESH_MeshEditor* presentEditor;
12339 SMESH_MeshEditor tgtEditor2( tgtEditor.GetMesh() );
12340 presentEditor = toAddExistingBondary ? &tgtEditor : &tgtEditor2;
12342 SMESH_MesherHelper helper( *myMesh );
12343 const TopAbs_ShapeEnum missShapeType = ( missType==SMDSAbs_Face ? TopAbs_FACE : TopAbs_EDGE );
12344 SMDS_VolumeTool vTool;
12345 TIDSortedElemSet avoidSet;
12346 const TIDSortedElemSet emptySet, *elemSet = aroundElements ? &elements : &emptySet;
12349 typedef vector<const SMDS_MeshNode*> TConnectivity;
12350 TConnectivity tgtNodes;
12351 ElemFeatures elemKind( missType );
12353 SMDS_ElemIteratorPtr eIt;
12354 if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
12355 else eIt = elemSetIterator( elements );
12357 while (eIt->more())
12359 const SMDS_MeshElement* elem = eIt->next();
12360 const int iQuad = elem->IsQuadratic();
12361 elemKind.SetQuad( iQuad );
12363 // ------------------------------------------------------------------------------------
12364 // 1. For an elem, get present bnd elements and connectivities of missing bnd elements
12365 // ------------------------------------------------------------------------------------
12366 vector<const SMDS_MeshElement*> presentBndElems;
12367 vector<TConnectivity> missingBndElems;
12368 TConnectivity nodes, elemNodes;
12369 if ( vTool.Set(elem, /*ignoreCentralNodes=*/true) ) // elem is a volume --------------
12371 vTool.SetExternalNormal();
12372 const SMDS_MeshElement* otherVol = 0;
12373 for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12375 if ( !vTool.IsFreeFace(iface, &otherVol) &&
12376 ( !aroundElements || elements.count( otherVol )))
12378 const SMDS_MeshNode** nn = vTool.GetFaceNodes(iface);
12379 const size_t nbFaceNodes = vTool.NbFaceNodes (iface);
12380 if ( missType == SMDSAbs_Edge ) // boundary edges
12382 nodes.resize( 2+iQuad );
12383 for ( int i = 0; i < nbFaceNodes; i += 1+iQuad)
12385 for ( int j = 0; j < nodes.size(); ++j )
12387 if ( const SMDS_MeshElement* edge =
12388 aMesh->FindElement(nodes,SMDSAbs_Edge,/*noMedium=*/false))
12389 presentBndElems.push_back( edge );
12391 missingBndElems.push_back( nodes );
12394 else // boundary face
12397 for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12398 nodes.push_back( nn[inode] ); // add corner nodes
12400 for ( inode = 1; inode < nbFaceNodes; inode += 2)
12401 nodes.push_back( nn[inode] ); // add medium nodes
12402 int iCenter = vTool.GetCenterNodeIndex(iface); // for HEX27
12404 nodes.push_back( vTool.GetNodes()[ iCenter ] );
12406 if (const SMDS_MeshElement * f = aMesh->FindElement( nodes,
12407 SMDSAbs_Face, /*noMedium=*/false ))
12408 presentBndElems.push_back( f );
12410 missingBndElems.push_back( nodes );
12412 if ( targetMesh != myMesh )
12414 // add 1D elements on face boundary to be added to a new mesh
12415 const SMDS_MeshElement* edge;
12416 for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12419 edge = aMesh->FindEdge( nn[inode], nn[inode+1], nn[inode+2]);
12421 edge = aMesh->FindEdge( nn[inode], nn[inode+1]);
12422 if ( edge && avoidSet.insert( edge ).second )
12423 presentBndElems.push_back( edge );
12429 else if ( elem->GetType() == SMDSAbs_Face ) // elem is a face ------------------------
12431 avoidSet.clear(), avoidSet.insert( elem );
12432 elemNodes.assign( SMDS_MeshElement::iterator( elem->interlacedNodesElemIterator() ),
12433 SMDS_MeshElement::iterator() );
12434 elemNodes.push_back( elemNodes[0] );
12435 nodes.resize( 2 + iQuad );
12436 const int nbLinks = elem->NbCornerNodes();
12437 for ( int i = 0, iN = 0; i < nbLinks; i++, iN += 1+iQuad )
12439 nodes[0] = elemNodes[iN];
12440 nodes[1] = elemNodes[iN+1+iQuad];
12441 if ( SMESH_MeshAlgos::FindFaceInSet( nodes[0], nodes[1], *elemSet, avoidSet))
12442 continue; // not free link
12444 if ( iQuad ) nodes[2] = elemNodes[iN+1];
12445 if ( const SMDS_MeshElement* edge =
12446 aMesh->FindElement(nodes,SMDSAbs_Edge,/*noMedium=*/false))
12447 presentBndElems.push_back( edge );
12449 missingBndElems.push_back( nodes );
12453 // ---------------------------------
12454 // 2. Add missing boundary elements
12455 // ---------------------------------
12456 if ( targetMesh != myMesh )
12457 // instead of making a map of nodes in this mesh and targetMesh,
12458 // we create nodes with same IDs.
12459 for ( size_t i = 0; i < missingBndElems.size(); ++i )
12461 TConnectivity& srcNodes = missingBndElems[i];
12462 tgtNodes.resize( srcNodes.size() );
12463 for ( inode = 0; inode < srcNodes.size(); ++inode )
12464 tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, srcNodes[inode] );
12465 if ( aroundElements && tgtEditor.GetMeshDS()->FindElement( tgtNodes,
12467 /*noMedium=*/false))
12469 tgtEditor.AddElement( tgtNodes, elemKind.SetPoly( tgtNodes.size()/(iQuad+1) > 4 ));
12473 for ( int i = 0; i < missingBndElems.size(); ++i )
12475 TConnectivity& nodes = missingBndElems[i];
12476 if ( aroundElements && tgtEditor.GetMeshDS()->FindElement( nodes,
12478 /*noMedium=*/false))
12480 SMDS_MeshElement* newElem =
12481 tgtEditor.AddElement( nodes, elemKind.SetPoly( nodes.size()/(iQuad+1) > 4 ));
12482 nbAddedBnd += bool( newElem );
12484 // try to set a new element to a shape
12485 if ( myMesh->HasShapeToMesh() )
12488 set< pair<TopAbs_ShapeEnum, int > > mediumShapes;
12489 const size_t nbN = nodes.size() / (iQuad+1 );
12490 for ( inode = 0; inode < nbN && ok; ++inode )
12492 pair<int, TopAbs_ShapeEnum> i_stype =
12493 helper.GetMediumPos( nodes[inode], nodes[(inode+1)%nbN]);
12494 if (( ok = ( i_stype.first > 0 && i_stype.second >= TopAbs_FACE )))
12495 mediumShapes.insert( make_pair ( i_stype.second, i_stype.first ));
12497 if ( ok && mediumShapes.size() > 1 )
12499 set< pair<TopAbs_ShapeEnum, int > >::iterator stype_i = mediumShapes.begin();
12500 pair<TopAbs_ShapeEnum, int> stype_i_0 = *stype_i;
12501 for ( ++stype_i; stype_i != mediumShapes.end() && ok; ++stype_i )
12503 if (( ok = ( stype_i->first != stype_i_0.first )))
12504 ok = helper.IsSubShape( aMesh->IndexToShape( stype_i->second ),
12505 aMesh->IndexToShape( stype_i_0.second ));
12508 if ( ok && mediumShapes.begin()->first == missShapeType )
12509 aMesh->SetMeshElementOnShape( newElem, mediumShapes.begin()->second );
12513 // ----------------------------------
12514 // 3. Copy present boundary elements
12515 // ----------------------------------
12516 if ( toCopyExistingBoundary )
12517 for ( int i = 0 ; i < presentBndElems.size(); ++i )
12519 const SMDS_MeshElement* e = presentBndElems[i];
12520 tgtNodes.resize( e->NbNodes() );
12521 for ( inode = 0; inode < nodes.size(); ++inode )
12522 tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, e->GetNode(inode) );
12523 presentEditor->AddElement( tgtNodes, elemKind.Init( e ));
12525 else // store present elements to add them to a group
12526 for ( int i = 0 ; i < presentBndElems.size(); ++i )
12528 presentEditor->myLastCreatedElems.Append( presentBndElems[i] );
12531 } // loop on given elements
12533 // ---------------------------------------------
12534 // 4. Fill group with boundary elements
12535 // ---------------------------------------------
12538 if ( SMESHDS_Group* g = dynamic_cast<SMESHDS_Group*>( group->GetGroupDS() ))
12539 for ( int i = 0; i < tgtEditor.myLastCreatedElems.Size(); ++i )
12540 g->SMDSGroup().Add( tgtEditor.myLastCreatedElems( i+1 ));
12542 tgtEditor.myLastCreatedElems.Clear();
12543 tgtEditor2.myLastCreatedElems.Clear();
12545 // -----------------------
12546 // 5. Copy given elements
12547 // -----------------------
12548 if ( toCopyElements && targetMesh != myMesh )
12550 if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
12551 else eIt = elemSetIterator( elements );
12552 while (eIt->more())
12554 const SMDS_MeshElement* elem = eIt->next();
12555 tgtNodes.resize( elem->NbNodes() );
12556 for ( inode = 0; inode < tgtNodes.size(); ++inode )
12557 tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, elem->GetNode(inode) );
12558 tgtEditor.AddElement( tgtNodes, elemKind.Init( elem ));
12560 tgtEditor.myLastCreatedElems.Clear();
12566 //================================================================================
12568 * \brief Copy node position and set \a to node on the same geometry
12570 //================================================================================
12572 void SMESH_MeshEditor::copyPosition( const SMDS_MeshNode* from,
12573 const SMDS_MeshNode* to )
12575 if ( !from || !to ) return;
12577 SMDS_PositionPtr pos = from->GetPosition();
12578 if ( !pos || from->getshapeId() < 1 ) return;
12580 switch ( pos->GetTypeOfPosition() )
12582 case SMDS_TOP_3DSPACE: break;
12584 case SMDS_TOP_FACE:
12586 const SMDS_FacePosition* fPos = static_cast< const SMDS_FacePosition* >( pos );
12587 GetMeshDS()->SetNodeOnFace( to, from->getshapeId(),
12588 fPos->GetUParameter(), fPos->GetVParameter() );
12591 case SMDS_TOP_EDGE:
12593 // WARNING: it is dangerous to set equal nodes on one EDGE!!!!!!!!
12594 const SMDS_EdgePosition* ePos = static_cast< const SMDS_EdgePosition* >( pos );
12595 GetMeshDS()->SetNodeOnEdge( to, from->getshapeId(), ePos->GetUParameter() );
12598 case SMDS_TOP_VERTEX:
12600 GetMeshDS()->SetNodeOnVertex( to, from->getshapeId() );
12603 case SMDS_TOP_UNSPEC: