1 // Copyright (C) 2007-2016 CEA/DEN, EDF R&D, OPEN CASCADE
3 // Copyright (C) 2003-2007 OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN,
4 // CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS
6 // This library is free software; you can redistribute it and/or
7 // modify it under the terms of the GNU Lesser General Public
8 // License as published by the Free Software Foundation; either
9 // version 2.1 of the License, or (at your option) any later version.
11 // This library is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 // Lesser General Public License for more details.
16 // You should have received a copy of the GNU Lesser General Public
17 // License along with this library; if not, write to the Free Software
18 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
23 // File : SMESH_MeshEditor.cxx
24 // Created : Mon Apr 12 16:10:22 2004
25 // Author : Edward AGAPOV (eap)
27 #include "SMESH_MeshEditor.hxx"
29 #include "SMDS_Downward.hxx"
30 #include "SMDS_EdgePosition.hxx"
31 #include "SMDS_FaceOfNodes.hxx"
32 #include "SMDS_FacePosition.hxx"
33 #include "SMDS_LinearEdge.hxx"
34 #include "SMDS_MeshGroup.hxx"
35 #include "SMDS_SetIterator.hxx"
36 #include "SMDS_SpacePosition.hxx"
37 #include "SMDS_VolumeTool.hxx"
38 #include "SMESHDS_Group.hxx"
39 #include "SMESHDS_Mesh.hxx"
40 #include "SMESH_Algo.hxx"
41 #include "SMESH_ControlsDef.hxx"
42 #include "SMESH_Group.hxx"
43 #include "SMESH_Mesh.hxx"
44 #include "SMESH_MeshAlgos.hxx"
45 #include "SMESH_MesherHelper.hxx"
46 #include "SMESH_OctreeNode.hxx"
47 #include "SMESH_subMesh.hxx"
49 #include <Basics_OCCTVersion.hxx>
51 #include "utilities.h"
54 #include <BRepAdaptor_Surface.hxx>
55 #include <BRepBuilderAPI_MakeEdge.hxx>
56 #include <BRepClass3d_SolidClassifier.hxx>
57 #include <BRep_Tool.hxx>
59 #include <Extrema_GenExtPS.hxx>
60 #include <Extrema_POnCurv.hxx>
61 #include <Extrema_POnSurf.hxx>
62 #include <Geom2d_Curve.hxx>
63 #include <GeomAdaptor_Surface.hxx>
64 #include <Geom_Curve.hxx>
65 #include <Geom_Surface.hxx>
66 #include <Precision.hxx>
67 #include <TColStd_ListOfInteger.hxx>
68 #include <TopAbs_State.hxx>
70 #include <TopExp_Explorer.hxx>
71 #include <TopTools_ListIteratorOfListOfShape.hxx>
72 #include <TopTools_ListOfShape.hxx>
73 #include <TopTools_SequenceOfShape.hxx>
75 #include <TopoDS_Edge.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 Return mesh DS
132 //================================================================================
134 SMESHDS_Mesh * SMESH_MeshEditor::GetMeshDS()
136 return myMesh->GetMeshDS();
140 //================================================================================
142 * \brief Clears myLastCreatedNodes and myLastCreatedElems
144 //================================================================================
146 void SMESH_MeshEditor::ClearLastCreated()
148 myLastCreatedNodes.Clear();
149 myLastCreatedElems.Clear();
152 //================================================================================
154 * \brief Initializes members by an existing element
155 * \param [in] elem - the source element
156 * \param [in] basicOnly - if true, does not set additional data of Ball and Polyhedron
158 //================================================================================
160 SMESH_MeshEditor::ElemFeatures&
161 SMESH_MeshEditor::ElemFeatures::Init( const SMDS_MeshElement* elem, bool basicOnly )
165 myType = elem->GetType();
166 if ( myType == SMDSAbs_Face || myType == SMDSAbs_Volume )
168 myIsPoly = elem->IsPoly();
171 myIsQuad = elem->IsQuadratic();
172 if ( myType == SMDSAbs_Volume && !basicOnly )
174 vector<int > quant = static_cast<const SMDS_VtkVolume* >( elem )->GetQuantities();
175 myPolyhedQuantities.swap( quant );
179 else if ( myType == SMDSAbs_Ball && !basicOnly )
181 myBallDiameter = static_cast<const SMDS_BallElement*>(elem)->GetDiameter();
187 //=======================================================================
191 //=======================================================================
194 SMESH_MeshEditor::AddElement(const vector<const SMDS_MeshNode*> & node,
195 const ElemFeatures& features)
197 SMDS_MeshElement* e = 0;
198 int nbnode = node.size();
199 SMESHDS_Mesh* mesh = GetMeshDS();
200 const int ID = features.myID;
202 switch ( features.myType ) {
204 if ( !features.myIsPoly ) {
206 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], ID);
207 else e = mesh->AddFace (node[0], node[1], node[2] );
209 else if (nbnode == 4) {
210 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3], ID);
211 else e = mesh->AddFace (node[0], node[1], node[2], node[3] );
213 else if (nbnode == 6) {
214 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
215 node[4], node[5], ID);
216 else e = mesh->AddFace (node[0], node[1], node[2], node[3],
219 else if (nbnode == 7) {
220 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
221 node[4], node[5], node[6], ID);
222 else e = mesh->AddFace (node[0], node[1], node[2], node[3],
223 node[4], node[5], node[6] );
225 else if (nbnode == 8) {
226 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
227 node[4], node[5], node[6], node[7], ID);
228 else e = mesh->AddFace (node[0], node[1], node[2], node[3],
229 node[4], node[5], node[6], node[7] );
231 else if (nbnode == 9) {
232 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
233 node[4], node[5], node[6], node[7], node[8], ID);
234 else e = mesh->AddFace (node[0], node[1], node[2], node[3],
235 node[4], node[5], node[6], node[7], node[8] );
238 else if ( !features.myIsQuad )
240 if ( ID >= 1 ) e = mesh->AddPolygonalFaceWithID(node, ID);
241 else e = mesh->AddPolygonalFace (node );
243 else if ( nbnode % 2 == 0 ) // just a protection
245 if ( ID >= 1 ) e = mesh->AddQuadPolygonalFaceWithID(node, ID);
246 else e = mesh->AddQuadPolygonalFace (node );
251 if ( !features.myIsPoly ) {
253 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3], ID);
254 else e = mesh->AddVolume (node[0], node[1], node[2], node[3] );
256 else if (nbnode == 5) {
257 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
259 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
262 else if (nbnode == 6) {
263 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
264 node[4], node[5], ID);
265 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
268 else if (nbnode == 8) {
269 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
270 node[4], node[5], node[6], node[7], ID);
271 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
272 node[4], node[5], node[6], node[7] );
274 else if (nbnode == 10) {
275 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
276 node[4], node[5], node[6], node[7],
277 node[8], node[9], ID);
278 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
279 node[4], node[5], node[6], node[7],
282 else if (nbnode == 12) {
283 if ( ID >= 1 ) e = mesh->AddVolumeWithID(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], ID);
286 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
287 node[4], node[5], node[6], node[7],
288 node[8], node[9], node[10], node[11] );
290 else if (nbnode == 13) {
291 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
292 node[4], node[5], node[6], node[7],
293 node[8], node[9], node[10],node[11],
295 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
296 node[4], node[5], node[6], node[7],
297 node[8], node[9], node[10],node[11],
300 else if (nbnode == 15) {
301 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
302 node[4], node[5], node[6], node[7],
303 node[8], node[9], node[10],node[11],
304 node[12],node[13],node[14],ID);
305 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
306 node[4], node[5], node[6], node[7],
307 node[8], node[9], node[10],node[11],
308 node[12],node[13],node[14] );
310 else if (nbnode == 20) {
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],ID);
316 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
317 node[4], node[5], node[6], node[7],
318 node[8], node[9], node[10],node[11],
319 node[12],node[13],node[14],node[15],
320 node[16],node[17],node[18],node[19] );
322 else if (nbnode == 27) {
323 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
324 node[4], node[5], node[6], node[7],
325 node[8], node[9], node[10],node[11],
326 node[12],node[13],node[14],node[15],
327 node[16],node[17],node[18],node[19],
328 node[20],node[21],node[22],node[23],
329 node[24],node[25],node[26], ID);
330 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
331 node[4], node[5], node[6], node[7],
332 node[8], node[9], node[10],node[11],
333 node[12],node[13],node[14],node[15],
334 node[16],node[17],node[18],node[19],
335 node[20],node[21],node[22],node[23],
336 node[24],node[25],node[26] );
339 else if ( !features.myIsQuad )
341 if ( ID >= 1 ) e = mesh->AddPolyhedralVolumeWithID(node, features.myPolyhedQuantities, ID);
342 else e = mesh->AddPolyhedralVolume (node, features.myPolyhedQuantities );
346 // if ( ID >= 1 ) e = mesh->AddQuadPolyhedralVolumeWithID(node, features.myPolyhedQuantities,ID);
347 // else e = mesh->AddQuadPolyhedralVolume (node, features.myPolyhedQuantities );
353 if ( ID >= 1 ) e = mesh->AddEdgeWithID(node[0], node[1], ID);
354 else e = mesh->AddEdge (node[0], node[1] );
356 else if ( nbnode == 3 ) {
357 if ( ID >= 1 ) e = mesh->AddEdgeWithID(node[0], node[1], node[2], ID);
358 else e = mesh->AddEdge (node[0], node[1], node[2] );
362 case SMDSAbs_0DElement:
364 if ( ID >= 1 ) e = mesh->Add0DElementWithID(node[0], ID);
365 else e = mesh->Add0DElement (node[0] );
370 if ( ID >= 1 ) e = mesh->AddNodeWithID(node[0]->X(), node[0]->Y(), node[0]->Z(), ID);
371 else e = mesh->AddNode (node[0]->X(), node[0]->Y(), node[0]->Z() );
375 if ( ID >= 1 ) e = mesh->AddBallWithID(node[0], features.myBallDiameter, ID);
376 else e = mesh->AddBall (node[0], features.myBallDiameter );
381 if ( e ) myLastCreatedElems.Append( e );
385 //=======================================================================
389 //=======================================================================
391 SMDS_MeshElement* SMESH_MeshEditor::AddElement(const vector<int> & nodeIDs,
392 const ElemFeatures& features)
394 vector<const SMDS_MeshNode*> nodes;
395 nodes.reserve( nodeIDs.size() );
396 vector<int>::const_iterator id = nodeIDs.begin();
397 while ( id != nodeIDs.end() ) {
398 if ( const SMDS_MeshNode* node = GetMeshDS()->FindNode( *id++ ))
399 nodes.push_back( node );
403 return AddElement( nodes, features );
406 //=======================================================================
408 //purpose : Remove a node or an element.
409 // Modify a compute state of sub-meshes which become empty
410 //=======================================================================
412 int SMESH_MeshEditor::Remove (const list< int >& theIDs,
415 myLastCreatedElems.Clear();
416 myLastCreatedNodes.Clear();
418 SMESHDS_Mesh* aMesh = GetMeshDS();
419 set< SMESH_subMesh *> smmap;
422 list<int>::const_iterator it = theIDs.begin();
423 for ( ; it != theIDs.end(); it++ ) {
424 const SMDS_MeshElement * elem;
426 elem = aMesh->FindNode( *it );
428 elem = aMesh->FindElement( *it );
432 // Notify VERTEX sub-meshes about modification
434 const SMDS_MeshNode* node = cast2Node( elem );
435 if ( node->GetPosition()->GetTypeOfPosition() == SMDS_TOP_VERTEX )
436 if ( int aShapeID = node->getshapeId() )
437 if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( aShapeID ) )
440 // Find sub-meshes to notify about modification
441 // SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
442 // while ( nodeIt->more() ) {
443 // const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
444 // const SMDS_PositionPtr& aPosition = node->GetPosition();
445 // if ( aPosition.get() ) {
446 // if ( int aShapeID = aPosition->GetShapeId() ) {
447 // if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( aShapeID ) )
448 // smmap.insert( sm );
455 aMesh->RemoveNode( static_cast< const SMDS_MeshNode* >( elem ));
457 aMesh->RemoveElement( elem );
461 // Notify sub-meshes about modification
462 if ( !smmap.empty() ) {
463 set< SMESH_subMesh *>::iterator smIt;
464 for ( smIt = smmap.begin(); smIt != smmap.end(); smIt++ )
465 (*smIt)->ComputeStateEngine( SMESH_subMesh::MESH_ENTITY_REMOVED );
468 // // Check if the whole mesh becomes empty
469 // if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( 1 ) )
470 // sm->ComputeStateEngine( SMESH_subMesh::CHECK_COMPUTE_STATE );
475 //================================================================================
477 * \brief Create 0D elements on all nodes of the given object.
478 * \param elements - Elements on whose nodes to create 0D elements; if empty,
479 * the all mesh is treated
480 * \param all0DElems - returns all 0D elements found or created on nodes of \a elements
481 * \param duplicateElements - to add one more 0D element to a node or not
483 //================================================================================
485 void SMESH_MeshEditor::Create0DElementsOnAllNodes( const TIDSortedElemSet& elements,
486 TIDSortedElemSet& all0DElems,
487 const bool duplicateElements )
489 SMDS_ElemIteratorPtr elemIt;
490 if ( elements.empty() )
492 elemIt = GetMeshDS()->elementsIterator( SMDSAbs_Node );
496 elemIt = elemSetIterator( elements );
499 while ( elemIt->more() )
501 const SMDS_MeshElement* e = elemIt->next();
502 SMDS_ElemIteratorPtr nodeIt = e->nodesIterator();
503 while ( nodeIt->more() )
505 const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
506 SMDS_ElemIteratorPtr it0D = n->GetInverseElementIterator( SMDSAbs_0DElement );
507 if ( duplicateElements || !it0D->more() )
509 myLastCreatedElems.Append( GetMeshDS()->Add0DElement( n ));
510 all0DElems.insert( myLastCreatedElems.Last() );
512 while ( it0D->more() )
513 all0DElems.insert( it0D->next() );
518 //=======================================================================
519 //function : FindShape
520 //purpose : Return an index of the shape theElem is on
521 // or zero if a shape not found
522 //=======================================================================
524 int SMESH_MeshEditor::FindShape (const SMDS_MeshElement * theElem)
526 myLastCreatedElems.Clear();
527 myLastCreatedNodes.Clear();
529 SMESHDS_Mesh * aMesh = GetMeshDS();
530 if ( aMesh->ShapeToMesh().IsNull() )
533 int aShapeID = theElem->getshapeId();
537 if ( SMESHDS_SubMesh * sm = aMesh->MeshElements( aShapeID ))
538 if ( sm->Contains( theElem ))
541 if ( theElem->GetType() == SMDSAbs_Node ) {
542 MESSAGE( ":( Error: invalid myShapeId of node " << theElem->GetID() );
545 MESSAGE( ":( Error: invalid myShapeId of element " << theElem->GetID() );
548 TopoDS_Shape aShape; // the shape a node of theElem is on
549 if ( theElem->GetType() != SMDSAbs_Node )
551 SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator();
552 while ( nodeIt->more() ) {
553 const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
554 if ((aShapeID = node->getshapeId()) > 0) {
555 if ( SMESHDS_SubMesh * sm = aMesh->MeshElements( aShapeID ) ) {
556 if ( sm->Contains( theElem ))
558 if ( aShape.IsNull() )
559 aShape = aMesh->IndexToShape( aShapeID );
565 // None of nodes is on a proper shape,
566 // find the shape among ancestors of aShape on which a node is
567 if ( !aShape.IsNull() ) {
568 TopTools_ListIteratorOfListOfShape ancIt( GetMesh()->GetAncestors( aShape ));
569 for ( ; ancIt.More(); ancIt.Next() ) {
570 SMESHDS_SubMesh * sm = aMesh->MeshElements( ancIt.Value() );
571 if ( sm && sm->Contains( theElem ))
572 return aMesh->ShapeToIndex( ancIt.Value() );
577 SMESHDS_SubMeshIteratorPtr smIt = GetMeshDS()->SubMeshes();
578 while ( const SMESHDS_SubMesh* sm = smIt->next() )
579 if ( sm->Contains( theElem ))
586 //=======================================================================
587 //function : IsMedium
589 //=======================================================================
591 bool SMESH_MeshEditor::IsMedium(const SMDS_MeshNode* node,
592 const SMDSAbs_ElementType typeToCheck)
594 bool isMedium = false;
595 SMDS_ElemIteratorPtr it = node->GetInverseElementIterator(typeToCheck);
596 while (it->more() && !isMedium ) {
597 const SMDS_MeshElement* elem = it->next();
598 isMedium = elem->IsMediumNode(node);
603 //=======================================================================
604 //function : shiftNodesQuadTria
605 //purpose : Shift nodes in the array corresponded to quadratic triangle
606 // example: (0,1,2,3,4,5) -> (1,2,0,4,5,3)
607 //=======================================================================
609 static void shiftNodesQuadTria(vector< const SMDS_MeshNode* >& aNodes)
611 const SMDS_MeshNode* nd1 = aNodes[0];
612 aNodes[0] = aNodes[1];
613 aNodes[1] = aNodes[2];
615 const SMDS_MeshNode* nd2 = aNodes[3];
616 aNodes[3] = aNodes[4];
617 aNodes[4] = aNodes[5];
621 //=======================================================================
622 //function : nbEdgeConnectivity
623 //purpose : return number of the edges connected with the theNode.
624 // if theEdges has connections with the other type of the
625 // elements, return -1
626 //=======================================================================
628 static int nbEdgeConnectivity(const SMDS_MeshNode* theNode)
630 // SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator();
632 // while(elemIt->more()) {
637 return theNode->NbInverseElements();
640 //=======================================================================
641 //function : getNodesFromTwoTria
643 //=======================================================================
645 static bool getNodesFromTwoTria(const SMDS_MeshElement * theTria1,
646 const SMDS_MeshElement * theTria2,
647 vector< const SMDS_MeshNode*>& N1,
648 vector< const SMDS_MeshNode*>& N2)
650 N1.assign( theTria1->begin_nodes(), theTria1->end_nodes() );
651 if ( N1.size() < 6 ) return false;
652 N2.assign( theTria2->begin_nodes(), theTria2->end_nodes() );
653 if ( N2.size() < 6 ) return false;
655 int sames[3] = {-1,-1,-1};
667 if(nbsames!=2) return false;
669 shiftNodesQuadTria(N1);
671 shiftNodesQuadTria(N1);
674 i = sames[0] + sames[1] + sames[2];
676 shiftNodesQuadTria(N2);
678 // now we receive following N1 and N2 (using numeration as in the image below)
679 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
680 // i.e. first nodes from both arrays form a new diagonal
684 //=======================================================================
685 //function : InverseDiag
686 //purpose : Replace two neighbour triangles with ones built on the same 4 nodes
687 // but having other common link.
688 // Return False if args are improper
689 //=======================================================================
691 bool SMESH_MeshEditor::InverseDiag (const SMDS_MeshElement * theTria1,
692 const SMDS_MeshElement * theTria2 )
694 myLastCreatedElems.Clear();
695 myLastCreatedNodes.Clear();
697 if (!theTria1 || !theTria2)
700 const SMDS_VtkFace* F1 = dynamic_cast<const SMDS_VtkFace*>( theTria1 );
701 if (!F1) return false;
702 const SMDS_VtkFace* F2 = dynamic_cast<const SMDS_VtkFace*>( theTria2 );
703 if (!F2) return false;
704 if ((theTria1->GetEntityType() == SMDSEntity_Triangle) &&
705 (theTria2->GetEntityType() == SMDSEntity_Triangle)) {
707 // 1 +--+ A theTria1: ( 1 A B ) A->2 ( 1 2 B ) 1 +--+ A
708 // | /| theTria2: ( B A 2 ) B->1 ( 1 A 2 ) |\ |
712 // put nodes in array and find out indices of the same ones
713 const SMDS_MeshNode* aNodes [6];
714 int sameInd [] = { -1, -1, -1, -1, -1, -1 };
716 SMDS_ElemIteratorPtr it = theTria1->nodesIterator();
717 while ( it->more() ) {
718 aNodes[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
720 if ( i > 2 ) // theTria2
721 // find same node of theTria1
722 for ( int j = 0; j < 3; j++ )
723 if ( aNodes[ i ] == aNodes[ j ]) {
732 return false; // theTria1 is not a triangle
733 it = theTria2->nodesIterator();
735 if ( i == 6 && it->more() )
736 return false; // theTria2 is not a triangle
739 // find indices of 1,2 and of A,B in theTria1
740 int iA = -1, iB = 0, i1 = 0, i2 = 0;
741 for ( i = 0; i < 6; i++ ) {
742 if ( sameInd [ i ] == -1 ) {
747 if ( iA >= 0) iB = i;
751 // nodes 1 and 2 should not be the same
752 if ( aNodes[ i1 ] == aNodes[ i2 ] )
756 aNodes[ iA ] = aNodes[ i2 ];
758 aNodes[ sameInd[ iB ]] = aNodes[ i1 ];
760 GetMeshDS()->ChangeElementNodes( theTria1, aNodes, 3 );
761 GetMeshDS()->ChangeElementNodes( theTria2, &aNodes[ 3 ], 3 );
765 } // end if(F1 && F2)
767 // check case of quadratic faces
768 if (theTria1->GetEntityType() != SMDSEntity_Quad_Triangle &&
769 theTria1->GetEntityType() != SMDSEntity_BiQuad_Triangle)
771 if (theTria2->GetEntityType() != SMDSEntity_Quad_Triangle&&
772 theTria2->GetEntityType() != SMDSEntity_BiQuad_Triangle)
776 // 1 +--+--+ 2 theTria1: (1 2 4 5 9 7) or (2 4 1 9 7 5) or (4 1 2 7 5 9)
777 // | /| theTria2: (2 3 4 6 8 9) or (3 4 2 8 9 6) or (4 2 3 9 6 8)
785 vector< const SMDS_MeshNode* > N1;
786 vector< const SMDS_MeshNode* > N2;
787 if(!getNodesFromTwoTria(theTria1,theTria2,N1,N2))
789 // now we receive following N1 and N2 (using numeration as above image)
790 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
791 // i.e. first nodes from both arrays determ new diagonal
793 vector< const SMDS_MeshNode*> N1new( N1.size() );
794 vector< const SMDS_MeshNode*> N2new( N2.size() );
795 N1new.back() = N1.back(); // central node of biquadratic
796 N2new.back() = N2.back();
797 N1new[0] = N1[0]; N2new[0] = N1[0];
798 N1new[1] = N2[0]; N2new[1] = N1[1];
799 N1new[2] = N2[1]; N2new[2] = N2[0];
800 N1new[3] = N1[4]; N2new[3] = N1[3];
801 N1new[4] = N2[3]; N2new[4] = N2[5];
802 N1new[5] = N1[5]; N2new[5] = N1[4];
803 // change nodes in faces
804 GetMeshDS()->ChangeElementNodes( theTria1, &N1new[0], N1new.size() );
805 GetMeshDS()->ChangeElementNodes( theTria2, &N2new[0], N2new.size() );
807 // move the central node of biquadratic triangle
808 SMESH_MesherHelper helper( *GetMesh() );
809 for ( int is2nd = 0; is2nd < 2; ++is2nd )
811 const SMDS_MeshElement* tria = is2nd ? theTria2 : theTria1;
812 vector< const SMDS_MeshNode*>& nodes = is2nd ? N2new : N1new;
813 if ( nodes.size() < 7 )
815 helper.SetSubShape( tria->getshapeId() );
816 const TopoDS_Face& F = TopoDS::Face( helper.GetSubShape() );
820 xyz = ( SMESH_TNodeXYZ( nodes[3] ) +
821 SMESH_TNodeXYZ( nodes[4] ) +
822 SMESH_TNodeXYZ( nodes[5] )) / 3.;
827 gp_XY uv = ( helper.GetNodeUV( F, nodes[3], nodes[2], &checkUV ) +
828 helper.GetNodeUV( F, nodes[4], nodes[0], &checkUV ) +
829 helper.GetNodeUV( F, nodes[5], nodes[1], &checkUV )) / 3.;
831 Handle(Geom_Surface) S = BRep_Tool::Surface(F,loc);
832 xyz = S->Value( uv.X(), uv.Y() );
833 xyz.Transform( loc );
834 if ( nodes[6]->GetPosition()->GetTypeOfPosition() == SMDS_TOP_FACE && // set UV
835 nodes[6]->getshapeId() > 0 )
836 GetMeshDS()->SetNodeOnFace( nodes[6], nodes[6]->getshapeId(), uv.X(), uv.Y() );
838 GetMeshDS()->MoveNode( nodes[6], xyz.X(), xyz.Y(), xyz.Z() );
843 //=======================================================================
844 //function : findTriangles
845 //purpose : find triangles sharing theNode1-theNode2 link
846 //=======================================================================
848 static bool findTriangles(const SMDS_MeshNode * theNode1,
849 const SMDS_MeshNode * theNode2,
850 const SMDS_MeshElement*& theTria1,
851 const SMDS_MeshElement*& theTria2)
853 if ( !theNode1 || !theNode2 ) return false;
855 theTria1 = theTria2 = 0;
857 set< const SMDS_MeshElement* > emap;
858 SMDS_ElemIteratorPtr it = theNode1->GetInverseElementIterator(SMDSAbs_Face);
860 const SMDS_MeshElement* elem = it->next();
861 if ( elem->NbCornerNodes() == 3 )
864 it = theNode2->GetInverseElementIterator(SMDSAbs_Face);
866 const SMDS_MeshElement* elem = it->next();
867 if ( emap.count( elem )) {
875 // theTria1 must be element with minimum ID
876 if ( theTria2->GetID() < theTria1->GetID() )
877 std::swap( theTria2, theTria1 );
885 //=======================================================================
886 //function : InverseDiag
887 //purpose : Replace two neighbour triangles sharing theNode1-theNode2 link
888 // with ones built on the same 4 nodes but having other common link.
889 // Return false if proper faces not found
890 //=======================================================================
892 bool SMESH_MeshEditor::InverseDiag (const SMDS_MeshNode * theNode1,
893 const SMDS_MeshNode * theNode2)
895 myLastCreatedElems.Clear();
896 myLastCreatedNodes.Clear();
898 const SMDS_MeshElement *tr1, *tr2;
899 if ( !findTriangles( theNode1, theNode2, tr1, tr2 ))
902 const SMDS_VtkFace* F1 = dynamic_cast<const SMDS_VtkFace*>( tr1 );
903 if (!F1) return false;
904 const SMDS_VtkFace* F2 = dynamic_cast<const SMDS_VtkFace*>( tr2 );
905 if (!F2) return false;
906 if ((tr1->GetEntityType() == SMDSEntity_Triangle) &&
907 (tr2->GetEntityType() == SMDSEntity_Triangle)) {
909 // 1 +--+ A tr1: ( 1 A B ) A->2 ( 1 2 B ) 1 +--+ A
910 // | /| tr2: ( B A 2 ) B->1 ( 1 A 2 ) |\ |
914 // put nodes in array
915 // and find indices of 1,2 and of A in tr1 and of B in tr2
916 int i, iA1 = 0, i1 = 0;
917 const SMDS_MeshNode* aNodes1 [3];
918 SMDS_ElemIteratorPtr it;
919 for (i = 0, it = tr1->nodesIterator(); it->more(); i++ ) {
920 aNodes1[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
921 if ( aNodes1[ i ] == theNode1 )
922 iA1 = i; // node A in tr1
923 else if ( aNodes1[ i ] != theNode2 )
927 const SMDS_MeshNode* aNodes2 [3];
928 for (i = 0, it = tr2->nodesIterator(); it->more(); i++ ) {
929 aNodes2[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
930 if ( aNodes2[ i ] == theNode2 )
931 iB2 = i; // node B in tr2
932 else if ( aNodes2[ i ] != theNode1 )
936 // nodes 1 and 2 should not be the same
937 if ( aNodes1[ i1 ] == aNodes2[ i2 ] )
941 aNodes1[ iA1 ] = aNodes2[ i2 ];
943 aNodes2[ iB2 ] = aNodes1[ i1 ];
945 GetMeshDS()->ChangeElementNodes( tr1, aNodes1, 3 );
946 GetMeshDS()->ChangeElementNodes( tr2, aNodes2, 3 );
951 // check case of quadratic faces
952 return InverseDiag(tr1,tr2);
955 //=======================================================================
956 //function : getQuadrangleNodes
957 //purpose : fill theQuadNodes - nodes of a quadrangle resulting from
958 // fusion of triangles tr1 and tr2 having shared link on
959 // theNode1 and theNode2
960 //=======================================================================
962 bool getQuadrangleNodes(const SMDS_MeshNode * theQuadNodes [],
963 const SMDS_MeshNode * theNode1,
964 const SMDS_MeshNode * theNode2,
965 const SMDS_MeshElement * tr1,
966 const SMDS_MeshElement * tr2 )
968 if( tr1->NbNodes() != tr2->NbNodes() )
970 // find the 4-th node to insert into tr1
971 const SMDS_MeshNode* n4 = 0;
972 SMDS_ElemIteratorPtr it = tr2->nodesIterator();
974 while ( !n4 && i<3 ) {
975 const SMDS_MeshNode * n = cast2Node( it->next() );
977 bool isDiag = ( n == theNode1 || n == theNode2 );
981 // Make an array of nodes to be in a quadrangle
982 int iNode = 0, iFirstDiag = -1;
983 it = tr1->nodesIterator();
986 const SMDS_MeshNode * n = cast2Node( it->next() );
988 bool isDiag = ( n == theNode1 || n == theNode2 );
990 if ( iFirstDiag < 0 )
992 else if ( iNode - iFirstDiag == 1 )
993 theQuadNodes[ iNode++ ] = n4; // insert the 4-th node between diagonal nodes
995 else if ( n == n4 ) {
996 return false; // tr1 and tr2 should not have all the same nodes
998 theQuadNodes[ iNode++ ] = n;
1000 if ( iNode == 3 ) // diagonal nodes have 0 and 2 indices
1001 theQuadNodes[ iNode ] = n4;
1006 //=======================================================================
1007 //function : DeleteDiag
1008 //purpose : Replace two neighbour triangles sharing theNode1-theNode2 link
1009 // with a quadrangle built on the same 4 nodes.
1010 // Return false if proper faces not found
1011 //=======================================================================
1013 bool SMESH_MeshEditor::DeleteDiag (const SMDS_MeshNode * theNode1,
1014 const SMDS_MeshNode * theNode2)
1016 myLastCreatedElems.Clear();
1017 myLastCreatedNodes.Clear();
1019 const SMDS_MeshElement *tr1, *tr2;
1020 if ( !findTriangles( theNode1, theNode2, tr1, tr2 ))
1023 const SMDS_VtkFace* F1 = dynamic_cast<const SMDS_VtkFace*>( tr1 );
1024 if (!F1) return false;
1025 const SMDS_VtkFace* F2 = dynamic_cast<const SMDS_VtkFace*>( tr2 );
1026 if (!F2) return false;
1027 SMESHDS_Mesh * aMesh = GetMeshDS();
1029 if ((tr1->GetEntityType() == SMDSEntity_Triangle) &&
1030 (tr2->GetEntityType() == SMDSEntity_Triangle)) {
1032 const SMDS_MeshNode* aNodes [ 4 ];
1033 if ( ! getQuadrangleNodes( aNodes, theNode1, theNode2, tr1, tr2 ))
1036 const SMDS_MeshElement* newElem = 0;
1037 newElem = aMesh->AddFace( aNodes[0], aNodes[1], aNodes[2], aNodes[3] );
1038 myLastCreatedElems.Append(newElem);
1039 AddToSameGroups( newElem, tr1, aMesh );
1040 int aShapeId = tr1->getshapeId();
1043 aMesh->SetMeshElementOnShape( newElem, aShapeId );
1045 aMesh->RemoveElement( tr1 );
1046 aMesh->RemoveElement( tr2 );
1051 // check case of quadratic faces
1052 if (tr1->GetEntityType() != SMDSEntity_Quad_Triangle)
1054 if (tr2->GetEntityType() != SMDSEntity_Quad_Triangle)
1058 // 1 +--+--+ 2 tr1: (1 2 4 5 9 7) or (2 4 1 9 7 5) or (4 1 2 7 5 9)
1059 // | /| tr2: (2 3 4 6 8 9) or (3 4 2 8 9 6) or (4 2 3 9 6 8)
1067 vector< const SMDS_MeshNode* > N1;
1068 vector< const SMDS_MeshNode* > N2;
1069 if(!getNodesFromTwoTria(tr1,tr2,N1,N2))
1071 // now we receive following N1 and N2 (using numeration as above image)
1072 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
1073 // i.e. first nodes from both arrays determ new diagonal
1075 const SMDS_MeshNode* aNodes[8];
1085 const SMDS_MeshElement* newElem = 0;
1086 newElem = aMesh->AddFace( aNodes[0], aNodes[1], aNodes[2], aNodes[3],
1087 aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
1088 myLastCreatedElems.Append(newElem);
1089 AddToSameGroups( newElem, tr1, aMesh );
1090 int aShapeId = tr1->getshapeId();
1093 aMesh->SetMeshElementOnShape( newElem, aShapeId );
1095 aMesh->RemoveElement( tr1 );
1096 aMesh->RemoveElement( tr2 );
1098 // remove middle node (9)
1099 GetMeshDS()->RemoveNode( N1[4] );
1104 //=======================================================================
1105 //function : Reorient
1106 //purpose : Reverse theElement orientation
1107 //=======================================================================
1109 bool SMESH_MeshEditor::Reorient (const SMDS_MeshElement * theElem)
1111 myLastCreatedElems.Clear();
1112 myLastCreatedNodes.Clear();
1116 SMDS_ElemIteratorPtr it = theElem->nodesIterator();
1117 if ( !it || !it->more() )
1120 const SMDSAbs_ElementType type = theElem->GetType();
1121 if ( type < SMDSAbs_Edge || type > SMDSAbs_Volume )
1124 const SMDSAbs_EntityType geomType = theElem->GetEntityType();
1125 if ( geomType == SMDSEntity_Polyhedra ) // polyhedron
1127 const SMDS_VtkVolume* aPolyedre =
1128 dynamic_cast<const SMDS_VtkVolume*>( theElem );
1130 MESSAGE("Warning: bad volumic element");
1133 const int nbFaces = aPolyedre->NbFaces();
1134 vector<const SMDS_MeshNode *> poly_nodes;
1135 vector<int> quantities (nbFaces);
1137 // reverse each face of the polyedre
1138 for (int iface = 1; iface <= nbFaces; iface++) {
1139 int inode, nbFaceNodes = aPolyedre->NbFaceNodes(iface);
1140 quantities[iface - 1] = nbFaceNodes;
1142 for (inode = nbFaceNodes; inode >= 1; inode--) {
1143 const SMDS_MeshNode* curNode = aPolyedre->GetFaceNode(iface, inode);
1144 poly_nodes.push_back(curNode);
1147 return GetMeshDS()->ChangePolyhedronNodes( theElem, poly_nodes, quantities );
1149 else // other elements
1151 vector<const SMDS_MeshNode*> nodes( theElem->begin_nodes(), theElem->end_nodes() );
1152 const std::vector<int>& interlace = SMDS_MeshCell::reverseSmdsOrder( geomType, nodes.size() );
1153 if ( interlace.empty() )
1155 std::reverse( nodes.begin(), nodes.end() ); // obsolete, just in case
1159 SMDS_MeshCell::applyInterlace( interlace, nodes );
1161 return GetMeshDS()->ChangeElementNodes( theElem, &nodes[0], nodes.size() );
1166 //================================================================================
1168 * \brief Reorient faces.
1169 * \param theFaces - the faces to reorient. If empty the whole mesh is meant
1170 * \param theDirection - desired direction of normal of \a theFace
1171 * \param theFace - one of \a theFaces that should be oriented according to
1172 * \a theDirection and whose orientation defines orientation of other faces
1173 * \return number of reoriented faces.
1175 //================================================================================
1177 int SMESH_MeshEditor::Reorient2D (TIDSortedElemSet & theFaces,
1178 const gp_Dir& theDirection,
1179 const SMDS_MeshElement * theFace)
1182 if ( !theFace || theFace->GetType() != SMDSAbs_Face ) return nbReori;
1184 if ( theFaces.empty() )
1186 SMDS_FaceIteratorPtr fIt = GetMeshDS()->facesIterator(/*idInceasingOrder=*/true);
1187 while ( fIt->more() )
1188 theFaces.insert( theFaces.end(), fIt->next() );
1191 // orient theFace according to theDirection
1193 SMESH_MeshAlgos::FaceNormal( theFace, normal, /*normalized=*/false );
1194 if ( normal * theDirection.XYZ() < 0 )
1195 nbReori += Reorient( theFace );
1197 // Orient other faces
1199 set< const SMDS_MeshElement* > startFaces, visitedFaces;
1200 TIDSortedElemSet avoidSet;
1201 set< SMESH_TLink > checkedLinks;
1202 pair< set< SMESH_TLink >::iterator, bool > linkIt_isNew;
1204 if ( theFaces.size() > 1 )// leave 1 face to prevent finding not selected faces
1205 theFaces.erase( theFace );
1206 startFaces.insert( theFace );
1208 int nodeInd1, nodeInd2;
1209 const SMDS_MeshElement* otherFace;
1210 vector< const SMDS_MeshElement* > facesNearLink;
1211 vector< std::pair< int, int > > nodeIndsOfFace;
1213 set< const SMDS_MeshElement* >::iterator startFace = startFaces.begin();
1214 while ( !startFaces.empty() )
1216 startFace = startFaces.begin();
1217 theFace = *startFace;
1218 startFaces.erase( startFace );
1219 if ( !visitedFaces.insert( theFace ).second )
1223 avoidSet.insert(theFace);
1225 NLink link( theFace->GetNode( 0 ), (SMDS_MeshNode *) 0 );
1227 const int nbNodes = theFace->NbCornerNodes();
1228 for ( int i = 0; i < nbNodes; ++i ) // loop on links of theFace
1230 link.second = theFace->GetNode(( i+1 ) % nbNodes );
1231 linkIt_isNew = checkedLinks.insert( link );
1232 if ( !linkIt_isNew.second )
1234 // link has already been checked and won't be encountered more
1235 // if the group (theFaces) is manifold
1236 //checkedLinks.erase( linkIt_isNew.first );
1240 facesNearLink.clear();
1241 nodeIndsOfFace.clear();
1242 while (( otherFace = SMESH_MeshAlgos::FindFaceInSet( link.first, link.second,
1244 &nodeInd1, &nodeInd2 )))
1245 if ( otherFace != theFace)
1247 facesNearLink.push_back( otherFace );
1248 nodeIndsOfFace.push_back( make_pair( nodeInd1, nodeInd2 ));
1249 avoidSet.insert( otherFace );
1251 if ( facesNearLink.size() > 1 )
1253 // NON-MANIFOLD mesh shell !
1254 // select a face most co-directed with theFace,
1255 // other faces won't be visited this time
1257 SMESH_MeshAlgos::FaceNormal( theFace, NF, /*normalized=*/false );
1258 double proj, maxProj = -1;
1259 for ( size_t i = 0; i < facesNearLink.size(); ++i ) {
1260 SMESH_MeshAlgos::FaceNormal( facesNearLink[i], NOF, /*normalized=*/false );
1261 if (( proj = Abs( NF * NOF )) > maxProj ) {
1263 otherFace = facesNearLink[i];
1264 nodeInd1 = nodeIndsOfFace[i].first;
1265 nodeInd2 = nodeIndsOfFace[i].second;
1268 // not to visit rejected faces
1269 for ( size_t i = 0; i < facesNearLink.size(); ++i )
1270 if ( facesNearLink[i] != otherFace && theFaces.size() > 1 )
1271 visitedFaces.insert( facesNearLink[i] );
1273 else if ( facesNearLink.size() == 1 )
1275 otherFace = facesNearLink[0];
1276 nodeInd1 = nodeIndsOfFace.back().first;
1277 nodeInd2 = nodeIndsOfFace.back().second;
1279 if ( otherFace && otherFace != theFace)
1281 // link must be reverse in otherFace if orientation ot otherFace
1282 // is same as that of theFace
1283 if ( abs(nodeInd2-nodeInd1) == 1 ? nodeInd2 > nodeInd1 : nodeInd1 > nodeInd2 )
1285 nbReori += Reorient( otherFace );
1287 startFaces.insert( otherFace );
1290 std::swap( link.first, link.second ); // reverse the link
1296 //================================================================================
1298 * \brief Reorient faces basing on orientation of adjacent volumes.
1299 * \param theFaces - faces to reorient. If empty, all mesh faces are treated.
1300 * \param theVolumes - reference volumes.
1301 * \param theOutsideNormal - to orient faces to have their normal
1302 * pointing either \a outside or \a inside the adjacent volumes.
1303 * \return number of reoriented faces.
1305 //================================================================================
1307 int SMESH_MeshEditor::Reorient2DBy3D (TIDSortedElemSet & theFaces,
1308 TIDSortedElemSet & theVolumes,
1309 const bool theOutsideNormal)
1313 SMDS_ElemIteratorPtr faceIt;
1314 if ( theFaces.empty() )
1315 faceIt = GetMeshDS()->elementsIterator( SMDSAbs_Face );
1317 faceIt = elemSetIterator( theFaces );
1319 vector< const SMDS_MeshNode* > faceNodes;
1320 TIDSortedElemSet checkedVolumes;
1321 set< const SMDS_MeshNode* > faceNodesSet;
1322 SMDS_VolumeTool volumeTool;
1324 while ( faceIt->more() ) // loop on given faces
1326 const SMDS_MeshElement* face = faceIt->next();
1327 if ( face->GetType() != SMDSAbs_Face )
1330 const size_t nbCornersNodes = face->NbCornerNodes();
1331 faceNodes.assign( face->begin_nodes(), face->end_nodes() );
1333 checkedVolumes.clear();
1334 SMDS_ElemIteratorPtr vIt = faceNodes[ 0 ]->GetInverseElementIterator( SMDSAbs_Volume );
1335 while ( vIt->more() )
1337 const SMDS_MeshElement* volume = vIt->next();
1339 if ( !checkedVolumes.insert( volume ).second )
1341 if ( !theVolumes.empty() && !theVolumes.count( volume ))
1344 // is volume adjacent?
1345 bool allNodesCommon = true;
1346 for ( size_t iN = 1; iN < nbCornersNodes && allNodesCommon; ++iN )
1347 allNodesCommon = ( volume->GetNodeIndex( faceNodes[ iN ]) > -1 );
1348 if ( !allNodesCommon )
1351 // get nodes of a corresponding volume facet
1352 faceNodesSet.clear();
1353 faceNodesSet.insert( faceNodes.begin(), faceNodes.end() );
1354 volumeTool.Set( volume );
1355 int facetID = volumeTool.GetFaceIndex( faceNodesSet );
1356 if ( facetID < 0 ) continue;
1357 volumeTool.SetExternalNormal();
1358 const SMDS_MeshNode** facetNodes = volumeTool.GetFaceNodes( facetID );
1360 // compare order of faceNodes and facetNodes
1361 const int iQ = 1 + ( nbCornersNodes < faceNodes.size() );
1363 for ( int i = 0; i < 2; ++i )
1365 const SMDS_MeshNode* n = facetNodes[ i*iQ ];
1366 for ( size_t iN = 0; iN < nbCornersNodes; ++iN )
1367 if ( faceNodes[ iN ] == n )
1373 bool isOutside = Abs( iNN[0]-iNN[1] ) == 1 ? iNN[0] < iNN[1] : iNN[0] > iNN[1];
1374 if ( isOutside != theOutsideNormal )
1375 nbReori += Reorient( face );
1377 } // loop on given faces
1382 //=======================================================================
1383 //function : getBadRate
1385 //=======================================================================
1387 static double getBadRate (const SMDS_MeshElement* theElem,
1388 SMESH::Controls::NumericalFunctorPtr& theCrit)
1390 SMESH::Controls::TSequenceOfXYZ P;
1391 if ( !theElem || !theCrit->GetPoints( theElem, P ))
1393 return theCrit->GetBadRate( theCrit->GetValue( P ), theElem->NbNodes() );
1394 //return theCrit->GetBadRate( theCrit->GetValue( theElem->GetID() ), theElem->NbNodes() );
1397 //=======================================================================
1398 //function : QuadToTri
1399 //purpose : Cut quadrangles into triangles.
1400 // theCrit is used to select a diagonal to cut
1401 //=======================================================================
1403 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet & theElems,
1404 SMESH::Controls::NumericalFunctorPtr theCrit)
1406 myLastCreatedElems.Clear();
1407 myLastCreatedNodes.Clear();
1409 if ( !theCrit.get() )
1412 SMESHDS_Mesh * aMesh = GetMeshDS();
1414 Handle(Geom_Surface) surface;
1415 SMESH_MesherHelper helper( *GetMesh() );
1417 TIDSortedElemSet::iterator itElem;
1418 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
1420 const SMDS_MeshElement* elem = *itElem;
1421 if ( !elem || elem->GetType() != SMDSAbs_Face )
1423 if ( elem->NbCornerNodes() != 4 )
1426 // retrieve element nodes
1427 vector< const SMDS_MeshNode* > aNodes( elem->begin_nodes(), elem->end_nodes() );
1429 // compare two sets of possible triangles
1430 double aBadRate1, aBadRate2; // to what extent a set is bad
1431 SMDS_FaceOfNodes tr1 ( aNodes[0], aNodes[1], aNodes[2] );
1432 SMDS_FaceOfNodes tr2 ( aNodes[2], aNodes[3], aNodes[0] );
1433 aBadRate1 = getBadRate( &tr1, theCrit ) + getBadRate( &tr2, theCrit );
1435 SMDS_FaceOfNodes tr3 ( aNodes[1], aNodes[2], aNodes[3] );
1436 SMDS_FaceOfNodes tr4 ( aNodes[3], aNodes[0], aNodes[1] );
1437 aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit );
1439 const int aShapeId = FindShape( elem );
1440 const SMDS_MeshElement* newElem1 = 0;
1441 const SMDS_MeshElement* newElem2 = 0;
1443 if ( !elem->IsQuadratic() ) // split liner quadrangle
1445 // for MaxElementLength2D functor we return minimum diagonal for splitting,
1446 // because aBadRate1=2*len(diagonal 1-3); aBadRate2=2*len(diagonal 2-4)
1447 if ( aBadRate1 <= aBadRate2 ) {
1448 // tr1 + tr2 is better
1449 newElem1 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
1450 newElem2 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
1453 // tr3 + tr4 is better
1454 newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
1455 newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
1458 else // split quadratic quadrangle
1460 helper.SetIsQuadratic( true );
1461 helper.SetIsBiQuadratic( aNodes.size() == 9 );
1463 helper.AddTLinks( static_cast< const SMDS_MeshFace* >( elem ));
1464 if ( aNodes.size() == 9 )
1466 helper.SetIsBiQuadratic( true );
1467 if ( aBadRate1 <= aBadRate2 )
1468 helper.AddTLinkNode( aNodes[0], aNodes[2], aNodes[8] );
1470 helper.AddTLinkNode( aNodes[1], aNodes[3], aNodes[8] );
1472 // create a new element
1473 if ( aBadRate1 <= aBadRate2 ) {
1474 newElem1 = helper.AddFace( aNodes[2], aNodes[3], aNodes[0] );
1475 newElem2 = helper.AddFace( aNodes[2], aNodes[0], aNodes[1] );
1478 newElem1 = helper.AddFace( aNodes[3], aNodes[0], aNodes[1] );
1479 newElem2 = helper.AddFace( aNodes[3], aNodes[1], aNodes[2] );
1483 // care of a new element
1485 myLastCreatedElems.Append(newElem1);
1486 myLastCreatedElems.Append(newElem2);
1487 AddToSameGroups( newElem1, elem, aMesh );
1488 AddToSameGroups( newElem2, elem, aMesh );
1490 // put a new triangle on the same shape
1492 aMesh->SetMeshElementOnShape( newElem1, aShapeId );
1493 aMesh->SetMeshElementOnShape( newElem2, aShapeId );
1495 aMesh->RemoveElement( elem );
1500 //=======================================================================
1502 * \brief Split each of given quadrangles into 4 triangles.
1503 * \param theElems - The faces to be splitted. If empty all faces are split.
1505 //=======================================================================
1507 void SMESH_MeshEditor::QuadTo4Tri (TIDSortedElemSet & theElems)
1509 myLastCreatedElems.Clear();
1510 myLastCreatedNodes.Clear();
1512 SMESH_MesherHelper helper( *GetMesh() );
1513 helper.SetElementsOnShape( true );
1515 SMDS_ElemIteratorPtr faceIt;
1516 if ( theElems.empty() ) faceIt = GetMeshDS()->elementsIterator(SMDSAbs_Face);
1517 else faceIt = elemSetIterator( theElems );
1520 gp_XY uv [9]; uv[8] = gp_XY(0,0);
1522 vector< const SMDS_MeshNode* > nodes;
1523 SMESHDS_SubMesh* subMeshDS = 0;
1525 Handle(Geom_Surface) surface;
1526 TopLoc_Location loc;
1528 while ( faceIt->more() )
1530 const SMDS_MeshElement* quad = faceIt->next();
1531 if ( !quad || quad->NbCornerNodes() != 4 )
1534 // get a surface the quad is on
1536 if ( quad->getshapeId() < 1 )
1539 helper.SetSubShape( 0 );
1542 else if ( quad->getshapeId() != helper.GetSubShapeID() )
1544 helper.SetSubShape( quad->getshapeId() );
1545 if ( !helper.GetSubShape().IsNull() &&
1546 helper.GetSubShape().ShapeType() == TopAbs_FACE )
1548 F = TopoDS::Face( helper.GetSubShape() );
1549 surface = BRep_Tool::Surface( F, loc );
1550 subMeshDS = GetMeshDS()->MeshElements( quad->getshapeId() );
1554 helper.SetSubShape( 0 );
1559 // create a central node
1561 const SMDS_MeshNode* nCentral;
1562 nodes.assign( quad->begin_nodes(), quad->end_nodes() );
1564 if ( nodes.size() == 9 )
1566 nCentral = nodes.back();
1573 for ( ; iN < nodes.size(); ++iN )
1574 xyz[ iN ] = SMESH_TNodeXYZ( nodes[ iN ] );
1576 for ( ; iN < 8; ++iN ) // mid-side points of a linear qudrangle
1577 xyz[ iN ] = 0.5 * ( xyz[ iN - 4 ] + xyz[( iN - 3 )%4 ] );
1579 xyz[ 8 ] = helper.calcTFI( 0.5, 0.5,
1580 xyz[0], xyz[1], xyz[2], xyz[3],
1581 xyz[4], xyz[5], xyz[6], xyz[7] );
1585 for ( ; iN < nodes.size(); ++iN )
1586 uv[ iN ] = helper.GetNodeUV( F, nodes[iN], nodes[(iN+2)%4], &checkUV );
1588 for ( ; iN < 8; ++iN ) // UV of mid-side points of a linear qudrangle
1589 uv[ iN ] = helper.GetMiddleUV( surface, uv[ iN - 4 ], uv[( iN - 3 )%4 ] );
1591 uv[ 8 ] = helper.calcTFI( 0.5, 0.5,
1592 uv[0], uv[1], uv[2], uv[3],
1593 uv[4], uv[5], uv[6], uv[7] );
1595 gp_Pnt p = surface->Value( uv[8].X(), uv[8].Y() ).Transformed( loc );
1599 nCentral = helper.AddNode( xyz[8].X(), xyz[8].Y(), xyz[8].Z(), /*id=*/0,
1600 uv[8].X(), uv[8].Y() );
1601 myLastCreatedNodes.Append( nCentral );
1604 // create 4 triangles
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 GetMeshDS()->RemoveFreeElement( quad, subMeshDS, /*fromGroups=*/false );
1613 for ( int i = 0; i < 4; ++i )
1615 SMDS_MeshElement* tria = helper.AddFace( nodes[ i ],
1618 ReplaceElemInGroups( tria, quad, GetMeshDS() );
1619 myLastCreatedElems.Append( tria );
1624 //=======================================================================
1625 //function : BestSplit
1626 //purpose : Find better diagonal for cutting.
1627 //=======================================================================
1629 int SMESH_MeshEditor::BestSplit (const SMDS_MeshElement* theQuad,
1630 SMESH::Controls::NumericalFunctorPtr theCrit)
1632 myLastCreatedElems.Clear();
1633 myLastCreatedNodes.Clear();
1638 if (!theQuad || theQuad->GetType() != SMDSAbs_Face )
1641 if( theQuad->NbNodes()==4 ||
1642 (theQuad->NbNodes()==8 && theQuad->IsQuadratic()) ) {
1644 // retrieve element nodes
1645 const SMDS_MeshNode* aNodes [4];
1646 SMDS_ElemIteratorPtr itN = theQuad->nodesIterator();
1648 //while (itN->more())
1650 aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
1652 // compare two sets of possible triangles
1653 double aBadRate1, aBadRate2; // to what extent a set is bad
1654 SMDS_FaceOfNodes tr1 ( aNodes[0], aNodes[1], aNodes[2] );
1655 SMDS_FaceOfNodes tr2 ( aNodes[2], aNodes[3], aNodes[0] );
1656 aBadRate1 = getBadRate( &tr1, theCrit ) + getBadRate( &tr2, theCrit );
1658 SMDS_FaceOfNodes tr3 ( aNodes[1], aNodes[2], aNodes[3] );
1659 SMDS_FaceOfNodes tr4 ( aNodes[3], aNodes[0], aNodes[1] );
1660 aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit );
1661 // for MaxElementLength2D functor we return minimum diagonal for splitting,
1662 // because aBadRate1=2*len(diagonal 1-3); aBadRate2=2*len(diagonal 2-4)
1663 if (aBadRate1 <= aBadRate2) // tr1 + tr2 is better
1664 return 1; // diagonal 1-3
1666 return 2; // diagonal 2-4
1673 // Methods of splitting volumes into tetra
1675 const int theHexTo5_1[5*4+1] =
1677 0, 1, 2, 5, 0, 4, 5, 7, 0, 2, 3, 7, 2, 5, 6, 7, 0, 5, 2, 7, -1
1679 const int theHexTo5_2[5*4+1] =
1681 1, 2, 3, 6, 1, 4, 5, 6, 0, 1, 3, 4, 3, 4, 6, 7, 1, 3, 4, 6, -1
1683 const int* theHexTo5[2] = { theHexTo5_1, theHexTo5_2 };
1685 const int theHexTo6_1[6*4+1] =
1687 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
1689 const int theHexTo6_2[6*4+1] =
1691 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
1693 const int theHexTo6_3[6*4+1] =
1695 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
1697 const int theHexTo6_4[6*4+1] =
1699 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
1701 const int* theHexTo6[4] = { theHexTo6_1, theHexTo6_2, theHexTo6_3, theHexTo6_4 };
1703 const int thePyraTo2_1[2*4+1] =
1705 0, 1, 2, 4, 0, 2, 3, 4, -1
1707 const int thePyraTo2_2[2*4+1] =
1709 1, 2, 3, 4, 1, 3, 0, 4, -1
1711 const int* thePyraTo2[2] = { thePyraTo2_1, thePyraTo2_2 };
1713 const int thePentaTo3_1[3*4+1] =
1715 0, 1, 2, 3, 1, 3, 4, 2, 2, 3, 4, 5, -1
1717 const int thePentaTo3_2[3*4+1] =
1719 1, 2, 0, 4, 2, 4, 5, 0, 0, 4, 5, 3, -1
1721 const int thePentaTo3_3[3*4+1] =
1723 2, 0, 1, 5, 0, 5, 3, 1, 1, 5, 3, 4, -1
1725 const int thePentaTo3_4[3*4+1] =
1727 0, 1, 2, 3, 1, 3, 4, 5, 2, 3, 1, 5, -1
1729 const int thePentaTo3_5[3*4+1] =
1731 1, 2, 0, 4, 2, 4, 5, 3, 0, 4, 2, 3, -1
1733 const int thePentaTo3_6[3*4+1] =
1735 2, 0, 1, 5, 0, 5, 3, 4, 1, 5, 0, 4, -1
1737 const int* thePentaTo3[6] = { thePentaTo3_1, thePentaTo3_2, thePentaTo3_3,
1738 thePentaTo3_4, thePentaTo3_5, thePentaTo3_6 };
1740 // Methods of splitting hexahedron into prisms
1742 const int theHexTo4Prisms_BT[6*4+1] = // bottom-top
1744 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
1746 const int theHexTo4Prisms_LR[6*4+1] = // left-right
1748 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
1750 const int theHexTo4Prisms_FB[6*4+1] = // front-back
1752 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
1755 const int theHexTo2Prisms_BT_1[6*2+1] =
1757 0, 1, 3, 4, 5, 7, 1, 2, 3, 5, 6, 7, -1
1759 const int theHexTo2Prisms_BT_2[6*2+1] =
1761 0, 1, 2, 4, 5, 6, 0, 2, 3, 4, 6, 7, -1
1763 const int* theHexTo2Prisms_BT[2] = { theHexTo2Prisms_BT_1, theHexTo2Prisms_BT_2 };
1765 const int theHexTo2Prisms_LR_1[6*2+1] =
1767 1, 0, 4, 2, 3, 7, 1, 4, 5, 2, 7, 6, -1
1769 const int theHexTo2Prisms_LR_2[6*2+1] =
1771 1, 0, 4, 2, 3, 7, 1, 4, 5, 2, 7, 6, -1
1773 const int* theHexTo2Prisms_LR[2] = { theHexTo2Prisms_LR_1, theHexTo2Prisms_LR_2 };
1775 const int theHexTo2Prisms_FB_1[6*2+1] =
1777 0, 3, 4, 1, 2, 5, 3, 7, 4, 2, 6, 5, -1
1779 const int theHexTo2Prisms_FB_2[6*2+1] =
1781 0, 3, 7, 1, 2, 7, 0, 7, 4, 1, 6, 5, -1
1783 const int* theHexTo2Prisms_FB[2] = { theHexTo2Prisms_FB_1, theHexTo2Prisms_FB_2 };
1786 struct TTriangleFacet //!< stores indices of three nodes of tetra facet
1789 TTriangleFacet(int n1, int n2, int n3): _n1(n1), _n2(n2), _n3(n3) {}
1790 bool contains(int n) const { return ( n == _n1 || n == _n2 || n == _n3 ); }
1791 bool hasAdjacentVol( const SMDS_MeshElement* elem,
1792 const SMDSAbs_GeometryType geom = SMDSGeom_TETRA) const;
1798 const int* _connectivity; //!< foursomes of tetra connectivy finished by -1
1799 bool _baryNode; //!< additional node is to be created at cell barycenter
1800 bool _ownConn; //!< to delete _connectivity in destructor
1801 map<int, const SMDS_MeshNode*> _faceBaryNode; //!< map face index to node at BC of face
1803 TSplitMethod( int nbTet=0, const int* conn=0, bool addNode=false)
1804 : _nbSplits(nbTet), _nbCorners(4), _connectivity(conn), _baryNode(addNode), _ownConn(false) {}
1805 ~TSplitMethod() { if ( _ownConn ) delete [] _connectivity; _connectivity = 0; }
1806 bool hasFacet( const TTriangleFacet& facet ) const
1808 if ( _nbCorners == 4 )
1810 const int* tetConn = _connectivity;
1811 for ( ; tetConn[0] >= 0; tetConn += 4 )
1812 if (( facet.contains( tetConn[0] ) +
1813 facet.contains( tetConn[1] ) +
1814 facet.contains( tetConn[2] ) +
1815 facet.contains( tetConn[3] )) == 3 )
1818 else // prism, _nbCorners == 6
1820 const int* prismConn = _connectivity;
1821 for ( ; prismConn[0] >= 0; prismConn += 6 )
1823 if (( facet.contains( prismConn[0] ) &&
1824 facet.contains( prismConn[1] ) &&
1825 facet.contains( prismConn[2] ))
1827 ( facet.contains( prismConn[3] ) &&
1828 facet.contains( prismConn[4] ) &&
1829 facet.contains( prismConn[5] )))
1837 //=======================================================================
1839 * \brief return TSplitMethod for the given element to split into tetrahedra
1841 //=======================================================================
1843 TSplitMethod getTetraSplitMethod( SMDS_VolumeTool& vol, const int theMethodFlags)
1845 const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
1847 // at HEXA_TO_24 method, each face of volume is split into triangles each based on
1848 // an edge and a face barycenter; tertaherdons are based on triangles and
1849 // a volume barycenter
1850 const bool is24TetMode = ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_24 );
1852 // Find out how adjacent volumes are split
1854 vector < list< TTriangleFacet > > triaSplitsByFace( vol.NbFaces() ); // splits of each side
1855 int hasAdjacentSplits = 0, maxTetConnSize = 0;
1856 for ( int iF = 0; iF < vol.NbFaces(); ++iF )
1858 int nbNodes = vol.NbFaceNodes( iF ) / iQ;
1859 maxTetConnSize += 4 * ( nbNodes - (is24TetMode ? 0 : 2));
1860 if ( nbNodes < 4 ) continue;
1862 list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
1863 const int* nInd = vol.GetFaceNodesIndices( iF );
1866 TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
1867 TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
1868 if ( t012.hasAdjacentVol( vol.Element() )) triaSplits.push_back( t012 );
1869 else if ( t123.hasAdjacentVol( vol.Element() )) triaSplits.push_back( t123 );
1873 int iCom = 0; // common node of triangle faces to split into
1874 for ( int iVar = 0; iVar < nbNodes; ++iVar, ++iCom )
1876 TTriangleFacet t012( nInd[ iQ * ( iCom )],
1877 nInd[ iQ * ( (iCom+1)%nbNodes )],
1878 nInd[ iQ * ( (iCom+2)%nbNodes )]);
1879 TTriangleFacet t023( nInd[ iQ * ( iCom )],
1880 nInd[ iQ * ( (iCom+2)%nbNodes )],
1881 nInd[ iQ * ( (iCom+3)%nbNodes )]);
1882 if ( t012.hasAdjacentVol( vol.Element() ) && t023.hasAdjacentVol( vol.Element() ))
1884 triaSplits.push_back( t012 );
1885 triaSplits.push_back( t023 );
1890 if ( !triaSplits.empty() )
1891 hasAdjacentSplits = true;
1894 // Among variants of split method select one compliant with adjacent volumes
1896 TSplitMethod method;
1897 if ( !vol.Element()->IsPoly() && !is24TetMode )
1899 int nbVariants = 2, nbTet = 0;
1900 const int** connVariants = 0;
1901 switch ( vol.Element()->GetEntityType() )
1903 case SMDSEntity_Hexa:
1904 case SMDSEntity_Quad_Hexa:
1905 case SMDSEntity_TriQuad_Hexa:
1906 if ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_5 )
1907 connVariants = theHexTo5, nbTet = 5;
1909 connVariants = theHexTo6, nbTet = 6, nbVariants = 4;
1911 case SMDSEntity_Pyramid:
1912 case SMDSEntity_Quad_Pyramid:
1913 connVariants = thePyraTo2; nbTet = 2;
1915 case SMDSEntity_Penta:
1916 case SMDSEntity_Quad_Penta:
1917 case SMDSEntity_BiQuad_Penta:
1918 connVariants = thePentaTo3; nbTet = 3; nbVariants = 6;
1923 for ( int variant = 0; variant < nbVariants && method._nbSplits == 0; ++variant )
1925 // check method compliancy with adjacent tetras,
1926 // all found splits must be among facets of tetras described by this method
1927 method = TSplitMethod( nbTet, connVariants[variant] );
1928 if ( hasAdjacentSplits && method._nbSplits > 0 )
1930 bool facetCreated = true;
1931 for ( size_t iF = 0; facetCreated && iF < triaSplitsByFace.size(); ++iF )
1933 list< TTriangleFacet >::const_iterator facet = triaSplitsByFace[iF].begin();
1934 for ( ; facetCreated && facet != triaSplitsByFace[iF].end(); ++facet )
1935 facetCreated = method.hasFacet( *facet );
1937 if ( !facetCreated )
1938 method = TSplitMethod(0); // incompatible method
1942 if ( method._nbSplits < 1 )
1944 // No standard method is applicable, use a generic solution:
1945 // each facet of a volume is split into triangles and
1946 // each of triangles and a volume barycenter form a tetrahedron.
1948 const bool isHex27 = ( vol.Element()->GetEntityType() == SMDSEntity_TriQuad_Hexa );
1950 int* connectivity = new int[ maxTetConnSize + 1 ];
1951 method._connectivity = connectivity;
1952 method._ownConn = true;
1953 method._baryNode = !isHex27; // to create central node or not
1956 int baryCenInd = vol.NbNodes() - int( isHex27 );
1957 for ( int iF = 0; iF < vol.NbFaces(); ++iF )
1959 const int nbNodes = vol.NbFaceNodes( iF ) / iQ;
1960 const int* nInd = vol.GetFaceNodesIndices( iF );
1961 // find common node of triangle facets of tetra to create
1962 int iCommon = 0; // index in linear numeration
1963 const list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
1964 if ( !triaSplits.empty() )
1967 const TTriangleFacet* facet = &triaSplits.front();
1968 for ( ; iCommon < nbNodes-1 ; ++iCommon )
1969 if ( facet->contains( nInd[ iQ * iCommon ]) &&
1970 facet->contains( nInd[ iQ * ((iCommon+2)%nbNodes) ]))
1973 else if ( nbNodes > 3 && !is24TetMode )
1975 // find the best method of splitting into triangles by aspect ratio
1976 SMESH::Controls::NumericalFunctorPtr aspectRatio( new SMESH::Controls::AspectRatio);
1977 map< double, int > badness2iCommon;
1978 const SMDS_MeshNode** nodes = vol.GetFaceNodes( iF );
1979 int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
1980 for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCommon )
1983 for ( int iLast = iCommon+2; iLast < iCommon+nbNodes; ++iLast )
1985 SMDS_FaceOfNodes tria ( nodes[ iQ*( iCommon )],
1986 nodes[ iQ*((iLast-1)%nbNodes)],
1987 nodes[ iQ*((iLast )%nbNodes)]);
1988 badness += getBadRate( &tria, aspectRatio );
1990 badness2iCommon.insert( make_pair( badness, iCommon ));
1992 // use iCommon with lowest badness
1993 iCommon = badness2iCommon.begin()->second;
1995 if ( iCommon >= nbNodes )
1996 iCommon = 0; // something wrong
1998 // fill connectivity of tetrahedra based on a current face
1999 int nbTet = nbNodes - 2;
2000 if ( is24TetMode && nbNodes > 3 && triaSplits.empty())
2005 faceBaryCenInd = vol.GetCenterNodeIndex( iF );
2006 method._faceBaryNode[ iF ] = vol.GetNodes()[ faceBaryCenInd ];
2010 method._faceBaryNode[ iF ] = 0;
2011 faceBaryCenInd = baryCenInd + method._faceBaryNode.size();
2014 for ( int i = 0; i < nbTet; ++i )
2016 int i1 = i, i2 = (i+1) % nbNodes;
2017 if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
2018 connectivity[ connSize++ ] = nInd[ iQ * i1 ];
2019 connectivity[ connSize++ ] = nInd[ iQ * i2 ];
2020 connectivity[ connSize++ ] = faceBaryCenInd;
2021 connectivity[ connSize++ ] = baryCenInd;
2026 for ( int i = 0; i < nbTet; ++i )
2028 int i1 = (iCommon+1+i) % nbNodes, i2 = (iCommon+2+i) % nbNodes;
2029 if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
2030 connectivity[ connSize++ ] = nInd[ iQ * iCommon ];
2031 connectivity[ connSize++ ] = nInd[ iQ * i1 ];
2032 connectivity[ connSize++ ] = nInd[ iQ * i2 ];
2033 connectivity[ connSize++ ] = baryCenInd;
2036 method._nbSplits += nbTet;
2038 } // loop on volume faces
2040 connectivity[ connSize++ ] = -1;
2042 } // end of generic solution
2046 //=======================================================================
2048 * \brief return TSplitMethod to split haxhedron into prisms
2050 //=======================================================================
2052 TSplitMethod getPrismSplitMethod( SMDS_VolumeTool& vol,
2053 const int methodFlags,
2054 const int facetToSplit)
2056 // order of facets in HEX according to SMDS_VolumeTool::Hexa_F :
2058 const int iF = ( facetToSplit < 2 ) ? 0 : 1 + ( facetToSplit-2 ) % 2; // [0,1,2]
2060 if ( methodFlags == SMESH_MeshEditor::HEXA_TO_4_PRISMS )
2062 static TSplitMethod to4methods[4]; // order BT, LR, FB
2063 if ( to4methods[iF]._nbSplits == 0 )
2067 to4methods[iF]._connectivity = theHexTo4Prisms_BT;
2068 to4methods[iF]._faceBaryNode[ 0 ] = 0;
2069 to4methods[iF]._faceBaryNode[ 1 ] = 0;
2072 to4methods[iF]._connectivity = theHexTo4Prisms_LR;
2073 to4methods[iF]._faceBaryNode[ 2 ] = 0;
2074 to4methods[iF]._faceBaryNode[ 4 ] = 0;
2077 to4methods[iF]._connectivity = theHexTo4Prisms_FB;
2078 to4methods[iF]._faceBaryNode[ 3 ] = 0;
2079 to4methods[iF]._faceBaryNode[ 5 ] = 0;
2081 default: return to4methods[3];
2083 to4methods[iF]._nbSplits = 4;
2084 to4methods[iF]._nbCorners = 6;
2086 return to4methods[iF];
2088 // else if ( methodFlags == HEXA_TO_2_PRISMS )
2090 TSplitMethod method;
2092 const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
2094 const int nbVariants = 2, nbSplits = 2;
2095 const int** connVariants = 0;
2097 case 0: connVariants = theHexTo2Prisms_BT; break;
2098 case 1: connVariants = theHexTo2Prisms_LR; break;
2099 case 2: connVariants = theHexTo2Prisms_FB; break;
2100 default: return method;
2103 // look for prisms adjacent via facetToSplit and an opposite one
2104 for ( int is2nd = 0; is2nd < 2; ++is2nd )
2106 int iFacet = is2nd ? vol.GetOppFaceIndexOfHex( facetToSplit ) : facetToSplit;
2107 int nbNodes = vol.NbFaceNodes( iFacet ) / iQ;
2108 if ( nbNodes != 4 ) return method;
2110 const int* nInd = vol.GetFaceNodesIndices( iFacet );
2111 TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
2112 TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
2114 if ( t012.hasAdjacentVol( vol.Element(), SMDSGeom_PENTA ))
2116 else if ( t123.hasAdjacentVol( vol.Element(), SMDSGeom_PENTA ))
2121 // there are adjacent prism
2122 for ( int variant = 0; variant < nbVariants; ++variant )
2124 // check method compliancy with adjacent prisms,
2125 // the found prism facets must be among facets of prisms described by current method
2126 method._nbSplits = nbSplits;
2127 method._nbCorners = 6;
2128 method._connectivity = connVariants[ variant ];
2129 if ( method.hasFacet( *t ))
2134 // No adjacent prisms. Select a variant with a best aspect ratio.
2136 double badness[2] = { 0., 0. };
2137 static SMESH::Controls::NumericalFunctorPtr aspectRatio( new SMESH::Controls::AspectRatio);
2138 const SMDS_MeshNode** nodes = vol.GetNodes();
2139 for ( int variant = 0; variant < nbVariants; ++variant )
2140 for ( int is2nd = 0; is2nd < 2; ++is2nd )
2142 int iFacet = is2nd ? vol.GetOppFaceIndexOfHex( facetToSplit ) : facetToSplit;
2143 const int* nInd = vol.GetFaceNodesIndices( iFacet );
2145 method._connectivity = connVariants[ variant ];
2146 TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
2147 TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
2148 TTriangleFacet* t = ( method.hasFacet( t012 )) ? & t012 : & t123;
2150 SMDS_FaceOfNodes tria ( nodes[ t->_n1 ],
2153 badness[ variant ] += getBadRate( &tria, aspectRatio );
2155 const int iBetter = ( badness[1] < badness[0] && badness[0]-badness[1] > 0.1 * badness[0] );
2157 method._nbSplits = nbSplits;
2158 method._nbCorners = 6;
2159 method._connectivity = connVariants[ iBetter ];
2164 //================================================================================
2166 * \brief Check if there is a tetraherdon adjacent to the given element via this facet
2168 //================================================================================
2170 bool TTriangleFacet::hasAdjacentVol( const SMDS_MeshElement* elem,
2171 const SMDSAbs_GeometryType geom ) const
2173 // find the tetrahedron including the three nodes of facet
2174 const SMDS_MeshNode* n1 = elem->GetNode(_n1);
2175 const SMDS_MeshNode* n2 = elem->GetNode(_n2);
2176 const SMDS_MeshNode* n3 = elem->GetNode(_n3);
2177 SMDS_ElemIteratorPtr volIt1 = n1->GetInverseElementIterator(SMDSAbs_Volume);
2178 while ( volIt1->more() )
2180 const SMDS_MeshElement* v = volIt1->next();
2181 if ( v->GetGeomType() != geom )
2183 const int lastCornerInd = v->NbCornerNodes() - 1;
2184 if ( v->IsQuadratic() && v->GetNodeIndex( n1 ) > lastCornerInd )
2185 continue; // medium node not allowed
2186 const int ind2 = v->GetNodeIndex( n2 );
2187 if ( ind2 < 0 || lastCornerInd < ind2 )
2189 const int ind3 = v->GetNodeIndex( n3 );
2190 if ( ind3 < 0 || lastCornerInd < ind3 )
2197 //=======================================================================
2199 * \brief A key of a face of volume
2201 //=======================================================================
2203 struct TVolumeFaceKey: pair< pair< int, int>, pair< int, int> >
2205 TVolumeFaceKey( SMDS_VolumeTool& vol, int iF )
2207 TIDSortedNodeSet sortedNodes;
2208 const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
2209 int nbNodes = vol.NbFaceNodes( iF );
2210 const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iF );
2211 for ( int i = 0; i < nbNodes; i += iQ )
2212 sortedNodes.insert( fNodes[i] );
2213 TIDSortedNodeSet::iterator n = sortedNodes.begin();
2214 first.first = (*(n++))->GetID();
2215 first.second = (*(n++))->GetID();
2216 second.first = (*(n++))->GetID();
2217 second.second = ( sortedNodes.size() > 3 ) ? (*(n++))->GetID() : 0;
2222 //=======================================================================
2223 //function : SplitVolumes
2224 //purpose : Split volume elements into tetrahedra or prisms.
2225 // If facet ID < 0, element is split into tetrahedra,
2226 // else a hexahedron is split into prisms so that the given facet is
2227 // split into triangles
2228 //=======================================================================
2230 void SMESH_MeshEditor::SplitVolumes (const TFacetOfElem & theElems,
2231 const int theMethodFlags)
2233 SMDS_VolumeTool volTool;
2234 SMESH_MesherHelper helper( *GetMesh()), fHelper(*GetMesh());
2235 fHelper.ToFixNodeParameters( true );
2237 SMESHDS_SubMesh* subMesh = 0;//GetMeshDS()->MeshElements(1);
2238 SMESHDS_SubMesh* fSubMesh = 0;//subMesh;
2240 SMESH_SequenceOfElemPtr newNodes, newElems;
2242 // map face of volume to it's baricenrtic node
2243 map< TVolumeFaceKey, const SMDS_MeshNode* > volFace2BaryNode;
2245 vector<const SMDS_MeshElement* > splitVols;
2247 TFacetOfElem::const_iterator elem2facet = theElems.begin();
2248 for ( ; elem2facet != theElems.end(); ++elem2facet )
2250 const SMDS_MeshElement* elem = elem2facet->first;
2251 const int facetToSplit = elem2facet->second;
2252 if ( elem->GetType() != SMDSAbs_Volume )
2254 const SMDSAbs_EntityType geomType = elem->GetEntityType();
2255 if ( geomType == SMDSEntity_Tetra || geomType == SMDSEntity_Quad_Tetra )
2258 if ( !volTool.Set( elem, /*ignoreCentralNodes=*/false )) continue; // strange...
2260 TSplitMethod splitMethod = ( facetToSplit < 0 ?
2261 getTetraSplitMethod( volTool, theMethodFlags ) :
2262 getPrismSplitMethod( volTool, theMethodFlags, facetToSplit ));
2263 if ( splitMethod._nbSplits < 1 ) continue;
2265 // find submesh to add new tetras to
2266 if ( !subMesh || !subMesh->Contains( elem ))
2268 int shapeID = FindShape( elem );
2269 helper.SetSubShape( shapeID ); // helper will add tetras to the found submesh
2270 subMesh = GetMeshDS()->MeshElements( shapeID );
2273 if ( elem->IsQuadratic() )
2276 // add quadratic links to the helper
2277 for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
2279 const SMDS_MeshNode** fNodes = volTool.GetFaceNodes( iF );
2280 int nbN = volTool.NbFaceNodes( iF ) - bool( volTool.GetCenterNodeIndex(iF) > 0 );
2281 for ( int iN = 0; iN < nbN; iN += iQ )
2282 helper.AddTLinkNode( fNodes[iN], fNodes[iN+2], fNodes[iN+1] );
2284 helper.SetIsQuadratic( true );
2289 helper.SetIsQuadratic( false );
2291 vector<const SMDS_MeshNode*> nodes( volTool.GetNodes(),
2292 volTool.GetNodes() + elem->NbNodes() );
2293 helper.SetElementsOnShape( true );
2294 if ( splitMethod._baryNode )
2296 // make a node at barycenter
2297 volTool.GetBaryCenter( bc[0], bc[1], bc[2] );
2298 SMDS_MeshNode* gcNode = helper.AddNode( bc[0], bc[1], bc[2] );
2299 nodes.push_back( gcNode );
2300 newNodes.Append( gcNode );
2302 if ( !splitMethod._faceBaryNode.empty() )
2304 // make or find baricentric nodes of faces
2305 map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.begin();
2306 for ( ; iF_n != splitMethod._faceBaryNode.end(); ++iF_n )
2308 map< TVolumeFaceKey, const SMDS_MeshNode* >::iterator f_n =
2309 volFace2BaryNode.insert
2310 ( make_pair( TVolumeFaceKey( volTool,iF_n->first ), iF_n->second )).first;
2313 volTool.GetFaceBaryCenter( iF_n->first, bc[0], bc[1], bc[2] );
2314 newNodes.Append( f_n->second = helper.AddNode( bc[0], bc[1], bc[2] ));
2316 nodes.push_back( iF_n->second = f_n->second );
2321 splitVols.resize( splitMethod._nbSplits ); // splits of a volume
2322 const int* volConn = splitMethod._connectivity;
2323 if ( splitMethod._nbCorners == 4 ) // tetra
2324 for ( int i = 0; i < splitMethod._nbSplits; ++i, volConn += splitMethod._nbCorners )
2325 newElems.Append( splitVols[ i ] = helper.AddVolume( nodes[ volConn[0] ],
2326 nodes[ volConn[1] ],
2327 nodes[ volConn[2] ],
2328 nodes[ volConn[3] ]));
2330 for ( int i = 0; i < splitMethod._nbSplits; ++i, volConn += splitMethod._nbCorners )
2331 newElems.Append( splitVols[ i ] = helper.AddVolume( nodes[ volConn[0] ],
2332 nodes[ volConn[1] ],
2333 nodes[ volConn[2] ],
2334 nodes[ volConn[3] ],
2335 nodes[ volConn[4] ],
2336 nodes[ volConn[5] ]));
2338 ReplaceElemInGroups( elem, splitVols, GetMeshDS() );
2340 // Split faces on sides of the split volume
2342 const SMDS_MeshNode** volNodes = volTool.GetNodes();
2343 for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
2345 const int nbNodes = volTool.NbFaceNodes( iF ) / iQ;
2346 if ( nbNodes < 4 ) continue;
2348 // find an existing face
2349 vector<const SMDS_MeshNode*> fNodes( volTool.GetFaceNodes( iF ),
2350 volTool.GetFaceNodes( iF ) + volTool.NbFaceNodes( iF ));
2351 while ( const SMDS_MeshElement* face = GetMeshDS()->FindElement( fNodes, SMDSAbs_Face,
2352 /*noMedium=*/false))
2355 helper.SetElementsOnShape( false );
2356 vector< const SMDS_MeshElement* > triangles;
2358 // find submesh to add new triangles in
2359 if ( !fSubMesh || !fSubMesh->Contains( face ))
2361 int shapeID = FindShape( face );
2362 fSubMesh = GetMeshDS()->MeshElements( shapeID );
2364 map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.find(iF);
2365 if ( iF_n != splitMethod._faceBaryNode.end() )
2367 const SMDS_MeshNode *baryNode = iF_n->second;
2368 for ( int iN = 0; iN < nbNodes*iQ; iN += iQ )
2370 const SMDS_MeshNode* n1 = fNodes[iN];
2371 const SMDS_MeshNode *n2 = fNodes[(iN+iQ)%(nbNodes*iQ)];
2372 const SMDS_MeshNode *n3 = baryNode;
2373 if ( !volTool.IsFaceExternal( iF ))
2375 triangles.push_back( helper.AddFace( n1,n2,n3 ));
2377 if ( fSubMesh ) // update position of the bary node on geometry
2380 subMesh->RemoveNode( baryNode, false );
2381 GetMeshDS()->SetNodeOnFace( baryNode, fSubMesh->GetID() );
2382 const TopoDS_Shape& s = GetMeshDS()->IndexToShape( fSubMesh->GetID() );
2383 if ( !s.IsNull() && s.ShapeType() == TopAbs_FACE )
2385 fHelper.SetSubShape( s );
2386 gp_XY uv( 1e100, 1e100 );
2388 if ( !fHelper.CheckNodeUV( TopoDS::Face( s ), baryNode,
2389 uv, /*tol=*/1e-7, /*force=*/true, distXYZ ) &&
2392 // node is too far from the surface
2393 GetMeshDS()->MoveNode( baryNode, distXYZ[1], distXYZ[2], distXYZ[3] );
2394 const_cast<SMDS_MeshNode*>( baryNode )->SetPosition
2395 ( SMDS_PositionPtr( new SMDS_FacePosition( uv.X(), uv.Y() )));
2402 // among possible triangles create ones described by split method
2403 const int* nInd = volTool.GetFaceNodesIndices( iF );
2404 int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
2405 int iCom = 0; // common node of triangle faces to split into
2406 list< TTriangleFacet > facets;
2407 for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCom )
2409 TTriangleFacet t012( nInd[ iQ * ( iCom )],
2410 nInd[ iQ * ( (iCom+1)%nbNodes )],
2411 nInd[ iQ * ( (iCom+2)%nbNodes )]);
2412 TTriangleFacet t023( nInd[ iQ * ( iCom )],
2413 nInd[ iQ * ( (iCom+2)%nbNodes )],
2414 nInd[ iQ * ( (iCom+3)%nbNodes )]);
2415 if ( splitMethod.hasFacet( t012 ) && splitMethod.hasFacet( t023 ))
2417 facets.push_back( t012 );
2418 facets.push_back( t023 );
2419 for ( int iLast = iCom+4; iLast < iCom+nbNodes; ++iLast )
2420 facets.push_back( TTriangleFacet( nInd[ iQ * ( iCom )],
2421 nInd[ iQ * ((iLast-1)%nbNodes )],
2422 nInd[ iQ * ((iLast )%nbNodes )]));
2426 list< TTriangleFacet >::iterator facet = facets.begin();
2427 if ( facet == facets.end() )
2429 for ( ; facet != facets.end(); ++facet )
2431 if ( !volTool.IsFaceExternal( iF ))
2432 swap( facet->_n2, facet->_n3 );
2433 triangles.push_back( helper.AddFace( volNodes[ facet->_n1 ],
2434 volNodes[ facet->_n2 ],
2435 volNodes[ facet->_n3 ]));
2438 for ( size_t i = 0; i < triangles.size(); ++i )
2440 if ( !triangles[ i ]) continue;
2442 fSubMesh->AddElement( triangles[ i ]);
2443 newElems.Append( triangles[ i ]);
2445 ReplaceElemInGroups( face, triangles, GetMeshDS() );
2446 GetMeshDS()->RemoveFreeElement( face, fSubMesh, /*fromGroups=*/false );
2448 } // while a face based on facet nodes exists
2449 } // loop on volume faces to split them into triangles
2451 GetMeshDS()->RemoveFreeElement( elem, subMesh, /*fromGroups=*/false );
2453 if ( geomType == SMDSEntity_TriQuad_Hexa )
2455 // remove medium nodes that could become free
2456 for ( int i = 20; i < volTool.NbNodes(); ++i )
2457 if ( volNodes[i]->NbInverseElements() == 0 )
2458 GetMeshDS()->RemoveNode( volNodes[i] );
2460 } // loop on volumes to split
2462 myLastCreatedNodes = newNodes;
2463 myLastCreatedElems = newElems;
2466 //=======================================================================
2467 //function : GetHexaFacetsToSplit
2468 //purpose : For hexahedra that will be split into prisms, finds facets to
2469 // split into triangles. Only hexahedra adjacent to the one closest
2470 // to theFacetNormal.Location() are returned.
2471 //param [in,out] theHexas - the hexahedra
2472 //param [in] theFacetNormal - facet normal
2473 //param [out] theFacets - the hexahedra and found facet IDs
2474 //=======================================================================
2476 void SMESH_MeshEditor::GetHexaFacetsToSplit( TIDSortedElemSet& theHexas,
2477 const gp_Ax1& theFacetNormal,
2478 TFacetOfElem & theFacets)
2480 #define THIS_METHOD "SMESH_MeshEditor::GetHexaFacetsToSplit(): "
2482 // Find a hexa closest to the location of theFacetNormal
2484 const SMDS_MeshElement* startHex;
2486 // get SMDS_ElemIteratorPtr on theHexas
2487 typedef const SMDS_MeshElement* TValue;
2488 typedef TIDSortedElemSet::iterator TSetIterator;
2489 typedef SMDS::SimpleAccessor<TValue,TSetIterator> TAccesor;
2490 typedef SMDS_MeshElement::GeomFilter TFilter;
2491 typedef SMDS_SetIterator < TValue, TSetIterator, TAccesor, TFilter > TElemSetIter;
2492 SMDS_ElemIteratorPtr elemIt = SMDS_ElemIteratorPtr
2493 ( new TElemSetIter( theHexas.begin(),
2495 SMDS_MeshElement::GeomFilter( SMDSGeom_HEXA )));
2497 SMESH_ElementSearcher* searcher =
2498 SMESH_MeshAlgos::GetElementSearcher( *myMesh->GetMeshDS(), elemIt );
2500 startHex = searcher->FindClosestTo( theFacetNormal.Location(), SMDSAbs_Volume );
2505 throw SALOME_Exception( THIS_METHOD "startHex not found");
2508 // Select a facet of startHex by theFacetNormal
2510 SMDS_VolumeTool vTool( startHex );
2511 double norm[3], dot, maxDot = 0;
2513 for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2514 if ( vTool.GetFaceNormal( iF, norm[0], norm[1], norm[2] ))
2516 dot = Abs( theFacetNormal.Direction().Dot( gp_Dir( norm[0], norm[1], norm[2] )));
2524 throw SALOME_Exception( THIS_METHOD "facet of startHex not found");
2526 // Fill theFacets starting from facetID of startHex
2528 // facets used for searching of volumes adjacent to already treated ones
2529 typedef pair< TFacetOfElem::iterator, int > TElemFacets;
2530 typedef map< TVolumeFaceKey, TElemFacets > TFacetMap;
2531 TFacetMap facetsToCheck;
2533 set<const SMDS_MeshNode*> facetNodes;
2534 const SMDS_MeshElement* curHex;
2536 const bool allHex = ((int) theHexas.size() == myMesh->NbHexas() );
2540 // move in two directions from startHex via facetID
2541 for ( int is2nd = 0; is2nd < 2; ++is2nd )
2544 int curFacet = facetID;
2545 if ( is2nd ) // do not treat startHex twice
2547 vTool.Set( curHex );
2548 if ( vTool.IsFreeFace( curFacet, &curHex ))
2554 vTool.GetFaceNodes( curFacet, facetNodes );
2555 vTool.Set( curHex );
2556 curFacet = vTool.GetFaceIndex( facetNodes );
2561 // store a facet to split
2562 if ( curHex->GetGeomType() != SMDSGeom_HEXA )
2564 theFacets.insert( make_pair( curHex, -1 ));
2567 if ( !allHex && !theHexas.count( curHex ))
2570 pair< TFacetOfElem::iterator, bool > facetIt2isNew =
2571 theFacets.insert( make_pair( curHex, curFacet ));
2572 if ( !facetIt2isNew.second )
2575 // remember not-to-split facets in facetsToCheck
2576 int oppFacet = vTool.GetOppFaceIndexOfHex( curFacet );
2577 for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2579 if ( iF == curFacet && iF == oppFacet )
2581 TVolumeFaceKey facetKey ( vTool, iF );
2582 TElemFacets elemFacet( facetIt2isNew.first, iF );
2583 pair< TFacetMap::iterator, bool > it2isnew =
2584 facetsToCheck.insert( make_pair( facetKey, elemFacet ));
2585 if ( !it2isnew.second )
2586 facetsToCheck.erase( it2isnew.first ); // adjacent hex already checked
2588 // pass to a volume adjacent via oppFacet
2589 if ( vTool.IsFreeFace( oppFacet, &curHex ))
2595 // get a new curFacet
2596 vTool.GetFaceNodes( oppFacet, facetNodes );
2597 vTool.Set( curHex );
2598 curFacet = vTool.GetFaceIndex( facetNodes, /*hint=*/curFacet );
2601 } // move in two directions from startHex via facetID
2603 // Find a new startHex by facetsToCheck
2607 TFacetMap::iterator fIt = facetsToCheck.begin();
2608 while ( !startHex && fIt != facetsToCheck.end() )
2610 const TElemFacets& elemFacets = fIt->second;
2611 const SMDS_MeshElement* hex = elemFacets.first->first;
2612 int splitFacet = elemFacets.first->second;
2613 int lateralFacet = elemFacets.second;
2614 facetsToCheck.erase( fIt );
2615 fIt = facetsToCheck.begin();
2618 if ( vTool.IsFreeFace( lateralFacet, &curHex ) ||
2619 curHex->GetGeomType() != SMDSGeom_HEXA )
2621 if ( !allHex && !theHexas.count( curHex ))
2626 // find a facet of startHex to split
2628 set<const SMDS_MeshNode*> lateralNodes;
2629 vTool.GetFaceNodes( lateralFacet, lateralNodes );
2630 vTool.GetFaceNodes( splitFacet, facetNodes );
2631 int oppLateralFacet = vTool.GetOppFaceIndexOfHex( lateralFacet );
2632 vTool.Set( startHex );
2633 lateralFacet = vTool.GetFaceIndex( lateralNodes, oppLateralFacet );
2635 // look for a facet of startHex having common nodes with facetNodes
2636 // but not lateralFacet
2637 for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2639 if ( iF == lateralFacet )
2641 int nbCommonNodes = 0;
2642 const SMDS_MeshNode** nn = vTool.GetFaceNodes( iF );
2643 for ( int iN = 0, nbN = vTool.NbFaceNodes( iF ); iN < nbN; ++iN )
2644 nbCommonNodes += facetNodes.count( nn[ iN ]);
2646 if ( nbCommonNodes >= 2 )
2653 throw SALOME_Exception( THIS_METHOD "facet of a new startHex not found");
2655 } // while ( startHex )
2662 //================================================================================
2664 * \brief Selects nodes of several elements according to a given interlace
2665 * \param [in] srcNodes - nodes to select from
2666 * \param [out] tgtNodesVec - array of nodes of several elements to fill in
2667 * \param [in] interlace - indices of nodes for all elements
2668 * \param [in] nbElems - nb of elements
2669 * \param [in] nbNodes - nb of nodes in each element
2670 * \param [in] mesh - the mesh
2671 * \param [out] elemQueue - a list to push elements found by the selected nodes
2672 * \param [in] type - type of elements to look for
2674 //================================================================================
2676 void selectNodes( const vector< const SMDS_MeshNode* >& srcNodes,
2677 vector< const SMDS_MeshNode* >* tgtNodesVec,
2678 const int* interlace,
2681 SMESHDS_Mesh* mesh = 0,
2682 list< const SMDS_MeshElement* >* elemQueue=0,
2683 SMDSAbs_ElementType type=SMDSAbs_All)
2685 for ( int iE = 0; iE < nbElems; ++iE )
2687 vector< const SMDS_MeshNode* >& elemNodes = tgtNodesVec[iE];
2688 const int* select = & interlace[iE*nbNodes];
2689 elemNodes.resize( nbNodes );
2690 for ( int iN = 0; iN < nbNodes; ++iN )
2691 elemNodes[iN] = srcNodes[ select[ iN ]];
2693 const SMDS_MeshElement* e;
2695 for ( int iE = 0; iE < nbElems; ++iE )
2696 if (( e = mesh->FindElement( tgtNodesVec[iE], type, /*noMedium=*/false)))
2697 elemQueue->push_back( e );
2701 //=======================================================================
2703 * Split bi-quadratic elements into linear ones without creation of additional nodes
2704 * - bi-quadratic triangle will be split into 3 linear quadrangles;
2705 * - bi-quadratic quadrangle will be split into 4 linear quadrangles;
2706 * - tri-quadratic hexahedron will be split into 8 linear hexahedra;
2707 * Quadratic elements of lower dimension adjacent to the split bi-quadratic element
2708 * will be split in order to keep the mesh conformal.
2709 * \param elems - elements to split
2711 //=======================================================================
2713 void SMESH_MeshEditor::SplitBiQuadraticIntoLinear(TIDSortedElemSet& theElems)
2715 vector< const SMDS_MeshNode* > elemNodes(27), subNodes[12], splitNodes[8];
2716 vector<const SMDS_MeshElement* > splitElems;
2717 list< const SMDS_MeshElement* > elemQueue;
2718 list< const SMDS_MeshElement* >::iterator elemIt;
2720 SMESHDS_Mesh * mesh = GetMeshDS();
2721 ElemFeatures *elemType, hexaType(SMDSAbs_Volume), quadType(SMDSAbs_Face), segType(SMDSAbs_Edge);
2722 int nbElems, nbNodes;
2724 TIDSortedElemSet::iterator elemSetIt = theElems.begin();
2725 for ( ; elemSetIt != theElems.end(); ++elemSetIt )
2728 elemQueue.push_back( *elemSetIt );
2729 for ( elemIt = elemQueue.begin(); elemIt != elemQueue.end(); ++elemIt )
2731 const SMDS_MeshElement* elem = *elemIt;
2732 switch( elem->GetEntityType() )
2734 case SMDSEntity_TriQuad_Hexa: // HEX27
2736 elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2737 nbElems = nbNodes = 8;
2738 elemType = & hexaType;
2740 // get nodes for new elements
2741 static int vInd[8][8] = {{ 0,8,20,11, 16,21,26,24 },
2742 { 1,9,20,8, 17,22,26,21 },
2743 { 2,10,20,9, 18,23,26,22 },
2744 { 3,11,20,10, 19,24,26,23 },
2745 { 16,21,26,24, 4,12,25,15 },
2746 { 17,22,26,21, 5,13,25,12 },
2747 { 18,23,26,22, 6,14,25,13 },
2748 { 19,24,26,23, 7,15,25,14 }};
2749 selectNodes( elemNodes, & splitNodes[0], &vInd[0][0], nbElems, nbNodes );
2751 // add boundary faces to elemQueue
2752 static int fInd[6][9] = {{ 0,1,2,3, 8,9,10,11, 20 },
2753 { 4,5,6,7, 12,13,14,15, 25 },
2754 { 0,1,5,4, 8,17,12,16, 21 },
2755 { 1,2,6,5, 9,18,13,17, 22 },
2756 { 2,3,7,6, 10,19,14,18, 23 },
2757 { 3,0,4,7, 11,16,15,19, 24 }};
2758 selectNodes( elemNodes, & subNodes[0], &fInd[0][0], 6,9, mesh, &elemQueue, SMDSAbs_Face );
2760 // add boundary segments to elemQueue
2761 static int eInd[12][3] = {{ 0,1,8 }, { 1,2,9 }, { 2,3,10 }, { 3,0,11 },
2762 { 4,5,12}, { 5,6,13}, { 6,7,14 }, { 7,4,15 },
2763 { 0,4,16}, { 1,5,17}, { 2,6,18 }, { 3,7,19 }};
2764 selectNodes( elemNodes, & subNodes[0], &eInd[0][0], 12,3, mesh, &elemQueue, SMDSAbs_Edge );
2767 case SMDSEntity_BiQuad_Triangle: // TRIA7
2769 elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2772 elemType = & quadType;
2774 // get nodes for new elements
2775 static int fInd[3][4] = {{ 0,3,6,5 }, { 1,4,6,3 }, { 2,5,6,4 }};
2776 selectNodes( elemNodes, & splitNodes[0], &fInd[0][0], nbElems, nbNodes );
2778 // add boundary segments to elemQueue
2779 static int eInd[3][3] = {{ 0,1,3 }, { 1,2,4 }, { 2,0,5 }};
2780 selectNodes( elemNodes, & subNodes[0], &eInd[0][0], 3,3, mesh, &elemQueue, SMDSAbs_Edge );
2783 case SMDSEntity_BiQuad_Quadrangle: // QUAD9
2785 elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2788 elemType = & quadType;
2790 // get nodes for new elements
2791 static int fInd[4][4] = {{ 0,4,8,7 }, { 1,5,8,4 }, { 2,6,8,5 }, { 3,7,8,6 }};
2792 selectNodes( elemNodes, & splitNodes[0], &fInd[0][0], nbElems, nbNodes );
2794 // add boundary segments to elemQueue
2795 static int eInd[4][3] = {{ 0,1,4 }, { 1,2,5 }, { 2,3,6 }, { 3,0,7 }};
2796 selectNodes( elemNodes, & subNodes[0], &eInd[0][0], 4,3, mesh, &elemQueue, SMDSAbs_Edge );
2799 case SMDSEntity_Quad_Edge:
2801 if ( elemIt == elemQueue.begin() )
2802 continue; // an elem is in theElems
2803 elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2806 elemType = & segType;
2808 // get nodes for new elements
2809 static int eInd[2][2] = {{ 0,2 }, { 2,1 }};
2810 selectNodes( elemNodes, & splitNodes[0], &eInd[0][0], nbElems, nbNodes );
2814 } // switch( elem->GetEntityType() )
2816 // Create new elements
2818 SMESHDS_SubMesh* subMesh = mesh->MeshElements( elem->getshapeId() );
2822 //elemType->SetID( elem->GetID() ); // create an elem with the same ID as a removed one
2823 mesh->RemoveFreeElement( elem, subMesh, /*fromGroups=*/false );
2824 //splitElems.push_back( AddElement( splitNodes[ 0 ], *elemType ));
2825 //elemType->SetID( -1 );
2827 for ( int iE = 0; iE < nbElems; ++iE )
2828 splitElems.push_back( AddElement( splitNodes[ iE ], *elemType ));
2831 ReplaceElemInGroups( elem, splitElems, mesh );
2834 for ( size_t i = 0; i < splitElems.size(); ++i )
2835 subMesh->AddElement( splitElems[i] );
2840 //=======================================================================
2841 //function : AddToSameGroups
2842 //purpose : add elemToAdd to the groups the elemInGroups belongs to
2843 //=======================================================================
2845 void SMESH_MeshEditor::AddToSameGroups (const SMDS_MeshElement* elemToAdd,
2846 const SMDS_MeshElement* elemInGroups,
2847 SMESHDS_Mesh * aMesh)
2849 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2850 if (!groups.empty()) {
2851 set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2852 for ( ; grIt != groups.end(); grIt++ ) {
2853 SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2854 if ( group && group->Contains( elemInGroups ))
2855 group->SMDSGroup().Add( elemToAdd );
2861 //=======================================================================
2862 //function : RemoveElemFromGroups
2863 //purpose : Remove removeelem to the groups the elemInGroups belongs to
2864 //=======================================================================
2865 void SMESH_MeshEditor::RemoveElemFromGroups (const SMDS_MeshElement* removeelem,
2866 SMESHDS_Mesh * aMesh)
2868 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2869 if (!groups.empty())
2871 set<SMESHDS_GroupBase*>::const_iterator GrIt = groups.begin();
2872 for (; GrIt != groups.end(); GrIt++)
2874 SMESHDS_Group* grp = dynamic_cast<SMESHDS_Group*>(*GrIt);
2875 if (!grp || grp->IsEmpty()) continue;
2876 grp->SMDSGroup().Remove(removeelem);
2881 //================================================================================
2883 * \brief Replace elemToRm by elemToAdd in the all groups
2885 //================================================================================
2887 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement* elemToRm,
2888 const SMDS_MeshElement* elemToAdd,
2889 SMESHDS_Mesh * aMesh)
2891 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2892 if (!groups.empty()) {
2893 set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2894 for ( ; grIt != groups.end(); grIt++ ) {
2895 SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2896 if ( group && group->SMDSGroup().Remove( elemToRm ) && elemToAdd )
2897 group->SMDSGroup().Add( elemToAdd );
2902 //================================================================================
2904 * \brief Replace elemToRm by elemToAdd in the all groups
2906 //================================================================================
2908 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement* elemToRm,
2909 const vector<const SMDS_MeshElement*>& elemToAdd,
2910 SMESHDS_Mesh * aMesh)
2912 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2913 if (!groups.empty())
2915 set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2916 for ( ; grIt != groups.end(); grIt++ ) {
2917 SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2918 if ( group && group->SMDSGroup().Remove( elemToRm ) )
2919 for ( size_t i = 0; i < elemToAdd.size(); ++i )
2920 group->SMDSGroup().Add( elemToAdd[ i ] );
2925 //=======================================================================
2926 //function : QuadToTri
2927 //purpose : Cut quadrangles into triangles.
2928 // theCrit is used to select a diagonal to cut
2929 //=======================================================================
2931 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet & theElems,
2932 const bool the13Diag)
2934 myLastCreatedElems.Clear();
2935 myLastCreatedNodes.Clear();
2937 SMESHDS_Mesh * aMesh = GetMeshDS();
2939 Handle(Geom_Surface) surface;
2940 SMESH_MesherHelper helper( *GetMesh() );
2942 TIDSortedElemSet::iterator itElem;
2943 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
2945 const SMDS_MeshElement* elem = *itElem;
2946 if ( !elem || elem->GetGeomType() != SMDSGeom_QUADRANGLE )
2949 if ( elem->NbNodes() == 4 ) {
2950 // retrieve element nodes
2951 const SMDS_MeshNode* aNodes [4];
2952 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
2954 while ( itN->more() )
2955 aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
2957 int aShapeId = FindShape( elem );
2958 const SMDS_MeshElement* newElem1 = 0;
2959 const SMDS_MeshElement* newElem2 = 0;
2961 newElem1 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
2962 newElem2 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
2965 newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
2966 newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
2968 myLastCreatedElems.Append(newElem1);
2969 myLastCreatedElems.Append(newElem2);
2970 // put a new triangle on the same shape and add to the same groups
2973 aMesh->SetMeshElementOnShape( newElem1, aShapeId );
2974 aMesh->SetMeshElementOnShape( newElem2, aShapeId );
2976 AddToSameGroups( newElem1, elem, aMesh );
2977 AddToSameGroups( newElem2, elem, aMesh );
2978 aMesh->RemoveElement( elem );
2981 // Quadratic quadrangle
2983 else if ( elem->NbNodes() >= 8 )
2985 // get surface elem is on
2986 int aShapeId = FindShape( elem );
2987 if ( aShapeId != helper.GetSubShapeID() ) {
2991 shape = aMesh->IndexToShape( aShapeId );
2992 if ( !shape.IsNull() && shape.ShapeType() == TopAbs_FACE ) {
2993 TopoDS_Face face = TopoDS::Face( shape );
2994 surface = BRep_Tool::Surface( face );
2995 if ( !surface.IsNull() )
2996 helper.SetSubShape( shape );
3000 const SMDS_MeshNode* aNodes [9]; aNodes[8] = 0;
3001 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3002 for ( int i = 0; itN->more(); ++i )
3003 aNodes[ i ] = static_cast<const SMDS_MeshNode*>( itN->next() );
3005 const SMDS_MeshNode* centrNode = aNodes[8];
3006 if ( centrNode == 0 )
3008 centrNode = helper.GetCentralNode( aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3009 aNodes[4], aNodes[5], aNodes[6], aNodes[7],
3011 myLastCreatedNodes.Append(centrNode);
3014 // create a new element
3015 const SMDS_MeshElement* newElem1 = 0;
3016 const SMDS_MeshElement* newElem2 = 0;
3018 newElem1 = aMesh->AddFace(aNodes[2], aNodes[3], aNodes[0],
3019 aNodes[6], aNodes[7], centrNode );
3020 newElem2 = aMesh->AddFace(aNodes[2], aNodes[0], aNodes[1],
3021 centrNode, aNodes[4], aNodes[5] );
3024 newElem1 = aMesh->AddFace(aNodes[3], aNodes[0], aNodes[1],
3025 aNodes[7], aNodes[4], centrNode );
3026 newElem2 = aMesh->AddFace(aNodes[3], aNodes[1], aNodes[2],
3027 centrNode, aNodes[5], aNodes[6] );
3029 myLastCreatedElems.Append(newElem1);
3030 myLastCreatedElems.Append(newElem2);
3031 // put a new triangle on the same shape and add to the same groups
3034 aMesh->SetMeshElementOnShape( newElem1, aShapeId );
3035 aMesh->SetMeshElementOnShape( newElem2, aShapeId );
3037 AddToSameGroups( newElem1, elem, aMesh );
3038 AddToSameGroups( newElem2, elem, aMesh );
3039 aMesh->RemoveElement( elem );
3046 //=======================================================================
3047 //function : getAngle
3049 //=======================================================================
3051 double getAngle(const SMDS_MeshElement * tr1,
3052 const SMDS_MeshElement * tr2,
3053 const SMDS_MeshNode * n1,
3054 const SMDS_MeshNode * n2)
3056 double angle = 2. * M_PI; // bad angle
3059 SMESH::Controls::TSequenceOfXYZ P1, P2;
3060 if ( !SMESH::Controls::NumericalFunctor::GetPoints( tr1, P1 ) ||
3061 !SMESH::Controls::NumericalFunctor::GetPoints( tr2, P2 ))
3064 if(!tr1->IsQuadratic())
3065 N1 = gp_Vec( P1(2) - P1(1) ) ^ gp_Vec( P1(3) - P1(1) );
3067 N1 = gp_Vec( P1(3) - P1(1) ) ^ gp_Vec( P1(5) - P1(1) );
3068 if ( N1.SquareMagnitude() <= gp::Resolution() )
3070 if(!tr2->IsQuadratic())
3071 N2 = gp_Vec( P2(2) - P2(1) ) ^ gp_Vec( P2(3) - P2(1) );
3073 N2 = gp_Vec( P2(3) - P2(1) ) ^ gp_Vec( P2(5) - P2(1) );
3074 if ( N2.SquareMagnitude() <= gp::Resolution() )
3077 // find the first diagonal node n1 in the triangles:
3078 // take in account a diagonal link orientation
3079 const SMDS_MeshElement *nFirst[2], *tr[] = { tr1, tr2 };
3080 for ( int t = 0; t < 2; t++ ) {
3081 SMDS_ElemIteratorPtr it = tr[ t ]->nodesIterator();
3082 int i = 0, iDiag = -1;
3083 while ( it->more()) {
3084 const SMDS_MeshElement *n = it->next();
3085 if ( n == n1 || n == n2 ) {
3089 if ( i - iDiag == 1 )
3090 nFirst[ t ] = ( n == n1 ? n2 : n1 );
3099 if ( nFirst[ 0 ] == nFirst[ 1 ] )
3102 angle = N1.Angle( N2 );
3107 // =================================================
3108 // class generating a unique ID for a pair of nodes
3109 // and able to return nodes by that ID
3110 // =================================================
3114 LinkID_Gen( const SMESHDS_Mesh* theMesh )
3115 :myMesh( theMesh ), myMaxID( theMesh->MaxNodeID() + 1)
3118 long GetLinkID (const SMDS_MeshNode * n1,
3119 const SMDS_MeshNode * n2) const
3121 return ( Min(n1->GetID(),n2->GetID()) * myMaxID + Max(n1->GetID(),n2->GetID()));
3124 bool GetNodes (const long theLinkID,
3125 const SMDS_MeshNode* & theNode1,
3126 const SMDS_MeshNode* & theNode2) const
3128 theNode1 = myMesh->FindNode( theLinkID / myMaxID );
3129 if ( !theNode1 ) return false;
3130 theNode2 = myMesh->FindNode( theLinkID % myMaxID );
3131 if ( !theNode2 ) return false;
3137 const SMESHDS_Mesh* myMesh;
3142 //=======================================================================
3143 //function : TriToQuad
3144 //purpose : Fuse neighbour triangles into quadrangles.
3145 // theCrit is used to select a neighbour to fuse with.
3146 // theMaxAngle is a max angle between element normals at which
3147 // fusion is still performed.
3148 //=======================================================================
3150 bool SMESH_MeshEditor::TriToQuad (TIDSortedElemSet & theElems,
3151 SMESH::Controls::NumericalFunctorPtr theCrit,
3152 const double theMaxAngle)
3154 myLastCreatedElems.Clear();
3155 myLastCreatedNodes.Clear();
3157 if ( !theCrit.get() )
3160 SMESHDS_Mesh * aMesh = GetMeshDS();
3162 // Prepare data for algo: build
3163 // 1. map of elements with their linkIDs
3164 // 2. map of linkIDs with their elements
3166 map< SMESH_TLink, list< const SMDS_MeshElement* > > mapLi_listEl;
3167 map< SMESH_TLink, list< const SMDS_MeshElement* > >::iterator itLE;
3168 map< const SMDS_MeshElement*, set< SMESH_TLink > > mapEl_setLi;
3169 map< const SMDS_MeshElement*, set< SMESH_TLink > >::iterator itEL;
3171 TIDSortedElemSet::iterator itElem;
3172 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
3174 const SMDS_MeshElement* elem = *itElem;
3175 if(!elem || elem->GetType() != SMDSAbs_Face ) continue;
3176 bool IsTria = ( elem->NbCornerNodes()==3 );
3177 if (!IsTria) continue;
3179 // retrieve element nodes
3180 const SMDS_MeshNode* aNodes [4];
3181 SMDS_NodeIteratorPtr itN = elem->nodeIterator();
3184 aNodes[ i++ ] = itN->next();
3185 aNodes[ 3 ] = aNodes[ 0 ];
3188 for ( i = 0; i < 3; i++ ) {
3189 SMESH_TLink link( aNodes[i], aNodes[i+1] );
3190 // check if elements sharing a link can be fused
3191 itLE = mapLi_listEl.find( link );
3192 if ( itLE != mapLi_listEl.end() ) {
3193 if ((*itLE).second.size() > 1 ) // consider only 2 elems adjacent by a link
3195 const SMDS_MeshElement* elem2 = (*itLE).second.front();
3196 //if ( FindShape( elem ) != FindShape( elem2 ))
3197 // continue; // do not fuse triangles laying on different shapes
3198 if ( getAngle( elem, elem2, aNodes[i], aNodes[i+1] ) > theMaxAngle )
3199 continue; // avoid making badly shaped quads
3200 (*itLE).second.push_back( elem );
3203 mapLi_listEl[ link ].push_back( elem );
3205 mapEl_setLi [ elem ].insert( link );
3208 // Clean the maps from the links shared by a sole element, ie
3209 // links to which only one element is bound in mapLi_listEl
3211 for ( itLE = mapLi_listEl.begin(); itLE != mapLi_listEl.end(); itLE++ ) {
3212 int nbElems = (*itLE).second.size();
3213 if ( nbElems < 2 ) {
3214 const SMDS_MeshElement* elem = (*itLE).second.front();
3215 SMESH_TLink link = (*itLE).first;
3216 mapEl_setLi[ elem ].erase( link );
3217 if ( mapEl_setLi[ elem ].empty() )
3218 mapEl_setLi.erase( elem );
3222 // Algo: fuse triangles into quadrangles
3224 while ( ! mapEl_setLi.empty() ) {
3225 // Look for the start element:
3226 // the element having the least nb of shared links
3227 const SMDS_MeshElement* startElem = 0;
3229 for ( itEL = mapEl_setLi.begin(); itEL != mapEl_setLi.end(); itEL++ ) {
3230 int nbLinks = (*itEL).second.size();
3231 if ( nbLinks < minNbLinks ) {
3232 startElem = (*itEL).first;
3233 minNbLinks = nbLinks;
3234 if ( minNbLinks == 1 )
3239 // search elements to fuse starting from startElem or links of elements
3240 // fused earlyer - startLinks
3241 list< SMESH_TLink > startLinks;
3242 while ( startElem || !startLinks.empty() ) {
3243 while ( !startElem && !startLinks.empty() ) {
3244 // Get an element to start, by a link
3245 SMESH_TLink linkId = startLinks.front();
3246 startLinks.pop_front();
3247 itLE = mapLi_listEl.find( linkId );
3248 if ( itLE != mapLi_listEl.end() ) {
3249 list< const SMDS_MeshElement* > & listElem = (*itLE).second;
3250 list< const SMDS_MeshElement* >::iterator itE = listElem.begin();
3251 for ( ; itE != listElem.end() ; itE++ )
3252 if ( mapEl_setLi.find( (*itE) ) != mapEl_setLi.end() )
3254 mapLi_listEl.erase( itLE );
3259 // Get candidates to be fused
3260 const SMDS_MeshElement *tr1 = startElem, *tr2 = 0, *tr3 = 0;
3261 const SMESH_TLink *link12 = 0, *link13 = 0;
3263 ASSERT( mapEl_setLi.find( tr1 ) != mapEl_setLi.end() );
3264 set< SMESH_TLink >& setLi = mapEl_setLi[ tr1 ];
3265 ASSERT( !setLi.empty() );
3266 set< SMESH_TLink >::iterator itLi;
3267 for ( itLi = setLi.begin(); itLi != setLi.end(); itLi++ )
3269 const SMESH_TLink & link = (*itLi);
3270 itLE = mapLi_listEl.find( link );
3271 if ( itLE == mapLi_listEl.end() )
3274 const SMDS_MeshElement* elem = (*itLE).second.front();
3276 elem = (*itLE).second.back();
3277 mapLi_listEl.erase( itLE );
3278 if ( mapEl_setLi.find( elem ) == mapEl_setLi.end())
3289 // add other links of elem to list of links to re-start from
3290 set< SMESH_TLink >& links = mapEl_setLi[ elem ];
3291 set< SMESH_TLink >::iterator it;
3292 for ( it = links.begin(); it != links.end(); it++ ) {
3293 const SMESH_TLink& link2 = (*it);
3294 if ( link2 != link )
3295 startLinks.push_back( link2 );
3299 // Get nodes of possible quadrangles
3300 const SMDS_MeshNode *n12 [4], *n13 [4];
3301 bool Ok12 = false, Ok13 = false;
3302 const SMDS_MeshNode *linkNode1, *linkNode2;
3304 linkNode1 = link12->first;
3305 linkNode2 = link12->second;
3306 if ( tr2 && getQuadrangleNodes( n12, linkNode1, linkNode2, tr1, tr2 ))
3310 linkNode1 = link13->first;
3311 linkNode2 = link13->second;
3312 if ( tr3 && getQuadrangleNodes( n13, linkNode1, linkNode2, tr1, tr3 ))
3316 // Choose a pair to fuse
3317 if ( Ok12 && Ok13 ) {
3318 SMDS_FaceOfNodes quad12 ( n12[ 0 ], n12[ 1 ], n12[ 2 ], n12[ 3 ] );
3319 SMDS_FaceOfNodes quad13 ( n13[ 0 ], n13[ 1 ], n13[ 2 ], n13[ 3 ] );
3320 double aBadRate12 = getBadRate( &quad12, theCrit );
3321 double aBadRate13 = getBadRate( &quad13, theCrit );
3322 if ( aBadRate13 < aBadRate12 )
3329 // and remove fused elems and remove links from the maps
3330 mapEl_setLi.erase( tr1 );
3333 mapEl_setLi.erase( tr2 );
3334 mapLi_listEl.erase( *link12 );
3335 if ( tr1->NbNodes() == 3 )
3337 const SMDS_MeshElement* newElem = 0;
3338 newElem = aMesh->AddFace(n12[0], n12[1], n12[2], n12[3] );
3339 myLastCreatedElems.Append(newElem);
3340 AddToSameGroups( newElem, tr1, aMesh );
3341 int aShapeId = tr1->getshapeId();
3343 aMesh->SetMeshElementOnShape( newElem, aShapeId );
3344 aMesh->RemoveElement( tr1 );
3345 aMesh->RemoveElement( tr2 );
3348 vector< const SMDS_MeshNode* > N1;
3349 vector< const SMDS_MeshNode* > N2;
3350 getNodesFromTwoTria(tr1,tr2,N1,N2);
3351 // now we receive following N1 and N2 (using numeration as in image in InverseDiag())
3352 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
3353 // i.e. first nodes from both arrays form a new diagonal
3354 const SMDS_MeshNode* aNodes[8];
3363 const SMDS_MeshElement* newElem = 0;
3364 if ( N1.size() == 7 || N2.size() == 7 ) // biquadratic
3365 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3366 aNodes[4], aNodes[5], aNodes[6], aNodes[7], N1[4]);
3368 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3369 aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
3370 myLastCreatedElems.Append(newElem);
3371 AddToSameGroups( newElem, tr1, aMesh );
3372 int aShapeId = tr1->getshapeId();
3374 aMesh->SetMeshElementOnShape( newElem, aShapeId );
3375 aMesh->RemoveElement( tr1 );
3376 aMesh->RemoveElement( tr2 );
3377 // remove middle node (9)
3378 if ( N1[4]->NbInverseElements() == 0 )
3379 aMesh->RemoveNode( N1[4] );
3380 if ( N1.size() == 7 && N1[6]->NbInverseElements() == 0 )
3381 aMesh->RemoveNode( N1[6] );
3382 if ( N2.size() == 7 && N2[6]->NbInverseElements() == 0 )
3383 aMesh->RemoveNode( N2[6] );
3388 mapEl_setLi.erase( tr3 );
3389 mapLi_listEl.erase( *link13 );
3390 if ( tr1->NbNodes() == 3 ) {
3391 const SMDS_MeshElement* newElem = 0;
3392 newElem = aMesh->AddFace(n13[0], n13[1], n13[2], n13[3] );
3393 myLastCreatedElems.Append(newElem);
3394 AddToSameGroups( newElem, tr1, aMesh );
3395 int aShapeId = tr1->getshapeId();
3397 aMesh->SetMeshElementOnShape( newElem, aShapeId );
3398 aMesh->RemoveElement( tr1 );
3399 aMesh->RemoveElement( tr3 );
3402 vector< const SMDS_MeshNode* > N1;
3403 vector< const SMDS_MeshNode* > N2;
3404 getNodesFromTwoTria(tr1,tr3,N1,N2);
3405 // now we receive following N1 and N2 (using numeration as above image)
3406 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
3407 // i.e. first nodes from both arrays form a new diagonal
3408 const SMDS_MeshNode* aNodes[8];
3417 const SMDS_MeshElement* newElem = 0;
3418 if ( N1.size() == 7 || N2.size() == 7 ) // biquadratic
3419 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3420 aNodes[4], aNodes[5], aNodes[6], aNodes[7], N1[4]);
3422 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3423 aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
3424 myLastCreatedElems.Append(newElem);
3425 AddToSameGroups( newElem, tr1, aMesh );
3426 int aShapeId = tr1->getshapeId();
3428 aMesh->SetMeshElementOnShape( newElem, aShapeId );
3429 aMesh->RemoveElement( tr1 );
3430 aMesh->RemoveElement( tr3 );
3431 // remove middle node (9)
3432 if ( N1[4]->NbInverseElements() == 0 )
3433 aMesh->RemoveNode( N1[4] );
3434 if ( N1.size() == 7 && N1[6]->NbInverseElements() == 0 )
3435 aMesh->RemoveNode( N1[6] );
3436 if ( N2.size() == 7 && N2[6]->NbInverseElements() == 0 )
3437 aMesh->RemoveNode( N2[6] );
3441 // Next element to fuse: the rejected one
3443 startElem = Ok12 ? tr3 : tr2;
3445 } // if ( startElem )
3446 } // while ( startElem || !startLinks.empty() )
3447 } // while ( ! mapEl_setLi.empty() )
3453 /*#define DUMPSO(txt) \
3454 // cout << txt << endl;
3455 //=============================================================================
3459 //=============================================================================
3460 static void swap( int i1, int i2, int idNodes[], gp_Pnt P[] )
3464 int tmp = idNodes[ i1 ];
3465 idNodes[ i1 ] = idNodes[ i2 ];
3466 idNodes[ i2 ] = tmp;
3467 gp_Pnt Ptmp = P[ i1 ];
3470 DUMPSO( i1 << "(" << idNodes[ i2 ] << ") <-> " << i2 << "(" << idNodes[ i1 ] << ")");
3473 //=======================================================================
3474 //function : SortQuadNodes
3475 //purpose : Set 4 nodes of a quadrangle face in a good order.
3476 // Swap 1<->2 or 2<->3 nodes and correspondingly return
3478 //=======================================================================
3480 int SMESH_MeshEditor::SortQuadNodes (const SMDS_Mesh * theMesh,
3485 for ( i = 0; i < 4; i++ ) {
3486 const SMDS_MeshNode *n = theMesh->FindNode( idNodes[i] );
3488 P[ i ].SetCoord( n->X(), n->Y(), n->Z() );
3491 gp_Vec V1(P[0], P[1]);
3492 gp_Vec V2(P[0], P[2]);
3493 gp_Vec V3(P[0], P[3]);
3495 gp_Vec Cross1 = V1 ^ V2;
3496 gp_Vec Cross2 = V2 ^ V3;
3499 if (Cross1.Dot(Cross2) < 0)
3504 if (Cross1.Dot(Cross2) < 0)
3508 swap ( i, i + 1, idNodes, P );
3510 // for ( int ii = 0; ii < 4; ii++ ) {
3511 // const SMDS_MeshNode *n = theMesh->FindNode( idNodes[ii] );
3512 // DUMPSO( ii << "(" << idNodes[ii] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
3518 //=======================================================================
3519 //function : SortHexaNodes
3520 //purpose : Set 8 nodes of a hexahedron in a good order.
3521 // Return success status
3522 //=======================================================================
3524 bool SMESH_MeshEditor::SortHexaNodes (const SMDS_Mesh * theMesh,
3529 DUMPSO( "INPUT: ========================================");
3530 for ( i = 0; i < 8; i++ ) {
3531 const SMDS_MeshNode *n = theMesh->FindNode( idNodes[i] );
3532 if ( !n ) return false;
3533 P[ i ].SetCoord( n->X(), n->Y(), n->Z() );
3534 DUMPSO( i << "(" << idNodes[i] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
3536 DUMPSO( "========================================");
3539 set<int> faceNodes; // ids of bottom face nodes, to be found
3540 set<int> checkedId1; // ids of tried 2-nd nodes
3541 Standard_Real leastDist = DBL_MAX; // dist of the 4-th node from 123 plane
3542 const Standard_Real tol = 1.e-6; // tolerance to find nodes in plane
3543 int iMin, iLoop1 = 0;
3545 // Loop to try the 2-nd nodes
3547 while ( leastDist > DBL_MIN && ++iLoop1 < 8 )
3549 // Find not checked 2-nd node
3550 for ( i = 1; i < 8; i++ )
3551 if ( checkedId1.find( idNodes[i] ) == checkedId1.end() ) {
3552 int id1 = idNodes[i];
3553 swap ( 1, i, idNodes, P );
3554 checkedId1.insert ( id1 );
3558 // Find the 3-d node so that 1-2-3 triangle to be on a hexa face,
3559 // ie that all but meybe one (id3 which is on the same face) nodes
3560 // lay on the same side from the triangle plane.
3562 bool manyInPlane = false; // more than 4 nodes lay in plane
3564 while ( ++iLoop2 < 6 ) {
3566 // get 1-2-3 plane coeffs
3567 Standard_Real A, B, C, D;
3568 gp_Vec N = gp_Vec (P[0], P[1]).Crossed( gp_Vec (P[0], P[2]) );
3569 if ( N.SquareMagnitude() > gp::Resolution() )
3571 gp_Pln pln ( P[0], N );
3572 pln.Coefficients( A, B, C, D );
3574 // find the node (iMin) closest to pln
3575 Standard_Real dist[ 8 ], minDist = DBL_MAX;
3577 for ( i = 3; i < 8; i++ ) {
3578 dist[i] = A * P[i].X() + B * P[i].Y() + C * P[i].Z() + D;
3579 if ( fabs( dist[i] ) < minDist ) {
3580 minDist = fabs( dist[i] );
3583 if ( fabs( dist[i] ) <= tol )
3584 idInPln.insert( idNodes[i] );
3587 // there should not be more than 4 nodes in bottom plane
3588 if ( idInPln.size() > 1 )
3590 DUMPSO( "### idInPln.size() = " << idInPln.size());
3591 // idInPlane does not contain the first 3 nodes
3592 if ( manyInPlane || idInPln.size() == 5)
3593 return false; // all nodes in one plane
3596 // set the 1-st node to be not in plane
3597 for ( i = 3; i < 8; i++ ) {
3598 if ( idInPln.find( idNodes[ i ] ) == idInPln.end() ) {
3599 DUMPSO( "### Reset 0-th node");
3600 swap( 0, i, idNodes, P );
3605 // reset to re-check second nodes
3606 leastDist = DBL_MAX;
3610 break; // from iLoop2;
3613 // check that the other 4 nodes are on the same side
3614 bool sameSide = true;
3615 bool isNeg = dist[ iMin == 3 ? 4 : 3 ] <= 0.;
3616 for ( i = 3; sameSide && i < 8; i++ ) {
3618 sameSide = ( isNeg == dist[i] <= 0.);
3621 // keep best solution
3622 if ( sameSide && minDist < leastDist ) {
3623 leastDist = minDist;
3625 faceNodes.insert( idNodes[ 1 ] );
3626 faceNodes.insert( idNodes[ 2 ] );
3627 faceNodes.insert( idNodes[ iMin ] );
3628 DUMPSO( "loop " << iLoop2 << " id2 " << idNodes[ 1 ] << " id3 " << idNodes[ 2 ]
3629 << " leastDist = " << leastDist);
3630 if ( leastDist <= DBL_MIN )
3635 // set next 3-d node to check
3636 int iNext = 2 + iLoop2;
3638 DUMPSO( "Try 2-nd");
3639 swap ( 2, iNext, idNodes, P );
3641 } // while ( iLoop2 < 6 )
3644 if ( faceNodes.empty() ) return false;
3646 // Put the faceNodes in proper places
3647 for ( i = 4; i < 8; i++ ) {
3648 if ( faceNodes.find( idNodes[ i ] ) != faceNodes.end() ) {
3649 // find a place to put
3651 while ( faceNodes.find( idNodes[ iTo ] ) != faceNodes.end() )
3653 DUMPSO( "Set faceNodes");
3654 swap ( iTo, i, idNodes, P );
3659 // Set nodes of the found bottom face in good order
3660 DUMPSO( " Found bottom face: ");
3661 i = SortQuadNodes( theMesh, idNodes );
3663 gp_Pnt Ptmp = P[ i ];
3668 // for ( int ii = 0; ii < 4; ii++ ) {
3669 // const SMDS_MeshNode *n = theMesh->FindNode( idNodes[ii] );
3670 // DUMPSO( ii << "(" << idNodes[ii] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
3673 // Gravity center of the top and bottom faces
3674 gp_Pnt aGCb = ( P[0].XYZ() + P[1].XYZ() + P[2].XYZ() + P[3].XYZ() ) / 4.;
3675 gp_Pnt aGCt = ( P[4].XYZ() + P[5].XYZ() + P[6].XYZ() + P[7].XYZ() ) / 4.;
3677 // Get direction from the bottom to the top face
3678 gp_Vec upDir ( aGCb, aGCt );
3679 Standard_Real upDirSize = upDir.Magnitude();
3680 if ( upDirSize <= gp::Resolution() ) return false;
3683 // Assure that the bottom face normal points up
3684 gp_Vec Nb = gp_Vec (P[0], P[1]).Crossed( gp_Vec (P[0], P[2]) );
3685 Nb += gp_Vec (P[0], P[2]).Crossed( gp_Vec (P[0], P[3]) );
3686 if ( Nb.Dot( upDir ) < 0 ) {
3687 DUMPSO( "Reverse bottom face");
3688 swap( 1, 3, idNodes, P );
3691 // Find 5-th node - the one closest to the 1-st among the last 4 nodes.
3692 Standard_Real minDist = DBL_MAX;
3693 for ( i = 4; i < 8; i++ ) {
3694 // projection of P[i] to the plane defined by P[0] and upDir
3695 gp_Pnt Pp = P[i].Translated( upDir * ( upDir.Dot( gp_Vec( P[i], P[0] ))));
3696 Standard_Real sqDist = P[0].SquareDistance( Pp );
3697 if ( sqDist < minDist ) {
3702 DUMPSO( "Set 4-th");
3703 swap ( 4, iMin, idNodes, P );
3705 // Set nodes of the top face in good order
3706 DUMPSO( "Sort top face");
3707 i = SortQuadNodes( theMesh, &idNodes[4] );
3710 gp_Pnt Ptmp = P[ i ];
3715 // Assure that direction of the top face normal is from the bottom face
3716 gp_Vec Nt = gp_Vec (P[4], P[5]).Crossed( gp_Vec (P[4], P[6]) );
3717 Nt += gp_Vec (P[4], P[6]).Crossed( gp_Vec (P[4], P[7]) );
3718 if ( Nt.Dot( upDir ) < 0 ) {
3719 DUMPSO( "Reverse top face");
3720 swap( 5, 7, idNodes, P );
3723 // DUMPSO( "OUTPUT: ========================================");
3724 // for ( i = 0; i < 8; i++ ) {
3725 // float *p = ugrid->GetPoint(idNodes[i]);
3726 // DUMPSO( i << "(" << idNodes[i] << ") : " << p[0] << " " << p[1] << " " << p[2]);
3732 //================================================================================
3734 * \brief Return nodes linked to the given one
3735 * \param theNode - the node
3736 * \param linkedNodes - the found nodes
3737 * \param type - the type of elements to check
3739 * Medium nodes are ignored
3741 //================================================================================
3743 void SMESH_MeshEditor::GetLinkedNodes( const SMDS_MeshNode* theNode,
3744 TIDSortedElemSet & linkedNodes,
3745 SMDSAbs_ElementType type )
3747 SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(type);
3748 while ( elemIt->more() )
3750 const SMDS_MeshElement* elem = elemIt->next();
3751 if(elem->GetType() == SMDSAbs_0DElement)
3754 SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
3755 if ( elem->GetType() == SMDSAbs_Volume )
3757 SMDS_VolumeTool vol( elem );
3758 while ( nodeIt->more() ) {
3759 const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
3760 if ( theNode != n && vol.IsLinked( theNode, n ))
3761 linkedNodes.insert( n );
3766 for ( int i = 0; nodeIt->more(); ++i ) {
3767 const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
3768 if ( n == theNode ) {
3769 int iBefore = i - 1;
3771 if ( elem->IsQuadratic() ) {
3772 int nb = elem->NbNodes() / 2;
3773 iAfter = SMESH_MesherHelper::WrapIndex( iAfter, nb );
3774 iBefore = SMESH_MesherHelper::WrapIndex( iBefore, nb );
3776 linkedNodes.insert( elem->GetNodeWrap( iAfter ));
3777 linkedNodes.insert( elem->GetNodeWrap( iBefore ));
3784 //=======================================================================
3785 //function : laplacianSmooth
3786 //purpose : pulls theNode toward the center of surrounding nodes directly
3787 // connected to that node along an element edge
3788 //=======================================================================
3790 void laplacianSmooth(const SMDS_MeshNode* theNode,
3791 const Handle(Geom_Surface)& theSurface,
3792 map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
3794 // find surrounding nodes
3796 TIDSortedElemSet nodeSet;
3797 SMESH_MeshEditor::GetLinkedNodes( theNode, nodeSet, SMDSAbs_Face );
3799 // compute new coodrs
3801 double coord[] = { 0., 0., 0. };
3802 TIDSortedElemSet::iterator nodeSetIt = nodeSet.begin();
3803 for ( ; nodeSetIt != nodeSet.end(); nodeSetIt++ ) {
3804 const SMDS_MeshNode* node = cast2Node(*nodeSetIt);
3805 if ( theSurface.IsNull() ) { // smooth in 3D
3806 coord[0] += node->X();
3807 coord[1] += node->Y();
3808 coord[2] += node->Z();
3810 else { // smooth in 2D
3811 ASSERT( theUVMap.find( node ) != theUVMap.end() );
3812 gp_XY* uv = theUVMap[ node ];
3813 coord[0] += uv->X();
3814 coord[1] += uv->Y();
3817 int nbNodes = nodeSet.size();
3820 coord[0] /= nbNodes;
3821 coord[1] /= nbNodes;
3823 if ( !theSurface.IsNull() ) {
3824 ASSERT( theUVMap.find( theNode ) != theUVMap.end() );
3825 theUVMap[ theNode ]->SetCoord( coord[0], coord[1] );
3826 gp_Pnt p3d = theSurface->Value( coord[0], coord[1] );
3832 coord[2] /= nbNodes;
3836 const_cast< SMDS_MeshNode* >( theNode )->setXYZ(coord[0],coord[1],coord[2]);
3839 //=======================================================================
3840 //function : centroidalSmooth
3841 //purpose : pulls theNode toward the element-area-weighted centroid of the
3842 // surrounding elements
3843 //=======================================================================
3845 void centroidalSmooth(const SMDS_MeshNode* theNode,
3846 const Handle(Geom_Surface)& theSurface,
3847 map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
3849 gp_XYZ aNewXYZ(0.,0.,0.);
3850 SMESH::Controls::Area anAreaFunc;
3851 double totalArea = 0.;
3856 SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(SMDSAbs_Face);
3857 while ( elemIt->more() )
3859 const SMDS_MeshElement* elem = elemIt->next();
3862 gp_XYZ elemCenter(0.,0.,0.);
3863 SMESH::Controls::TSequenceOfXYZ aNodePoints;
3864 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3865 int nn = elem->NbNodes();
3866 if(elem->IsQuadratic()) nn = nn/2;
3868 //while ( itN->more() ) {
3870 const SMDS_MeshNode* aNode = static_cast<const SMDS_MeshNode*>( itN->next() );
3872 gp_XYZ aP( aNode->X(), aNode->Y(), aNode->Z() );
3873 aNodePoints.push_back( aP );
3874 if ( !theSurface.IsNull() ) { // smooth in 2D
3875 ASSERT( theUVMap.find( aNode ) != theUVMap.end() );
3876 gp_XY* uv = theUVMap[ aNode ];
3877 aP.SetCoord( uv->X(), uv->Y(), 0. );
3881 double elemArea = anAreaFunc.GetValue( aNodePoints );
3882 totalArea += elemArea;
3884 aNewXYZ += elemCenter * elemArea;
3886 aNewXYZ /= totalArea;
3887 if ( !theSurface.IsNull() ) {
3888 theUVMap[ theNode ]->SetCoord( aNewXYZ.X(), aNewXYZ.Y() );
3889 aNewXYZ = theSurface->Value( aNewXYZ.X(), aNewXYZ.Y() ).XYZ();
3894 const_cast< SMDS_MeshNode* >( theNode )->setXYZ(aNewXYZ.X(),aNewXYZ.Y(),aNewXYZ.Z());
3897 //=======================================================================
3898 //function : getClosestUV
3899 //purpose : return UV of closest projection
3900 //=======================================================================
3902 static bool getClosestUV (Extrema_GenExtPS& projector,
3903 const gp_Pnt& point,
3906 projector.Perform( point );
3907 if ( projector.IsDone() ) {
3908 double u, v, minVal = DBL_MAX;
3909 for ( int i = projector.NbExt(); i > 0; i-- )
3910 if ( projector.SquareDistance( i ) < minVal ) {
3911 minVal = projector.SquareDistance( i );
3912 projector.Point( i ).Parameter( u, v );
3914 result.SetCoord( u, v );
3920 //=======================================================================
3922 //purpose : Smooth theElements during theNbIterations or until a worst
3923 // element has aspect ratio <= theTgtAspectRatio.
3924 // Aspect Ratio varies in range [1.0, inf].
3925 // If theElements is empty, the whole mesh is smoothed.
3926 // theFixedNodes contains additionally fixed nodes. Nodes built
3927 // on edges and boundary nodes are always fixed.
3928 //=======================================================================
3930 void SMESH_MeshEditor::Smooth (TIDSortedElemSet & theElems,
3931 set<const SMDS_MeshNode*> & theFixedNodes,
3932 const SmoothMethod theSmoothMethod,
3933 const int theNbIterations,
3934 double theTgtAspectRatio,
3937 myLastCreatedElems.Clear();
3938 myLastCreatedNodes.Clear();
3940 if ( theTgtAspectRatio < 1.0 )
3941 theTgtAspectRatio = 1.0;
3943 const double disttol = 1.e-16;
3945 SMESH::Controls::AspectRatio aQualityFunc;
3947 SMESHDS_Mesh* aMesh = GetMeshDS();
3949 if ( theElems.empty() ) {
3950 // add all faces to theElems
3951 SMDS_FaceIteratorPtr fIt = aMesh->facesIterator();
3952 while ( fIt->more() ) {
3953 const SMDS_MeshElement* face = fIt->next();
3954 theElems.insert( theElems.end(), face );
3957 // get all face ids theElems are on
3958 set< int > faceIdSet;
3959 TIDSortedElemSet::iterator itElem;
3961 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
3962 int fId = FindShape( *itElem );
3963 // check that corresponding submesh exists and a shape is face
3965 faceIdSet.find( fId ) == faceIdSet.end() &&
3966 aMesh->MeshElements( fId )) {
3967 TopoDS_Shape F = aMesh->IndexToShape( fId );
3968 if ( !F.IsNull() && F.ShapeType() == TopAbs_FACE )
3969 faceIdSet.insert( fId );
3972 faceIdSet.insert( 0 ); // to smooth elements that are not on any TopoDS_Face
3974 // ===============================================
3975 // smooth elements on each TopoDS_Face separately
3976 // ===============================================
3978 SMESH_MesherHelper helper( *GetMesh() );
3980 set< int >::reverse_iterator fId = faceIdSet.rbegin(); // treat 0 fId at the end
3981 for ( ; fId != faceIdSet.rend(); ++fId )
3983 // get face surface and submesh
3984 Handle(Geom_Surface) surface;
3985 SMESHDS_SubMesh* faceSubMesh = 0;
3988 double u1 = 0, u2 = 0, v1 = 0, v2 = 0;
3989 bool isUPeriodic = false, isVPeriodic = false;
3992 face = TopoDS::Face( aMesh->IndexToShape( *fId ));
3993 surface = BRep_Tool::Surface( face );
3994 faceSubMesh = aMesh->MeshElements( *fId );
3995 fToler2 = BRep_Tool::Tolerance( face );
3996 fToler2 *= fToler2 * 10.;
3997 isUPeriodic = surface->IsUPeriodic();
3998 // if ( isUPeriodic )
3999 // surface->UPeriod();
4000 isVPeriodic = surface->IsVPeriodic();
4001 // if ( isVPeriodic )
4002 // surface->VPeriod();
4003 surface->Bounds( u1, u2, v1, v2 );
4004 helper.SetSubShape( face );
4006 // ---------------------------------------------------------
4007 // for elements on a face, find movable and fixed nodes and
4008 // compute UV for them
4009 // ---------------------------------------------------------
4010 bool checkBoundaryNodes = false;
4011 bool isQuadratic = false;
4012 set<const SMDS_MeshNode*> setMovableNodes;
4013 map< const SMDS_MeshNode*, gp_XY* > uvMap, uvMap2;
4014 list< gp_XY > listUV; // uvs the 2 uvMaps refer to
4015 list< const SMDS_MeshElement* > elemsOnFace;
4017 Extrema_GenExtPS projector;
4018 GeomAdaptor_Surface surfAdaptor;
4019 if ( !surface.IsNull() ) {
4020 surfAdaptor.Load( surface );
4021 projector.Initialize( surfAdaptor, 20,20, 1e-5,1e-5 );
4023 int nbElemOnFace = 0;
4024 itElem = theElems.begin();
4025 // loop on not yet smoothed elements: look for elems on a face
4026 while ( itElem != theElems.end() )
4028 if ( faceSubMesh && nbElemOnFace == faceSubMesh->NbElements() )
4029 break; // all elements found
4031 const SMDS_MeshElement* elem = *itElem;
4032 if ( !elem || elem->GetType() != SMDSAbs_Face || elem->NbNodes() < 3 ||
4033 ( faceSubMesh && !faceSubMesh->Contains( elem ))) {
4037 elemsOnFace.push_back( elem );
4038 theElems.erase( itElem++ );
4042 isQuadratic = elem->IsQuadratic();
4044 // get movable nodes of elem
4045 const SMDS_MeshNode* node;
4046 SMDS_TypeOfPosition posType;
4047 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
4048 int nn = 0, nbn = elem->NbNodes();
4049 if(elem->IsQuadratic())
4051 while ( nn++ < nbn ) {
4052 node = static_cast<const SMDS_MeshNode*>( itN->next() );
4053 const SMDS_PositionPtr& pos = node->GetPosition();
4054 posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
4055 if (posType != SMDS_TOP_EDGE &&
4056 posType != SMDS_TOP_VERTEX &&
4057 theFixedNodes.find( node ) == theFixedNodes.end())
4059 // check if all faces around the node are on faceSubMesh
4060 // because a node on edge may be bound to face
4062 if ( faceSubMesh ) {
4063 SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
4064 while ( eIt->more() && all ) {
4065 const SMDS_MeshElement* e = eIt->next();
4066 all = faceSubMesh->Contains( e );
4070 setMovableNodes.insert( node );
4072 checkBoundaryNodes = true;
4074 if ( posType == SMDS_TOP_3DSPACE )
4075 checkBoundaryNodes = true;
4078 if ( surface.IsNull() )
4081 // get nodes to check UV
4082 list< const SMDS_MeshNode* > uvCheckNodes;
4083 const SMDS_MeshNode* nodeInFace = 0;
4084 itN = elem->nodesIterator();
4085 nn = 0; nbn = elem->NbNodes();
4086 if(elem->IsQuadratic())
4088 while ( nn++ < nbn ) {
4089 node = static_cast<const SMDS_MeshNode*>( itN->next() );
4090 if ( node->GetPosition()->GetDim() == 2 )
4092 if ( uvMap.find( node ) == uvMap.end() )
4093 uvCheckNodes.push_back( node );
4094 // add nodes of elems sharing node
4095 // SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
4096 // while ( eIt->more() ) {
4097 // const SMDS_MeshElement* e = eIt->next();
4098 // if ( e != elem ) {
4099 // SMDS_ElemIteratorPtr nIt = e->nodesIterator();
4100 // while ( nIt->more() ) {
4101 // const SMDS_MeshNode* n =
4102 // static_cast<const SMDS_MeshNode*>( nIt->next() );
4103 // if ( uvMap.find( n ) == uvMap.end() )
4104 // uvCheckNodes.push_back( n );
4110 list< const SMDS_MeshNode* >::iterator n = uvCheckNodes.begin();
4111 for ( ; n != uvCheckNodes.end(); ++n ) {
4114 const SMDS_PositionPtr& pos = node->GetPosition();
4115 posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
4119 bool toCheck = true;
4120 uv = helper.GetNodeUV( face, node, nodeInFace, &toCheck );
4122 // compute not existing UV
4123 bool project = ( posType == SMDS_TOP_3DSPACE );
4124 // double dist1 = DBL_MAX, dist2 = 0;
4125 // if ( posType != SMDS_TOP_3DSPACE ) {
4126 // dist1 = pNode.SquareDistance( surface->Value( uv.X(), uv.Y() ));
4127 // project = dist1 > fToler2;
4129 if ( project ) { // compute new UV
4131 gp_Pnt pNode = SMESH_TNodeXYZ( node );
4132 if ( !getClosestUV( projector, pNode, newUV )) {
4133 MESSAGE("Node Projection Failed " << node);
4137 newUV.SetX( ElCLib::InPeriod( newUV.X(), u1, u2 ));
4139 newUV.SetY( ElCLib::InPeriod( newUV.Y(), v1, v2 ));
4141 // if ( posType != SMDS_TOP_3DSPACE )
4142 // dist2 = pNode.SquareDistance( surface->Value( newUV.X(), newUV.Y() ));
4143 // if ( dist2 < dist1 )
4147 // store UV in the map
4148 listUV.push_back( uv );
4149 uvMap.insert( make_pair( node, &listUV.back() ));
4151 } // loop on not yet smoothed elements
4153 if ( !faceSubMesh || nbElemOnFace != faceSubMesh->NbElements() )
4154 checkBoundaryNodes = true;
4156 // fix nodes on mesh boundary
4158 if ( checkBoundaryNodes ) {
4159 map< SMESH_TLink, int > linkNbMap; // how many times a link encounters in elemsOnFace
4160 map< SMESH_TLink, int >::iterator link_nb;
4161 // put all elements links to linkNbMap
4162 list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4163 for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
4164 const SMDS_MeshElement* elem = (*elemIt);
4165 int nbn = elem->NbCornerNodes();
4166 // loop on elem links: insert them in linkNbMap
4167 for ( int iN = 0; iN < nbn; ++iN ) {
4168 const SMDS_MeshNode* n1 = elem->GetNode( iN );
4169 const SMDS_MeshNode* n2 = elem->GetNode(( iN+1 ) % nbn);
4170 SMESH_TLink link( n1, n2 );
4171 link_nb = linkNbMap.insert( make_pair( link, 0 )).first;
4175 // remove nodes that are in links encountered only once from setMovableNodes
4176 for ( link_nb = linkNbMap.begin(); link_nb != linkNbMap.end(); ++link_nb ) {
4177 if ( link_nb->second == 1 ) {
4178 setMovableNodes.erase( link_nb->first.node1() );
4179 setMovableNodes.erase( link_nb->first.node2() );
4184 // -----------------------------------------------------
4185 // for nodes on seam edge, compute one more UV ( uvMap2 );
4186 // find movable nodes linked to nodes on seam and which
4187 // are to be smoothed using the second UV ( uvMap2 )
4188 // -----------------------------------------------------
4190 set<const SMDS_MeshNode*> nodesNearSeam; // to smooth using uvMap2
4191 if ( !surface.IsNull() ) {
4192 TopExp_Explorer eExp( face, TopAbs_EDGE );
4193 for ( ; eExp.More(); eExp.Next() ) {
4194 TopoDS_Edge edge = TopoDS::Edge( eExp.Current() );
4195 if ( !BRep_Tool::IsClosed( edge, face ))
4197 SMESHDS_SubMesh* sm = aMesh->MeshElements( edge );
4198 if ( !sm ) continue;
4199 // find out which parameter varies for a node on seam
4202 Handle(Geom2d_Curve) pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
4203 if ( pcurve.IsNull() ) continue;
4204 uv1 = pcurve->Value( f );
4206 pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
4207 if ( pcurve.IsNull() ) continue;
4208 uv2 = pcurve->Value( f );
4209 int iPar = Abs( uv1.X() - uv2.X() ) > Abs( uv1.Y() - uv2.Y() ) ? 1 : 2;
4211 if ( uv1.Coord( iPar ) > uv2.Coord( iPar ))
4212 std::swap( uv1, uv2 );
4213 // get nodes on seam and its vertices
4214 list< const SMDS_MeshNode* > seamNodes;
4215 SMDS_NodeIteratorPtr nSeamIt = sm->GetNodes();
4216 while ( nSeamIt->more() ) {
4217 const SMDS_MeshNode* node = nSeamIt->next();
4218 if ( !isQuadratic || !IsMedium( node ))
4219 seamNodes.push_back( node );
4221 TopExp_Explorer vExp( edge, TopAbs_VERTEX );
4222 for ( ; vExp.More(); vExp.Next() ) {
4223 sm = aMesh->MeshElements( vExp.Current() );
4225 nSeamIt = sm->GetNodes();
4226 while ( nSeamIt->more() )
4227 seamNodes.push_back( nSeamIt->next() );
4230 // loop on nodes on seam
4231 list< const SMDS_MeshNode* >::iterator noSeIt = seamNodes.begin();
4232 for ( ; noSeIt != seamNodes.end(); ++noSeIt ) {
4233 const SMDS_MeshNode* nSeam = *noSeIt;
4234 map< const SMDS_MeshNode*, gp_XY* >::iterator n_uv = uvMap.find( nSeam );
4235 if ( n_uv == uvMap.end() )
4238 n_uv->second->SetCoord( iPar, uv1.Coord( iPar ));
4239 // set the second UV
4240 listUV.push_back( *n_uv->second );
4241 listUV.back().SetCoord( iPar, uv2.Coord( iPar ));
4242 if ( uvMap2.empty() )
4243 uvMap2 = uvMap; // copy the uvMap contents
4244 uvMap2[ nSeam ] = &listUV.back();
4246 // collect movable nodes linked to ones on seam in nodesNearSeam
4247 SMDS_ElemIteratorPtr eIt = nSeam->GetInverseElementIterator(SMDSAbs_Face);
4248 while ( eIt->more() ) {
4249 const SMDS_MeshElement* e = eIt->next();
4250 int nbUseMap1 = 0, nbUseMap2 = 0;
4251 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
4252 int nn = 0, nbn = e->NbNodes();
4253 if(e->IsQuadratic()) nbn = nbn/2;
4254 while ( nn++ < nbn )
4256 const SMDS_MeshNode* n =
4257 static_cast<const SMDS_MeshNode*>( nIt->next() );
4259 setMovableNodes.find( n ) == setMovableNodes.end() )
4261 // add only nodes being closer to uv2 than to uv1
4262 // gp_Pnt pMid (0.5 * ( n->X() + nSeam->X() ),
4263 // 0.5 * ( n->Y() + nSeam->Y() ),
4264 // 0.5 * ( n->Z() + nSeam->Z() ));
4266 // getClosestUV( projector, pMid, uv );
4267 double x = uvMap[ n ]->Coord( iPar );
4268 if ( Abs( uv1.Coord( iPar ) - x ) >
4269 Abs( uv2.Coord( iPar ) - x )) {
4270 nodesNearSeam.insert( n );
4276 // for centroidalSmooth all element nodes must
4277 // be on one side of a seam
4278 if ( theSmoothMethod == CENTROIDAL && nbUseMap1 && nbUseMap2 ) {
4279 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
4281 while ( nn++ < nbn ) {
4282 const SMDS_MeshNode* n =
4283 static_cast<const SMDS_MeshNode*>( nIt->next() );
4284 setMovableNodes.erase( n );
4288 } // loop on nodes on seam
4289 } // loop on edge of a face
4290 } // if ( !face.IsNull() )
4292 if ( setMovableNodes.empty() ) {
4293 MESSAGE( "Face id : " << *fId << " - NO SMOOTHING: no nodes to move!!!");
4294 continue; // goto next face
4302 double maxRatio = -1., maxDisplacement = -1.;
4303 set<const SMDS_MeshNode*>::iterator nodeToMove;
4304 for ( it = 0; it < theNbIterations; it++ ) {
4305 maxDisplacement = 0.;
4306 nodeToMove = setMovableNodes.begin();
4307 for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
4308 const SMDS_MeshNode* node = (*nodeToMove);
4309 gp_XYZ aPrevPos ( node->X(), node->Y(), node->Z() );
4312 bool map2 = ( nodesNearSeam.find( node ) != nodesNearSeam.end() );
4313 if ( theSmoothMethod == LAPLACIAN )
4314 laplacianSmooth( node, surface, map2 ? uvMap2 : uvMap );
4316 centroidalSmooth( node, surface, map2 ? uvMap2 : uvMap );
4318 // node displacement
4319 gp_XYZ aNewPos ( node->X(), node->Y(), node->Z() );
4320 Standard_Real aDispl = (aPrevPos - aNewPos).SquareModulus();
4321 if ( aDispl > maxDisplacement )
4322 maxDisplacement = aDispl;
4324 // no node movement => exit
4325 //if ( maxDisplacement < 1.e-16 ) {
4326 if ( maxDisplacement < disttol ) {
4327 MESSAGE("-- no node movement --");
4331 // check elements quality
4333 list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4334 for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
4335 const SMDS_MeshElement* elem = (*elemIt);
4336 if ( !elem || elem->GetType() != SMDSAbs_Face )
4338 SMESH::Controls::TSequenceOfXYZ aPoints;
4339 if ( aQualityFunc.GetPoints( elem, aPoints )) {
4340 double aValue = aQualityFunc.GetValue( aPoints );
4341 if ( aValue > maxRatio )
4345 if ( maxRatio <= theTgtAspectRatio ) {
4346 //MESSAGE("-- quality achieved --");
4349 if (it+1 == theNbIterations) {
4350 //MESSAGE("-- Iteration limit exceeded --");
4352 } // smoothing iterations
4354 // MESSAGE(" Face id: " << *fId <<
4355 // " Nb iterstions: " << it <<
4356 // " Displacement: " << maxDisplacement <<
4357 // " Aspect Ratio " << maxRatio);
4359 // ---------------------------------------
4360 // new nodes positions are computed,
4361 // record movement in DS and set new UV
4362 // ---------------------------------------
4363 nodeToMove = setMovableNodes.begin();
4364 for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
4365 SMDS_MeshNode* node = const_cast< SMDS_MeshNode* > (*nodeToMove);
4366 aMesh->MoveNode( node, node->X(), node->Y(), node->Z() );
4367 map< const SMDS_MeshNode*, gp_XY* >::iterator node_uv = uvMap.find( node );
4368 if ( node_uv != uvMap.end() ) {
4369 gp_XY* uv = node_uv->second;
4371 ( SMDS_PositionPtr( new SMDS_FacePosition( uv->X(), uv->Y() )));
4375 // move medium nodes of quadratic elements
4378 vector<const SMDS_MeshNode*> nodes;
4380 list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4381 for ( ; elemIt != elemsOnFace.end(); ++elemIt )
4383 const SMDS_MeshElement* QF = *elemIt;
4384 if ( QF->IsQuadratic() )
4386 nodes.assign( SMDS_MeshElement::iterator( QF->interlacedNodesElemIterator() ),
4387 SMDS_MeshElement::iterator() );
4388 nodes.push_back( nodes[0] );
4390 for (size_t i = 1; i < nodes.size(); i += 2 ) // i points to a medium node
4392 if ( !surface.IsNull() )
4394 gp_XY uv1 = helper.GetNodeUV( face, nodes[i-1], nodes[i+1], &checkUV );
4395 gp_XY uv2 = helper.GetNodeUV( face, nodes[i+1], nodes[i-1], &checkUV );
4396 gp_XY uv = helper.GetMiddleUV( surface, uv1, uv2 );
4397 xyz = surface->Value( uv.X(), uv.Y() );
4400 xyz = 0.5 * ( SMESH_TNodeXYZ( nodes[i-1] ) + SMESH_TNodeXYZ( nodes[i+1] ));
4402 if (( SMESH_TNodeXYZ( nodes[i] ) - xyz.XYZ() ).Modulus() > disttol )
4403 // we have to move a medium node
4404 aMesh->MoveNode( nodes[i], xyz.X(), xyz.Y(), xyz.Z() );
4410 } // loop on face ids
4416 //=======================================================================
4417 //function : isReverse
4418 //purpose : Return true if normal of prevNodes is not co-directied with
4419 // gp_Vec(prevNodes[iNotSame],nextNodes[iNotSame]).
4420 // iNotSame is where prevNodes and nextNodes are different.
4421 // If result is true then future volume orientation is OK
4422 //=======================================================================
4424 bool isReverse(const SMDS_MeshElement* face,
4425 const vector<const SMDS_MeshNode*>& prevNodes,
4426 const vector<const SMDS_MeshNode*>& nextNodes,
4430 SMESH_TNodeXYZ pP = prevNodes[ iNotSame ];
4431 SMESH_TNodeXYZ pN = nextNodes[ iNotSame ];
4432 gp_XYZ extrDir( pN - pP ), faceNorm;
4433 SMESH_MeshAlgos::FaceNormal( face, faceNorm, /*normalized=*/false );
4435 return faceNorm * extrDir < 0.0;
4438 //================================================================================
4440 * \brief Assure that theElemSets[0] holds elements, not nodes
4442 //================================================================================
4444 void setElemsFirst( TIDSortedElemSet theElemSets[2] )
4446 if ( !theElemSets[0].empty() &&
4447 (*theElemSets[0].begin())->GetType() == SMDSAbs_Node )
4449 std::swap( theElemSets[0], theElemSets[1] );
4451 else if ( !theElemSets[1].empty() &&
4452 (*theElemSets[1].begin())->GetType() != SMDSAbs_Node )
4454 std::swap( theElemSets[0], theElemSets[1] );
4459 //=======================================================================
4461 * \brief Create elements by sweeping an element
4462 * \param elem - element to sweep
4463 * \param newNodesItVec - nodes generated from each node of the element
4464 * \param newElems - generated elements
4465 * \param nbSteps - number of sweeping steps
4466 * \param srcElements - to append elem for each generated element
4468 //=======================================================================
4470 void SMESH_MeshEditor::sweepElement(const SMDS_MeshElement* elem,
4471 const vector<TNodeOfNodeListMapItr> & newNodesItVec,
4472 list<const SMDS_MeshElement*>& newElems,
4473 const size_t nbSteps,
4474 SMESH_SequenceOfElemPtr& srcElements)
4476 SMESHDS_Mesh* aMesh = GetMeshDS();
4478 const int nbNodes = elem->NbNodes();
4479 const int nbCorners = elem->NbCornerNodes();
4480 SMDSAbs_EntityType baseType = elem->GetEntityType(); /* it can change in case of
4481 polyhedron creation !!! */
4482 // Loop on elem nodes:
4483 // find new nodes and detect same nodes indices
4484 vector < list< const SMDS_MeshNode* >::const_iterator > itNN( nbNodes );
4485 vector<const SMDS_MeshNode*> prevNod( nbNodes );
4486 vector<const SMDS_MeshNode*> nextNod( nbNodes );
4487 vector<const SMDS_MeshNode*> midlNod( nbNodes );
4489 int iNode, nbSame = 0, nbDouble = 0, iNotSameNode = 0;
4490 vector<int> sames(nbNodes);
4491 vector<bool> isSingleNode(nbNodes);
4493 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4494 TNodeOfNodeListMapItr nnIt = newNodesItVec[ iNode ];
4495 const SMDS_MeshNode* node = nnIt->first;
4496 const list< const SMDS_MeshNode* > & listNewNodes = nnIt->second;
4497 if ( listNewNodes.empty() )
4500 itNN [ iNode ] = listNewNodes.begin();
4501 prevNod[ iNode ] = node;
4502 nextNod[ iNode ] = listNewNodes.front();
4504 isSingleNode[iNode] = (listNewNodes.size()==nbSteps); /* medium node of quadratic or
4505 corner node of linear */
4506 if ( prevNod[ iNode ] != nextNod [ iNode ])
4507 nbDouble += !isSingleNode[iNode];
4509 if( iNode < nbCorners ) { // check corners only
4510 if ( prevNod[ iNode ] == nextNod [ iNode ])
4511 sames[nbSame++] = iNode;
4513 iNotSameNode = iNode;
4517 if ( nbSame == nbNodes || nbSame > 2) {
4518 MESSAGE( " Too many same nodes of element " << elem->GetID() );
4522 if ( elem->GetType() == SMDSAbs_Face && !isReverse( elem, prevNod, nextNod, iNotSameNode ))
4524 // fix nodes order to have bottom normal external
4525 if ( baseType == SMDSEntity_Polygon )
4527 std::reverse( itNN.begin(), itNN.end() );
4528 std::reverse( prevNod.begin(), prevNod.end() );
4529 std::reverse( midlNod.begin(), midlNod.end() );
4530 std::reverse( nextNod.begin(), nextNod.end() );
4531 std::reverse( isSingleNode.begin(), isSingleNode.end() );
4535 const vector<int>& ind = SMDS_MeshCell::reverseSmdsOrder( baseType, nbNodes );
4536 SMDS_MeshCell::applyInterlace( ind, itNN );
4537 SMDS_MeshCell::applyInterlace( ind, prevNod );
4538 SMDS_MeshCell::applyInterlace( ind, nextNod );
4539 SMDS_MeshCell::applyInterlace( ind, midlNod );
4540 SMDS_MeshCell::applyInterlace( ind, isSingleNode );
4543 sames[nbSame] = iNotSameNode;
4544 for ( int j = 0; j <= nbSame; ++j )
4545 for ( size_t i = 0; i < ind.size(); ++i )
4546 if ( ind[i] == sames[j] )
4551 iNotSameNode = sames[nbSame];
4555 else if ( elem->GetType() == SMDSAbs_Edge )
4557 // orient a new face same as adjacent one
4559 const SMDS_MeshElement* e;
4560 TIDSortedElemSet dummy;
4561 if (( e = SMESH_MeshAlgos::FindFaceInSet( nextNod[0], prevNod[0], dummy,dummy, &i1, &i2 )) ||
4562 ( e = SMESH_MeshAlgos::FindFaceInSet( prevNod[1], nextNod[1], dummy,dummy, &i1, &i2 )) ||
4563 ( e = SMESH_MeshAlgos::FindFaceInSet( prevNod[0], prevNod[1], dummy,dummy, &i1, &i2 )))
4565 // there is an adjacent face, check order of nodes in it
4566 bool sameOrder = ( Abs( i2 - i1 ) == 1 ) ? ( i2 > i1 ) : ( i2 < i1 );
4569 std::swap( itNN[0], itNN[1] );
4570 std::swap( prevNod[0], prevNod[1] );
4571 std::swap( nextNod[0], nextNod[1] );
4572 #if defined(__APPLE__)
4573 std::swap( isSingleNode[0], isSingleNode[1] );
4575 isSingleNode.swap( isSingleNode[0], isSingleNode[1] );
4578 sames[0] = 1 - sames[0];
4579 iNotSameNode = 1 - iNotSameNode;
4584 int iSameNode = 0, iBeforeSame = 0, iAfterSame = 0, iOpposSame = 0;
4586 iSameNode = sames[ nbSame-1 ];
4587 iBeforeSame = ( iSameNode + nbCorners - 1 ) % nbCorners;
4588 iAfterSame = ( iSameNode + 1 ) % nbCorners;
4589 iOpposSame = ( iSameNode - 2 < 0 ? iSameNode + 2 : iSameNode - 2 );
4592 if ( baseType == SMDSEntity_Polygon )
4594 if ( nbNodes == 3 ) baseType = SMDSEntity_Triangle;
4595 else if ( nbNodes == 4 ) baseType = SMDSEntity_Quadrangle;
4597 else if ( baseType == SMDSEntity_Quad_Polygon )
4599 if ( nbNodes == 6 ) baseType = SMDSEntity_Quad_Triangle;
4600 else if ( nbNodes == 8 ) baseType = SMDSEntity_Quad_Quadrangle;
4603 // make new elements
4604 for ( size_t iStep = 0; iStep < nbSteps; iStep++ )
4607 for ( iNode = 0; iNode < nbNodes; iNode++ )
4609 midlNod[ iNode ] = isSingleNode[iNode] ? 0 : *itNN[ iNode ]++;
4610 nextNod[ iNode ] = *itNN[ iNode ]++;
4613 SMDS_MeshElement* aNewElem = 0;
4614 /*if(!elem->IsPoly())*/ {
4615 switch ( baseType ) {
4617 case SMDSEntity_Node: { // sweep NODE
4618 if ( nbSame == 0 ) {
4619 if ( isSingleNode[0] )
4620 aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ] );
4622 aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ], midlNod[ 0 ] );
4628 case SMDSEntity_Edge: { // sweep EDGE
4629 if ( nbDouble == 0 )
4631 if ( nbSame == 0 ) // ---> quadrangle
4632 aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
4633 nextNod[ 1 ], nextNod[ 0 ] );
4634 else // ---> triangle
4635 aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
4636 nextNod[ iNotSameNode ] );
4638 else // ---> polygon
4640 vector<const SMDS_MeshNode*> poly_nodes;
4641 poly_nodes.push_back( prevNod[0] );
4642 poly_nodes.push_back( prevNod[1] );
4643 if ( prevNod[1] != nextNod[1] )
4645 if ( midlNod[1]) poly_nodes.push_back( midlNod[1]);
4646 poly_nodes.push_back( nextNod[1] );
4648 if ( prevNod[0] != nextNod[0] )
4650 poly_nodes.push_back( nextNod[0] );
4651 if ( midlNod[0]) poly_nodes.push_back( midlNod[0]);
4653 switch ( poly_nodes.size() ) {
4655 aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ], poly_nodes[ 2 ]);
4658 aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ],
4659 poly_nodes[ 2 ], poly_nodes[ 3 ]);
4662 aNewElem = aMesh->AddPolygonalFace (poly_nodes);
4667 case SMDSEntity_Triangle: // TRIANGLE --->
4669 if ( nbDouble > 0 ) break;
4670 if ( nbSame == 0 ) // ---> pentahedron
4671 aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
4672 nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ] );
4674 else if ( nbSame == 1 ) // ---> pyramid
4675 aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
4676 nextNod[ iAfterSame ], nextNod[ iBeforeSame ],
4677 nextNod[ iSameNode ]);
4679 else // 2 same nodes: ---> tetrahedron
4680 aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
4681 nextNod[ iNotSameNode ]);
4684 case SMDSEntity_Quad_Edge: // sweep quadratic EDGE --->
4688 if ( nbDouble+nbSame == 2 )
4690 if(nbSame==0) { // ---> quadratic quadrangle
4691 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
4692 prevNod[2], midlNod[1], nextNod[2], midlNod[0]);
4694 else { //(nbSame==1) // ---> quadratic triangle
4696 return; // medium node on axis
4698 else if(sames[0]==0)
4699 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1],
4700 prevNod[2], midlNod[1], nextNod[2] );
4702 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[0],
4703 prevNod[2], nextNod[2], midlNod[0]);
4706 else if ( nbDouble == 3 )
4708 if ( nbSame == 0 ) { // ---> bi-quadratic quadrangle
4709 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
4710 prevNod[2], midlNod[1], nextNod[2], midlNod[0], midlNod[2]);
4717 case SMDSEntity_Quadrangle: { // sweep QUADRANGLE --->
4718 if ( nbDouble > 0 ) break;
4720 if ( nbSame == 0 ) // ---> hexahedron
4721 aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ], prevNod[ 3 ],
4722 nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ], nextNod[ 3 ]);
4724 else if ( nbSame == 1 ) { // ---> pyramid + pentahedron
4725 aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
4726 nextNod[ iAfterSame ], nextNod[ iBeforeSame ],
4727 nextNod[ iSameNode ]);
4728 newElems.push_back( aNewElem );
4729 aNewElem = aMesh->AddVolume (prevNod[ iAfterSame ], prevNod[ iOpposSame ],
4730 prevNod[ iBeforeSame ], nextNod[ iAfterSame ],
4731 nextNod[ iOpposSame ], nextNod[ iBeforeSame ] );
4733 else if ( nbSame == 2 ) { // ---> pentahedron
4734 if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] )
4735 // iBeforeSame is same too
4736 aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iOpposSame ],
4737 nextNod[ iOpposSame ], prevNod[ iSameNode ],
4738 prevNod[ iAfterSame ], nextNod[ iAfterSame ]);
4740 // iAfterSame is same too
4741 aNewElem = aMesh->AddVolume (prevNod[ iSameNode ], prevNod[ iBeforeSame ],
4742 nextNod[ iBeforeSame ], prevNod[ iAfterSame ],
4743 prevNod[ iOpposSame ], nextNod[ iOpposSame ]);
4747 case SMDSEntity_Quad_Triangle: // sweep (Bi)Quadratic TRIANGLE --->
4748 case SMDSEntity_BiQuad_Triangle: /* ??? */ {
4749 if ( nbDouble+nbSame != 3 ) break;
4751 // ---> pentahedron with 15 nodes
4752 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
4753 nextNod[0], nextNod[1], nextNod[2],
4754 prevNod[3], prevNod[4], prevNod[5],
4755 nextNod[3], nextNod[4], nextNod[5],
4756 midlNod[0], midlNod[1], midlNod[2]);
4758 else if(nbSame==1) {
4759 // ---> 2d order pyramid of 13 nodes
4760 int apex = iSameNode;
4761 int i0 = ( apex + 1 ) % nbCorners;
4762 int i1 = ( apex - 1 + nbCorners ) % nbCorners;
4766 aNewElem = aMesh->AddVolume(prevNod[i1], prevNod[i0],
4767 nextNod[i0], nextNod[i1], prevNod[apex],
4768 prevNod[i01], midlNod[i0],
4769 nextNod[i01], midlNod[i1],
4770 prevNod[i1a], prevNod[i0a],
4771 nextNod[i0a], nextNod[i1a]);
4773 else if(nbSame==2) {
4774 // ---> 2d order tetrahedron of 10 nodes
4775 int n1 = iNotSameNode;
4776 int n2 = ( n1 + 1 ) % nbCorners;
4777 int n3 = ( n1 + nbCorners - 1 ) % nbCorners;
4781 aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], prevNod[n3], nextNod[n1],
4782 prevNod[n12], prevNod[n23], prevNod[n31],
4783 midlNod[n1], nextNod[n12], nextNod[n31]);
4787 case SMDSEntity_Quad_Quadrangle: { // sweep Quadratic QUADRANGLE --->
4789 if ( nbDouble != 4 ) break;
4790 // ---> hexahedron with 20 nodes
4791 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
4792 nextNod[0], nextNod[1], nextNod[2], nextNod[3],
4793 prevNod[4], prevNod[5], prevNod[6], prevNod[7],
4794 nextNod[4], nextNod[5], nextNod[6], nextNod[7],
4795 midlNod[0], midlNod[1], midlNod[2], midlNod[3]);
4797 else if(nbSame==1) {
4798 // ---> pyramid + pentahedron - can not be created since it is needed
4799 // additional middle node at the center of face
4800 //INFOS( " Sweep for face " << elem->GetID() << " can not be created" );
4803 else if( nbSame == 2 ) {
4804 if ( nbDouble != 2 ) break;
4805 // ---> 2d order Pentahedron with 15 nodes
4807 if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] ) {
4808 // iBeforeSame is same too
4815 // iAfterSame is same too
4825 aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], nextNod[n2],
4826 prevNod[n4], prevNod[n5], nextNod[n5],
4827 prevNod[n12], midlNod[n2], nextNod[n12],
4828 prevNod[n45], midlNod[n5], nextNod[n45],
4829 prevNod[n14], prevNod[n25], nextNod[n25]);
4833 case SMDSEntity_BiQuad_Quadrangle: { // sweep BiQuadratic QUADRANGLE --->
4835 if( nbSame == 0 && nbDouble == 9 ) {
4836 // ---> tri-quadratic hexahedron with 27 nodes
4837 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
4838 nextNod[0], nextNod[1], nextNod[2], nextNod[3],
4839 prevNod[4], prevNod[5], prevNod[6], prevNod[7],
4840 nextNod[4], nextNod[5], nextNod[6], nextNod[7],
4841 midlNod[0], midlNod[1], midlNod[2], midlNod[3],
4842 prevNod[8], // bottom center
4843 midlNod[4], midlNod[5], midlNod[6], midlNod[7],
4844 nextNod[8], // top center
4845 midlNod[8]);// elem center
4853 case SMDSEntity_Polygon: { // sweep POLYGON
4855 if ( nbNodes == 6 && nbSame == 0 && nbDouble == 0 ) {
4856 // ---> hexagonal prism
4857 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
4858 prevNod[3], prevNod[4], prevNod[5],
4859 nextNod[0], nextNod[1], nextNod[2],
4860 nextNod[3], nextNod[4], nextNod[5]);
4864 case SMDSEntity_Ball:
4869 } // switch ( baseType )
4872 if ( !aNewElem && elem->GetType() == SMDSAbs_Face ) // try to create a polyherdal prism
4874 if ( baseType != SMDSEntity_Polygon )
4876 const std::vector<int>& ind = SMDS_MeshCell::interlacedSmdsOrder(baseType,nbNodes);
4877 SMDS_MeshCell::applyInterlace( ind, prevNod );
4878 SMDS_MeshCell::applyInterlace( ind, nextNod );
4879 SMDS_MeshCell::applyInterlace( ind, midlNod );
4880 SMDS_MeshCell::applyInterlace( ind, itNN );
4881 SMDS_MeshCell::applyInterlace( ind, isSingleNode );
4882 baseType = SMDSEntity_Polygon; // WARNING: change baseType !!!!
4884 vector<const SMDS_MeshNode*> polyedre_nodes (nbNodes*2 + 4*nbNodes);
4885 vector<int> quantities (nbNodes + 2);
4886 polyedre_nodes.clear();
4890 for (int inode = 0; inode < nbNodes; inode++)
4891 polyedre_nodes.push_back( prevNod[inode] );
4892 quantities.push_back( nbNodes );
4895 polyedre_nodes.push_back( nextNod[0] );
4896 for (int inode = nbNodes; inode-1; --inode )
4897 polyedre_nodes.push_back( nextNod[inode-1] );
4898 quantities.push_back( nbNodes );
4906 const int iQuad = elem->IsQuadratic();
4907 for (int iface = 0; iface < nbNodes; iface += 1+iQuad )
4909 const int prevNbNodes = polyedre_nodes.size(); // to detect degenerated face
4910 int inextface = (iface+1+iQuad) % nbNodes;
4911 int imid = (iface+1) % nbNodes;
4912 polyedre_nodes.push_back( prevNod[inextface] ); // 0
4913 if ( iQuad ) polyedre_nodes.push_back( prevNod[imid] ); // 4
4914 polyedre_nodes.push_back( prevNod[iface] ); // 1
4915 if ( prevNod[iface] != nextNod[iface] ) // 1 != 2
4917 if ( midlNod[ iface ]) polyedre_nodes.push_back( midlNod[ iface ]); // 5
4918 polyedre_nodes.push_back( nextNod[iface] ); // 2
4920 if ( iQuad ) polyedre_nodes.push_back( nextNod[imid] ); // 6
4921 if ( prevNod[inextface] != nextNod[inextface] ) // 0 != 3
4923 polyedre_nodes.push_back( nextNod[inextface] ); // 3
4924 if ( midlNod[ inextface ]) polyedre_nodes.push_back( midlNod[ inextface ]);// 7
4926 const int nbFaceNodes = polyedre_nodes.size() - prevNbNodes;
4927 if ( nbFaceNodes > 2 )
4928 quantities.push_back( nbFaceNodes );
4929 else // degenerated face
4930 polyedre_nodes.resize( prevNbNodes );
4932 aNewElem = aMesh->AddPolyhedralVolume (polyedre_nodes, quantities);
4934 } // try to create a polyherdal prism
4937 newElems.push_back( aNewElem );
4938 myLastCreatedElems.Append(aNewElem);
4939 srcElements.Append( elem );
4942 // set new prev nodes
4943 for ( iNode = 0; iNode < nbNodes; iNode++ )
4944 prevNod[ iNode ] = nextNod[ iNode ];
4949 //=======================================================================
4951 * \brief Create 1D and 2D elements around swept elements
4952 * \param mapNewNodes - source nodes and ones generated from them
4953 * \param newElemsMap - source elements and ones generated from them
4954 * \param elemNewNodesMap - nodes generated from each node of each element
4955 * \param elemSet - all swept elements
4956 * \param nbSteps - number of sweeping steps
4957 * \param srcElements - to append elem for each generated element
4959 //=======================================================================
4961 void SMESH_MeshEditor::makeWalls (TNodeOfNodeListMap & mapNewNodes,
4962 TTElemOfElemListMap & newElemsMap,
4963 TElemOfVecOfNnlmiMap & elemNewNodesMap,
4964 TIDSortedElemSet& elemSet,
4966 SMESH_SequenceOfElemPtr& srcElements)
4968 ASSERT( newElemsMap.size() == elemNewNodesMap.size() );
4969 SMESHDS_Mesh* aMesh = GetMeshDS();
4971 // Find nodes belonging to only one initial element - sweep them into edges.
4973 TNodeOfNodeListMapItr nList = mapNewNodes.begin();
4974 for ( ; nList != mapNewNodes.end(); nList++ )
4976 const SMDS_MeshNode* node =
4977 static_cast<const SMDS_MeshNode*>( nList->first );
4978 if ( newElemsMap.count( node ))
4979 continue; // node was extruded into edge
4980 SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator();
4981 int nbInitElems = 0;
4982 const SMDS_MeshElement* el = 0;
4983 SMDSAbs_ElementType highType = SMDSAbs_Edge; // count most complex elements only
4984 while ( eIt->more() && nbInitElems < 2 ) {
4985 const SMDS_MeshElement* e = eIt->next();
4986 SMDSAbs_ElementType type = e->GetType();
4987 if ( type == SMDSAbs_Volume ||
4991 if ( type > highType ) {
4998 if ( nbInitElems == 1 ) {
4999 bool NotCreateEdge = el && el->IsMediumNode(node);
5000 if(!NotCreateEdge) {
5001 vector<TNodeOfNodeListMapItr> newNodesItVec( 1, nList );
5002 list<const SMDS_MeshElement*> newEdges;
5003 sweepElement( node, newNodesItVec, newEdges, nbSteps, srcElements );
5008 // Make a ceiling for each element ie an equal element of last new nodes.
5009 // Find free links of faces - make edges and sweep them into faces.
5011 ElemFeatures polyFace( SMDSAbs_Face, /*isPoly=*/true ), anyFace;
5013 TTElemOfElemListMap::iterator itElem = newElemsMap.begin();
5014 TElemOfVecOfNnlmiMap::iterator itElemNodes = elemNewNodesMap.begin();
5015 for ( ; itElem != newElemsMap.end(); itElem++, itElemNodes++ )
5017 const SMDS_MeshElement* elem = itElem->first;
5018 vector<TNodeOfNodeListMapItr>& vecNewNodes = itElemNodes->second;
5020 if(itElem->second.size()==0) continue;
5022 const bool isQuadratic = elem->IsQuadratic();
5024 if ( elem->GetType() == SMDSAbs_Edge ) {
5025 // create a ceiling edge
5026 if ( !isQuadratic ) {
5027 if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
5028 vecNewNodes[ 1 ]->second.back())) {
5029 myLastCreatedElems.Append(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
5030 vecNewNodes[ 1 ]->second.back()));
5031 srcElements.Append( elem );
5035 if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
5036 vecNewNodes[ 1 ]->second.back(),
5037 vecNewNodes[ 2 ]->second.back())) {
5038 myLastCreatedElems.Append(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
5039 vecNewNodes[ 1 ]->second.back(),
5040 vecNewNodes[ 2 ]->second.back()));
5041 srcElements.Append( elem );
5045 if ( elem->GetType() != SMDSAbs_Face )
5048 bool hasFreeLinks = false;
5050 TIDSortedElemSet avoidSet;
5051 avoidSet.insert( elem );
5053 set<const SMDS_MeshNode*> aFaceLastNodes;
5054 int iNode, nbNodes = vecNewNodes.size();
5055 if ( !isQuadratic ) {
5056 // loop on the face nodes
5057 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
5058 aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
5059 // look for free links of the face
5060 int iNext = ( iNode + 1 == nbNodes ) ? 0 : iNode + 1;
5061 const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
5062 const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
5063 // check if a link n1-n2 is free
5064 if ( ! SMESH_MeshAlgos::FindFaceInSet ( n1, n2, elemSet, avoidSet )) {
5065 hasFreeLinks = true;
5066 // make a new edge and a ceiling for a new edge
5067 const SMDS_MeshElement* edge;
5068 if ( ! ( edge = aMesh->FindEdge( n1, n2 ))) {
5069 myLastCreatedElems.Append( edge = aMesh->AddEdge( n1, n2 )); // free link edge
5070 srcElements.Append( myLastCreatedElems.Last() );
5072 n1 = vecNewNodes[ iNode ]->second.back();
5073 n2 = vecNewNodes[ iNext ]->second.back();
5074 if ( !aMesh->FindEdge( n1, n2 )) {
5075 myLastCreatedElems.Append(aMesh->AddEdge( n1, n2 )); // new edge ceiling
5076 srcElements.Append( edge );
5081 else { // elem is quadratic face
5082 int nbn = nbNodes/2;
5083 for ( iNode = 0; iNode < nbn; iNode++ ) {
5084 aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
5085 int iNext = ( iNode + 1 == nbn ) ? 0 : iNode + 1;
5086 const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
5087 const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
5088 const SMDS_MeshNode* n3 = vecNewNodes[ iNode+nbn ]->first;
5089 // check if a link is free
5090 if ( ! SMESH_MeshAlgos::FindFaceInSet ( n1, n2, elemSet, avoidSet ) &&
5091 ! SMESH_MeshAlgos::FindFaceInSet ( n1, n3, elemSet, avoidSet ) &&
5092 ! SMESH_MeshAlgos::FindFaceInSet ( n3, n2, elemSet, avoidSet ) ) {
5093 hasFreeLinks = true;
5094 // make an edge and a ceiling for a new edge
5096 if ( !aMesh->FindEdge( n1, n2, n3 )) {
5097 myLastCreatedElems.Append(aMesh->AddEdge( n1, n2, n3 )); // free link edge
5098 srcElements.Append( elem );
5100 n1 = vecNewNodes[ iNode ]->second.back();
5101 n2 = vecNewNodes[ iNext ]->second.back();
5102 n3 = vecNewNodes[ iNode+nbn ]->second.back();
5103 if ( !aMesh->FindEdge( n1, n2, n3 )) {
5104 myLastCreatedElems.Append(aMesh->AddEdge( n1, n2, n3 )); // ceiling edge
5105 srcElements.Append( elem );
5109 for ( iNode = nbn; iNode < nbNodes; iNode++ ) {
5110 aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
5114 // sweep free links into faces
5116 if ( hasFreeLinks ) {
5117 list<const SMDS_MeshElement*> & newVolumes = itElem->second;
5118 int iVol, volNb, nbVolumesByStep = newVolumes.size() / nbSteps;
5120 set<const SMDS_MeshNode*> initNodeSet, topNodeSet, faceNodeSet;
5121 set<const SMDS_MeshNode*> initNodeSetNoCenter/*, topNodeSetNoCenter*/;
5122 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
5123 initNodeSet.insert( vecNewNodes[ iNode ]->first );
5124 topNodeSet .insert( vecNewNodes[ iNode ]->second.back() );
5126 if ( isQuadratic && nbNodes % 2 ) { // node set for the case of a biquadratic
5127 initNodeSetNoCenter = initNodeSet; // swept face and a not biquadratic volume
5128 initNodeSetNoCenter.erase( vecNewNodes.back()->first );
5130 for ( volNb = 0; volNb < nbVolumesByStep; volNb++ ) {
5131 list<const SMDS_MeshElement*>::iterator v = newVolumes.begin();
5132 std::advance( v, volNb );
5133 // find indices of free faces of a volume and their source edges
5134 list< int > freeInd;
5135 list< const SMDS_MeshElement* > srcEdges; // source edges of free faces
5136 SMDS_VolumeTool vTool( *v, /*ignoreCentralNodes=*/false );
5137 int iF, nbF = vTool.NbFaces();
5138 for ( iF = 0; iF < nbF; iF ++ ) {
5139 if (vTool.IsFreeFace( iF ) &&
5140 vTool.GetFaceNodes( iF, faceNodeSet ) &&
5141 initNodeSet != faceNodeSet) // except an initial face
5143 if ( nbSteps == 1 && faceNodeSet == topNodeSet )
5145 if ( faceNodeSet == initNodeSetNoCenter )
5147 freeInd.push_back( iF );
5148 // find source edge of a free face iF
5149 vector<const SMDS_MeshNode*> commonNodes; // shared by the initial and free faces
5150 vector<const SMDS_MeshNode*>::iterator lastCommom;
5151 commonNodes.resize( nbNodes, 0 );
5152 lastCommom = std::set_intersection( faceNodeSet.begin(), faceNodeSet.end(),
5153 initNodeSet.begin(), initNodeSet.end(),
5154 commonNodes.begin());
5155 if ( std::distance( commonNodes.begin(), lastCommom ) == 3 )
5156 srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1],commonNodes[2]));
5158 srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1]));
5160 if ( !srcEdges.back() )
5162 cout << "SMESH_MeshEditor::makeWalls(), no source edge found for a free face #"
5163 << iF << " of volume #" << vTool.ID() << endl;
5168 if ( freeInd.empty() )
5171 // create wall faces for all steps;
5172 // if such a face has been already created by sweep of edge,
5173 // assure that its orientation is OK
5174 for ( int iStep = 0; iStep < nbSteps; iStep++ )
5176 vTool.Set( *v, /*ignoreCentralNodes=*/false );
5177 vTool.SetExternalNormal();
5178 const int nextShift = vTool.IsForward() ? +1 : -1;
5179 list< int >::iterator ind = freeInd.begin();
5180 list< const SMDS_MeshElement* >::iterator srcEdge = srcEdges.begin();
5181 for ( ; ind != freeInd.end(); ++ind, ++srcEdge ) // loop on free faces
5183 const SMDS_MeshNode** nodes = vTool.GetFaceNodes( *ind );
5184 int nbn = vTool.NbFaceNodes( *ind );
5185 const SMDS_MeshElement * f = 0;
5186 if ( nbn == 3 ) ///// triangle
5188 f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ]);
5190 nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
5192 const SMDS_MeshNode* newOrder[3] = { nodes[ 1 - nextShift ],
5194 nodes[ 1 + nextShift ] };
5196 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5198 myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
5202 else if ( nbn == 4 ) ///// quadrangle
5204 f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ], nodes[ 3 ]);
5206 nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
5208 const SMDS_MeshNode* newOrder[4] = { nodes[ 0 ], nodes[ 2-nextShift ],
5209 nodes[ 2 ], nodes[ 2+nextShift ] };
5211 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5213 myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
5214 newOrder[ 2 ], newOrder[ 3 ]));
5217 else if ( nbn == 6 && isQuadratic ) /////// quadratic triangle
5219 f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[1], nodes[3], nodes[5] );
5221 nodes[2] != f->GetNodeWrap( f->GetNodeIndex( nodes[0] ) + 2*nextShift ))
5223 const SMDS_MeshNode* newOrder[6] = { nodes[2 - 2*nextShift],
5225 nodes[2 + 2*nextShift],
5226 nodes[3 - 2*nextShift],
5228 nodes[3 + 2*nextShift]};
5230 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5232 myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ],
5240 else if ( nbn == 8 && isQuadratic ) /////// quadratic quadrangle
5242 f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[6],
5243 nodes[1], nodes[3], nodes[5], nodes[7] );
5245 nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
5247 const SMDS_MeshNode* newOrder[8] = { nodes[0],
5248 nodes[4 - 2*nextShift],
5250 nodes[4 + 2*nextShift],
5252 nodes[5 - 2*nextShift],
5254 nodes[5 + 2*nextShift] };
5256 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5258 myLastCreatedElems.Append(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
5259 newOrder[ 2 ], newOrder[ 3 ],
5260 newOrder[ 4 ], newOrder[ 5 ],
5261 newOrder[ 6 ], newOrder[ 7 ]));
5264 else if ( nbn == 9 && isQuadratic ) /////// bi-quadratic quadrangle
5266 f = aMesh->FindElement( vector<const SMDS_MeshNode*>( nodes, nodes+nbn ),
5267 SMDSAbs_Face, /*noMedium=*/false);
5269 nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
5271 const SMDS_MeshNode* newOrder[9] = { nodes[0],
5272 nodes[4 - 2*nextShift],
5274 nodes[4 + 2*nextShift],
5276 nodes[5 - 2*nextShift],
5278 nodes[5 + 2*nextShift],
5281 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5283 myLastCreatedElems.Append(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
5284 newOrder[ 2 ], newOrder[ 3 ],
5285 newOrder[ 4 ], newOrder[ 5 ],
5286 newOrder[ 6 ], newOrder[ 7 ],
5290 else //////// polygon
5292 vector<const SMDS_MeshNode*> polygon_nodes ( nodes, nodes+nbn );
5293 const SMDS_MeshFace * f = aMesh->FindFace( polygon_nodes );
5295 nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + nextShift ))
5297 if ( !vTool.IsForward() )
5298 std::reverse( polygon_nodes.begin(), polygon_nodes.end());
5300 aMesh->ChangeElementNodes( f, &polygon_nodes[0], nbn );
5302 AddElement( polygon_nodes, polyFace.SetQuad( (*v)->IsQuadratic() ));
5306 while ( srcElements.Length() < myLastCreatedElems.Length() )
5307 srcElements.Append( *srcEdge );
5309 } // loop on free faces
5311 // go to the next volume
5313 while ( iVol++ < nbVolumesByStep ) v++;
5316 } // loop on volumes of one step
5317 } // sweep free links into faces
5319 // Make a ceiling face with a normal external to a volume
5321 // use SMDS_VolumeTool to get a correctly ordered nodes of a ceiling face
5322 SMDS_VolumeTool lastVol( itElem->second.back(), /*ignoreCentralNodes=*/false );
5323 int iF = lastVol.GetFaceIndex( aFaceLastNodes );
5325 if ( iF < 0 && isQuadratic && nbNodes % 2 ) { // remove a central node of biquadratic
5326 aFaceLastNodes.erase( vecNewNodes.back()->second.back() );
5327 iF = lastVol.GetFaceIndex( aFaceLastNodes );
5331 lastVol.SetExternalNormal();
5332 const SMDS_MeshNode** nodes = lastVol.GetFaceNodes( iF );
5333 const int nbn = lastVol.NbFaceNodes( iF );
5334 vector<const SMDS_MeshNode*> nodeVec( nodes, nodes+nbn );
5335 if ( !hasFreeLinks ||
5336 !aMesh->FindElement( nodeVec, SMDSAbs_Face, /*noMedium=*/false) )
5338 const vector<int>& interlace =
5339 SMDS_MeshCell::interlacedSmdsOrder( elem->GetEntityType(), nbn );
5340 SMDS_MeshCell::applyInterlaceRev( interlace, nodeVec );
5342 AddElement( nodeVec, anyFace.Init( elem ));
5344 while ( srcElements.Length() < myLastCreatedElems.Length() )
5345 srcElements.Append( elem );
5348 } // loop on swept elements
5351 //=======================================================================
5352 //function : RotationSweep
5354 //=======================================================================
5356 SMESH_MeshEditor::PGroupIDs
5357 SMESH_MeshEditor::RotationSweep(TIDSortedElemSet theElemSets[2],
5358 const gp_Ax1& theAxis,
5359 const double theAngle,
5360 const int theNbSteps,
5361 const double theTol,
5362 const bool theMakeGroups,
5363 const bool theMakeWalls)
5365 myLastCreatedElems.Clear();
5366 myLastCreatedNodes.Clear();
5368 // source elements for each generated one
5369 SMESH_SequenceOfElemPtr srcElems, srcNodes;
5372 aTrsf.SetRotation( theAxis, theAngle );
5374 aTrsf2.SetRotation( theAxis, theAngle/2. );
5376 gp_Lin aLine( theAxis );
5377 double aSqTol = theTol * theTol;
5379 SMESHDS_Mesh* aMesh = GetMeshDS();
5381 TNodeOfNodeListMap mapNewNodes;
5382 TElemOfVecOfNnlmiMap mapElemNewNodes;
5383 TTElemOfElemListMap newElemsMap;
5385 const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
5386 myMesh->NbFaces(ORDER_QUADRATIC) +
5387 myMesh->NbVolumes(ORDER_QUADRATIC) );
5388 // loop on theElemSets
5389 setElemsFirst( theElemSets );
5390 TIDSortedElemSet::iterator itElem;
5391 for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
5393 TIDSortedElemSet& theElems = theElemSets[ is2ndSet ];
5394 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
5395 const SMDS_MeshElement* elem = *itElem;
5396 if ( !elem || elem->GetType() == SMDSAbs_Volume )
5398 vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5399 newNodesItVec.reserve( elem->NbNodes() );
5401 // loop on elem nodes
5402 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5403 while ( itN->more() )
5405 const SMDS_MeshNode* node = cast2Node( itN->next() );
5407 gp_XYZ aXYZ( node->X(), node->Y(), node->Z() );
5409 aXYZ.Coord( coord[0], coord[1], coord[2] );
5410 bool isOnAxis = ( aLine.SquareDistance( aXYZ ) <= aSqTol );
5412 // check if a node has been already sweeped
5413 TNodeOfNodeListMapItr nIt =
5414 mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5415 list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5416 if ( listNewNodes.empty() )
5418 // check if we are to create medium nodes between corner ones
5419 bool needMediumNodes = false;
5420 if ( isQuadraticMesh )
5422 SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
5423 while (it->more() && !needMediumNodes )
5425 const SMDS_MeshElement* invElem = it->next();
5426 if ( invElem != elem && !theElems.count( invElem )) continue;
5427 needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
5428 if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
5429 needMediumNodes = true;
5434 const SMDS_MeshNode * newNode = node;
5435 for ( int i = 0; i < theNbSteps; i++ ) {
5437 if ( needMediumNodes ) // create a medium node
5439 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
5440 newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5441 myLastCreatedNodes.Append(newNode);
5442 srcNodes.Append( node );
5443 listNewNodes.push_back( newNode );
5444 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
5447 aTrsf.Transforms( coord[0], coord[1], coord[2] );
5449 // create a corner node
5450 newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5451 myLastCreatedNodes.Append(newNode);
5452 srcNodes.Append( node );
5453 listNewNodes.push_back( newNode );
5456 listNewNodes.push_back( newNode );
5457 // if ( needMediumNodes )
5458 // listNewNodes.push_back( newNode );
5462 newNodesItVec.push_back( nIt );
5464 // make new elements
5465 sweepElement( elem, newNodesItVec, newElemsMap[elem], theNbSteps, srcElems );
5470 makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElemSets[0], theNbSteps, srcElems );
5472 PGroupIDs newGroupIDs;
5473 if ( theMakeGroups )
5474 newGroupIDs = generateGroups( srcNodes, srcElems, "rotated");
5479 //=======================================================================
5480 //function : ExtrusParam
5481 //purpose : standard construction
5482 //=======================================================================
5484 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const gp_Vec& theStep,
5485 const int theNbSteps,
5486 const std::list<double>& theScales,
5487 const gp_XYZ* theBasePoint,
5489 const double theTolerance):
5491 myBaseP( Precision::Infinite(), 0, 0 ),
5492 myFlags( theFlags ),
5493 myTolerance( theTolerance ),
5494 myElemsToUse( NULL )
5496 mySteps = new TColStd_HSequenceOfReal;
5497 const double stepSize = theStep.Magnitude();
5498 for (int i=1; i<=theNbSteps; i++ )
5499 mySteps->Append( stepSize );
5501 int nbScales = theScales.size();
5504 if ( IsLinearVariation() && nbScales < theNbSteps )
5506 myScales.reserve( theNbSteps );
5507 std::list<double>::const_iterator scale = theScales.begin();
5508 double prevScale = 1.0;
5509 for ( int iSc = 1; scale != theScales.end(); ++scale, ++iSc )
5511 int iStep = int( iSc / double( nbScales ) * theNbSteps + 0.5 );
5512 int stDelta = Max( 1, iStep - myScales.size());
5513 double scDelta = ( *scale - prevScale ) / stDelta;
5514 for ( int iStep = 0; iStep < stDelta; ++iStep )
5516 myScales.push_back( prevScale + scDelta );
5517 prevScale = myScales.back();
5524 myScales.assign( theScales.begin(), theScales.end() );
5529 myBaseP = *theBasePoint;
5532 if (( theFlags & EXTRUSION_FLAG_SEW ) &&
5533 ( theTolerance > 0 ))
5535 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDirAndSew;
5539 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDir;
5543 //=======================================================================
5544 //function : ExtrusParam
5545 //purpose : steps are given explicitly
5546 //=======================================================================
5548 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const gp_Dir& theDir,
5549 Handle(TColStd_HSequenceOfReal) theSteps,
5551 const double theTolerance):
5553 mySteps( theSteps ),
5554 myFlags( theFlags ),
5555 myTolerance( theTolerance ),
5556 myElemsToUse( NULL )
5558 if (( theFlags & EXTRUSION_FLAG_SEW ) &&
5559 ( theTolerance > 0 ))
5561 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDirAndSew;
5565 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDir;
5569 //=======================================================================
5570 //function : ExtrusParam
5571 //purpose : for extrusion by normal
5572 //=======================================================================
5574 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const double theStepSize,
5575 const int theNbSteps,
5579 mySteps( new TColStd_HSequenceOfReal ),
5580 myFlags( theFlags ),
5582 myElemsToUse( NULL )
5584 for (int i = 0; i < theNbSteps; i++ )
5585 mySteps->Append( theStepSize );
5589 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByNormal1D;
5593 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByNormal2D;
5597 //=======================================================================
5598 //function : ExtrusParam::SetElementsToUse
5599 //purpose : stores elements to use for extrusion by normal, depending on
5600 // state of EXTRUSION_FLAG_USE_INPUT_ELEMS_ONLY flag;
5601 // define myBaseP for scaling
5602 //=======================================================================
5604 void SMESH_MeshEditor::ExtrusParam::SetElementsToUse( const TIDSortedElemSet& elems,
5605 const TIDSortedElemSet& nodes )
5607 myElemsToUse = ToUseInpElemsOnly() ? & elems : 0;
5609 if ( Precision::IsInfinite( myBaseP.X() )) // myBaseP not defined
5611 myBaseP.SetCoord( 0.,0.,0. );
5612 TIDSortedElemSet newNodes;
5614 const TIDSortedElemSet* elemSets[] = { &elems, &nodes };
5615 for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
5617 const TIDSortedElemSet& elements = *( elemSets[ is2ndSet ]);
5618 TIDSortedElemSet::const_iterator itElem = elements.begin();
5619 for ( ; itElem != elements.end(); itElem++ )
5621 const SMDS_MeshElement* elem = *itElem;
5622 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5623 while ( itN->more() ) {
5624 const SMDS_MeshElement* node = itN->next();
5625 if ( newNodes.insert( node ).second )
5626 myBaseP += SMESH_TNodeXYZ( node );
5630 myBaseP /= newNodes.size();
5634 //=======================================================================
5635 //function : ExtrusParam::beginStepIter
5636 //purpose : prepare iteration on steps
5637 //=======================================================================
5639 void SMESH_MeshEditor::ExtrusParam::beginStepIter( bool withMediumNodes )
5641 myWithMediumNodes = withMediumNodes;
5645 //=======================================================================
5646 //function : ExtrusParam::moreSteps
5647 //purpose : are there more steps?
5648 //=======================================================================
5650 bool SMESH_MeshEditor::ExtrusParam::moreSteps()
5652 return myNextStep <= mySteps->Length() || !myCurSteps.empty();
5654 //=======================================================================
5655 //function : ExtrusParam::nextStep
5656 //purpose : returns the next step
5657 //=======================================================================
5659 double SMESH_MeshEditor::ExtrusParam::nextStep()
5662 if ( !myCurSteps.empty() )
5664 res = myCurSteps.back();
5665 myCurSteps.pop_back();
5667 else if ( myNextStep <= mySteps->Length() )
5669 myCurSteps.push_back( mySteps->Value( myNextStep ));
5671 if ( myWithMediumNodes )
5673 myCurSteps.back() /= 2.;
5674 myCurSteps.push_back( myCurSteps.back() );
5681 //=======================================================================
5682 //function : ExtrusParam::makeNodesByDir
5683 //purpose : create nodes for standard extrusion
5684 //=======================================================================
5686 int SMESH_MeshEditor::ExtrusParam::
5687 makeNodesByDir( SMESHDS_Mesh* mesh,
5688 const SMDS_MeshNode* srcNode,
5689 std::list<const SMDS_MeshNode*> & newNodes,
5690 const bool makeMediumNodes)
5692 gp_XYZ p = SMESH_TNodeXYZ( srcNode );
5695 for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5697 p += myDir.XYZ() * nextStep();
5698 const SMDS_MeshNode * newNode = mesh->AddNode( p.X(), p.Y(), p.Z() );
5699 newNodes.push_back( newNode );
5702 if ( !myScales.empty() )
5704 if ( makeMediumNodes && myMediumScales.empty() )
5706 myMediumScales.resize( myScales.size() );
5707 double prevFactor = 1.;
5708 for ( size_t i = 0; i < myScales.size(); ++i )
5710 myMediumScales[i] = 0.5 * ( prevFactor + myScales[i] );
5711 prevFactor = myScales[i];
5714 typedef std::vector<double>::iterator ScaleIt;
5715 ScaleIt scales[] = { myScales.begin(), myMediumScales.begin() };
5717 size_t iSc = 0, nbScales = myScales.size() + myMediumScales.size();
5719 gp_XYZ center = myBaseP;
5720 std::list<const SMDS_MeshNode*>::iterator nIt = newNodes.begin();
5722 for ( beginStepIter( makeMediumNodes ); moreSteps() && ( iN < nbScales ); ++nIt, ++iN )
5724 center += myDir.XYZ() * nextStep();
5726 iSc += int( makeMediumNodes );
5727 ScaleIt& scale = scales[ iSc % 2 ];
5729 gp_XYZ xyz = SMESH_TNodeXYZ( *nIt );
5730 xyz = ( *scale * ( xyz - center )) + center;
5731 mesh->MoveNode( *nIt, xyz.X(), xyz.Y(), xyz.Z() );
5739 //=======================================================================
5740 //function : ExtrusParam::makeNodesByDirAndSew
5741 //purpose : create nodes for standard extrusion with sewing
5742 //=======================================================================
5744 int SMESH_MeshEditor::ExtrusParam::
5745 makeNodesByDirAndSew( SMESHDS_Mesh* mesh,
5746 const SMDS_MeshNode* srcNode,
5747 std::list<const SMDS_MeshNode*> & newNodes,
5748 const bool makeMediumNodes)
5750 gp_XYZ P1 = SMESH_TNodeXYZ( srcNode );
5753 for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5755 P1 += myDir.XYZ() * nextStep();
5757 // try to search in sequence of existing nodes
5758 // if myNodes.Length()>0 we 'nave to use given sequence
5759 // else - use all nodes of mesh
5760 const SMDS_MeshNode * node = 0;
5761 if ( myNodes.Length() > 0 ) {
5763 for(i=1; i<=myNodes.Length(); i++) {
5764 gp_XYZ P2 = SMESH_TNodeXYZ( myNodes.Value(i) );
5765 if (( P1 - P2 ).SquareModulus() < myTolerance * myTolerance )
5767 node = myNodes.Value(i);
5773 SMDS_NodeIteratorPtr itn = mesh->nodesIterator();
5774 while(itn->more()) {
5775 SMESH_TNodeXYZ P2( itn->next() );
5776 if (( P1 - P2 ).SquareModulus() < myTolerance * myTolerance )
5785 node = mesh->AddNode( P1.X(), P1.Y(), P1.Z() );
5787 newNodes.push_back( node );
5794 //=======================================================================
5795 //function : ExtrusParam::makeNodesByNormal2D
5796 //purpose : create nodes for extrusion using normals of faces
5797 //=======================================================================
5799 int SMESH_MeshEditor::ExtrusParam::
5800 makeNodesByNormal2D( SMESHDS_Mesh* mesh,
5801 const SMDS_MeshNode* srcNode,
5802 std::list<const SMDS_MeshNode*> & newNodes,
5803 const bool makeMediumNodes)
5805 const bool alongAvgNorm = ( myFlags & EXTRUSION_FLAG_BY_AVG_NORMAL );
5807 gp_XYZ p = SMESH_TNodeXYZ( srcNode );
5809 // get normals to faces sharing srcNode
5810 vector< gp_XYZ > norms, baryCenters;
5811 gp_XYZ norm, avgNorm( 0,0,0 );
5812 SMDS_ElemIteratorPtr faceIt = srcNode->GetInverseElementIterator( SMDSAbs_Face );
5813 while ( faceIt->more() )
5815 const SMDS_MeshElement* face = faceIt->next();
5816 if ( myElemsToUse && !myElemsToUse->count( face ))
5818 if ( SMESH_MeshAlgos::FaceNormal( face, norm, /*normalized=*/true ))
5820 norms.push_back( norm );
5822 if ( !alongAvgNorm )
5826 for ( SMDS_ElemIteratorPtr nIt = face->nodesIterator(); nIt->more(); ++nbN )
5827 bc += SMESH_TNodeXYZ( nIt->next() );
5828 baryCenters.push_back( bc / nbN );
5833 if ( norms.empty() ) return 0;
5835 double normSize = avgNorm.Modulus();
5836 if ( normSize < std::numeric_limits<double>::min() )
5839 if ( myFlags & EXTRUSION_FLAG_BY_AVG_NORMAL ) // extrude along avgNorm
5842 return makeNodesByDir( mesh, srcNode, newNodes, makeMediumNodes );
5845 avgNorm /= normSize;
5848 for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5851 double stepSize = nextStep();
5853 if ( norms.size() > 1 )
5855 for ( size_t iF = 0; iF < norms.size(); ++iF ) // loop on faces
5857 // translate plane of a face
5858 baryCenters[ iF ] += norms[ iF ] * stepSize;
5860 // find point of intersection of the face plane located at baryCenters[ iF ]
5861 // and avgNorm located at pNew
5862 double d = -( norms[ iF ] * baryCenters[ iF ]); // d of plane equation ax+by+cz+d=0
5863 double dot = ( norms[ iF ] * avgNorm );
5864 if ( dot < std::numeric_limits<double>::min() )
5865 dot = stepSize * 1e-3;
5866 double step = -( norms[ iF ] * pNew + d ) / dot;
5867 pNew += step * avgNorm;
5872 pNew += stepSize * avgNorm;
5876 const SMDS_MeshNode * newNode = mesh->AddNode( p.X(), p.Y(), p.Z() );
5877 newNodes.push_back( newNode );
5882 //=======================================================================
5883 //function : ExtrusParam::makeNodesByNormal1D
5884 //purpose : create nodes for extrusion using normals of edges
5885 //=======================================================================
5887 int SMESH_MeshEditor::ExtrusParam::
5888 makeNodesByNormal1D( SMESHDS_Mesh* mesh,
5889 const SMDS_MeshNode* srcNode,
5890 std::list<const SMDS_MeshNode*> & newNodes,
5891 const bool makeMediumNodes)
5893 throw SALOME_Exception("Extrusion 1D by Normal not implemented");
5897 //=======================================================================
5898 //function : ExtrusionSweep
5900 //=======================================================================
5902 SMESH_MeshEditor::PGroupIDs
5903 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet theElems[2],
5904 const gp_Vec& theStep,
5905 const int theNbSteps,
5906 TTElemOfElemListMap& newElemsMap,
5908 const double theTolerance)
5910 ExtrusParam aParams( theStep, theNbSteps, std::list<double>(), 0, theFlags, theTolerance );
5911 return ExtrusionSweep( theElems, aParams, newElemsMap );
5915 //=======================================================================
5916 //function : ExtrusionSweep
5918 //=======================================================================
5920 SMESH_MeshEditor::PGroupIDs
5921 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet theElemSets[2],
5922 ExtrusParam& theParams,
5923 TTElemOfElemListMap& newElemsMap)
5925 myLastCreatedElems.Clear();
5926 myLastCreatedNodes.Clear();
5928 // source elements for each generated one
5929 SMESH_SequenceOfElemPtr srcElems, srcNodes;
5931 setElemsFirst( theElemSets );
5932 const int nbSteps = theParams.NbSteps();
5933 theParams.SetElementsToUse( theElemSets[0], theElemSets[1] );
5935 TNodeOfNodeListMap mapNewNodes;
5936 TElemOfVecOfNnlmiMap mapElemNewNodes;
5938 const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
5939 myMesh->NbFaces(ORDER_QUADRATIC) +
5940 myMesh->NbVolumes(ORDER_QUADRATIC) );
5942 TIDSortedElemSet::iterator itElem;
5943 for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
5945 TIDSortedElemSet& theElems = theElemSets[ is2ndSet ];
5946 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
5948 // check element type
5949 const SMDS_MeshElement* elem = *itElem;
5950 if ( !elem || elem->GetType() == SMDSAbs_Volume )
5953 const size_t nbNodes = elem->NbNodes();
5954 vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5955 newNodesItVec.reserve( nbNodes );
5957 // loop on elem nodes
5958 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5959 while ( itN->more() )
5961 // check if a node has been already sweeped
5962 const SMDS_MeshNode* node = cast2Node( itN->next() );
5963 TNodeOfNodeListMap::iterator nIt =
5964 mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5965 list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5966 if ( listNewNodes.empty() )
5970 // check if we are to create medium nodes between corner ones
5971 bool needMediumNodes = false;
5972 if ( isQuadraticMesh )
5974 SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
5975 while (it->more() && !needMediumNodes )
5977 const SMDS_MeshElement* invElem = it->next();
5978 if ( invElem != elem && !theElems.count( invElem )) continue;
5979 needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
5980 if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
5981 needMediumNodes = true;
5984 // create nodes for all steps
5985 if ( theParams.MakeNodes( GetMeshDS(), node, listNewNodes, needMediumNodes ))
5987 list<const SMDS_MeshNode*>::iterator newNodesIt = listNewNodes.begin();
5988 for ( ; newNodesIt != listNewNodes.end(); ++newNodesIt )
5990 myLastCreatedNodes.Append( *newNodesIt );
5991 srcNodes.Append( node );
5996 break; // newNodesItVec will be shorter than nbNodes
5999 newNodesItVec.push_back( nIt );
6001 // make new elements
6002 if ( newNodesItVec.size() == nbNodes )
6003 sweepElement( elem, newNodesItVec, newElemsMap[elem], nbSteps, srcElems );
6007 if ( theParams.ToMakeBoundary() ) {
6008 makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElemSets[0], nbSteps, srcElems );
6010 PGroupIDs newGroupIDs;
6011 if ( theParams.ToMakeGroups() )
6012 newGroupIDs = generateGroups( srcNodes, srcElems, "extruded");
6017 //=======================================================================
6018 //function : ExtrusionAlongTrack
6020 //=======================================================================
6021 SMESH_MeshEditor::Extrusion_Error
6022 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet theElements[2],
6023 SMESH_subMesh* theTrack,
6024 const SMDS_MeshNode* theN1,
6025 const bool theHasAngles,
6026 list<double>& theAngles,
6027 const bool theLinearVariation,
6028 const bool theHasRefPoint,
6029 const gp_Pnt& theRefPoint,
6030 const bool theMakeGroups)
6032 myLastCreatedElems.Clear();
6033 myLastCreatedNodes.Clear();
6036 std::list<double> aPrms;
6037 TIDSortedElemSet::iterator itElem;
6040 TopoDS_Edge aTrackEdge;
6041 TopoDS_Vertex aV1, aV2;
6043 SMDS_ElemIteratorPtr aItE;
6044 SMDS_NodeIteratorPtr aItN;
6045 SMDSAbs_ElementType aTypeE;
6047 TNodeOfNodeListMap mapNewNodes;
6050 aNbE = theElements[0].size() + theElements[1].size();
6053 return EXTR_NO_ELEMENTS;
6055 // 1.1 Track Pattern
6058 SMESHDS_SubMesh* pSubMeshDS = theTrack->GetSubMeshDS();
6060 return ExtrusionAlongTrack( theElements, theTrack->GetFather(), theN1,
6061 theHasAngles, theAngles, theLinearVariation,
6062 theHasRefPoint, theRefPoint, theMakeGroups );
6064 aItE = pSubMeshDS->GetElements();
6065 while ( aItE->more() ) {
6066 const SMDS_MeshElement* pE = aItE->next();
6067 aTypeE = pE->GetType();
6068 // Pattern must contain links only
6069 if ( aTypeE != SMDSAbs_Edge )
6070 return EXTR_PATH_NOT_EDGE;
6073 list<SMESH_MeshEditor_PathPoint> fullList;
6075 const TopoDS_Shape& aS = theTrack->GetSubShape();
6076 // Sub-shape for the Pattern must be an Edge or Wire
6077 if( aS.ShapeType() == TopAbs_EDGE ) {
6078 aTrackEdge = TopoDS::Edge( aS );
6079 // the Edge must not be degenerated
6080 if ( SMESH_Algo::isDegenerated( aTrackEdge ) )
6081 return EXTR_BAD_PATH_SHAPE;
6082 TopExp::Vertices( aTrackEdge, aV1, aV2 );
6083 aItN = theTrack->GetFather()->GetSubMesh( aV1 )->GetSubMeshDS()->GetNodes();
6084 const SMDS_MeshNode* aN1 = aItN->next();
6085 aItN = theTrack->GetFather()->GetSubMesh( aV2 )->GetSubMeshDS()->GetNodes();
6086 const SMDS_MeshNode* aN2 = aItN->next();
6087 // starting node must be aN1 or aN2
6088 if ( !( aN1 == theN1 || aN2 == theN1 ) )
6089 return EXTR_BAD_STARTING_NODE;
6090 aItN = pSubMeshDS->GetNodes();
6091 while ( aItN->more() ) {
6092 const SMDS_MeshNode* pNode = aItN->next();
6093 const SMDS_EdgePosition* pEPos =
6094 static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
6095 double aT = pEPos->GetUParameter();
6096 aPrms.push_back( aT );
6098 //Extrusion_Error err =
6099 makeEdgePathPoints(aPrms, aTrackEdge, (aN1==theN1), fullList);
6100 } else if( aS.ShapeType() == TopAbs_WIRE ) {
6101 list< SMESH_subMesh* > LSM;
6102 TopTools_SequenceOfShape Edges;
6103 SMESH_subMeshIteratorPtr itSM = theTrack->getDependsOnIterator(false,true);
6104 while(itSM->more()) {
6105 SMESH_subMesh* SM = itSM->next();
6107 const TopoDS_Shape& aS = SM->GetSubShape();
6110 list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
6111 int startNid = theN1->GetID();
6112 TColStd_MapOfInteger UsedNums;
6114 int NbEdges = Edges.Length();
6116 for(; i<=NbEdges; i++) {
6118 list< SMESH_subMesh* >::iterator itLSM = LSM.begin();
6119 for(; itLSM!=LSM.end(); itLSM++) {
6121 if(UsedNums.Contains(k)) continue;
6122 aTrackEdge = TopoDS::Edge( Edges.Value(k) );
6123 SMESH_subMesh* locTrack = *itLSM;
6124 SMESHDS_SubMesh* locMeshDS = locTrack->GetSubMeshDS();
6125 TopExp::Vertices( aTrackEdge, aV1, aV2 );
6126 aItN = locTrack->GetFather()->GetSubMesh(aV1)->GetSubMeshDS()->GetNodes();
6127 const SMDS_MeshNode* aN1 = aItN->next();
6128 aItN = locTrack->GetFather()->GetSubMesh(aV2)->GetSubMeshDS()->GetNodes();
6129 const SMDS_MeshNode* aN2 = aItN->next();
6130 // starting node must be aN1 or aN2
6131 if ( !( aN1->GetID() == startNid || aN2->GetID() == startNid ) ) continue;
6132 // 2. Collect parameters on the track edge
6134 aItN = locMeshDS->GetNodes();
6135 while ( aItN->more() ) {
6136 const SMDS_MeshNode* pNode = aItN->next();
6137 const SMDS_EdgePosition* pEPos =
6138 static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
6139 double aT = pEPos->GetUParameter();
6140 aPrms.push_back( aT );
6142 list<SMESH_MeshEditor_PathPoint> LPP;
6143 //Extrusion_Error err =
6144 makeEdgePathPoints(aPrms, aTrackEdge,(aN1->GetID()==startNid), LPP);
6145 LLPPs.push_back(LPP);
6147 // update startN for search following egde
6148 if( aN1->GetID() == startNid ) startNid = aN2->GetID();
6149 else startNid = aN1->GetID();
6153 list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
6154 list<SMESH_MeshEditor_PathPoint> firstList = *itLLPP;
6155 list<SMESH_MeshEditor_PathPoint>::iterator itPP = firstList.begin();
6156 for(; itPP!=firstList.end(); itPP++) {
6157 fullList.push_back( *itPP );
6159 SMESH_MeshEditor_PathPoint PP1 = fullList.back();
6160 fullList.pop_back();
6162 for(; itLLPP!=LLPPs.end(); itLLPP++) {
6163 list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
6164 itPP = currList.begin();
6165 SMESH_MeshEditor_PathPoint PP2 = currList.front();
6166 gp_Dir D1 = PP1.Tangent();
6167 gp_Dir D2 = PP2.Tangent();
6168 gp_Dir Dnew( gp_Vec( (D1.X()+D2.X())/2, (D1.Y()+D2.Y())/2,
6169 (D1.Z()+D2.Z())/2 ) );
6170 PP1.SetTangent(Dnew);
6171 fullList.push_back(PP1);
6173 for(; itPP!=firstList.end(); itPP++) {
6174 fullList.push_back( *itPP );
6176 PP1 = fullList.back();
6177 fullList.pop_back();
6179 // if wire not closed
6180 fullList.push_back(PP1);
6184 return EXTR_BAD_PATH_SHAPE;
6187 return makeExtrElements(theElements, fullList, theHasAngles, theAngles, theLinearVariation,
6188 theHasRefPoint, theRefPoint, theMakeGroups);
6192 //=======================================================================
6193 //function : ExtrusionAlongTrack
6195 //=======================================================================
6196 SMESH_MeshEditor::Extrusion_Error
6197 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet theElements[2],
6198 SMESH_Mesh* theTrack,
6199 const SMDS_MeshNode* theN1,
6200 const bool theHasAngles,
6201 list<double>& theAngles,
6202 const bool theLinearVariation,
6203 const bool theHasRefPoint,
6204 const gp_Pnt& theRefPoint,
6205 const bool theMakeGroups)
6207 myLastCreatedElems.Clear();
6208 myLastCreatedNodes.Clear();
6211 std::list<double> aPrms;
6212 TIDSortedElemSet::iterator itElem;
6215 TopoDS_Edge aTrackEdge;
6216 TopoDS_Vertex aV1, aV2;
6218 SMDS_ElemIteratorPtr aItE;
6219 SMDS_NodeIteratorPtr aItN;
6220 SMDSAbs_ElementType aTypeE;
6222 TNodeOfNodeListMap mapNewNodes;
6225 aNbE = theElements[0].size() + theElements[1].size();
6228 return EXTR_NO_ELEMENTS;
6230 // 1.1 Track Pattern
6233 SMESHDS_Mesh* pMeshDS = theTrack->GetMeshDS();
6235 aItE = pMeshDS->elementsIterator();
6236 while ( aItE->more() ) {
6237 const SMDS_MeshElement* pE = aItE->next();
6238 aTypeE = pE->GetType();
6239 // Pattern must contain links only
6240 if ( aTypeE != SMDSAbs_Edge )
6241 return EXTR_PATH_NOT_EDGE;
6244 list<SMESH_MeshEditor_PathPoint> fullList;
6246 const TopoDS_Shape& aS = theTrack->GetShapeToMesh();
6248 if ( !theTrack->HasShapeToMesh() ) {
6249 //Mesh without shape
6250 const SMDS_MeshNode* currentNode = NULL;
6251 const SMDS_MeshNode* prevNode = theN1;
6252 std::vector<const SMDS_MeshNode*> aNodesList;
6253 aNodesList.push_back(theN1);
6254 int nbEdges = 0, conn=0;
6255 const SMDS_MeshElement* prevElem = NULL;
6256 const SMDS_MeshElement* currentElem = NULL;
6257 int totalNbEdges = theTrack->NbEdges();
6258 SMDS_ElemIteratorPtr nIt;
6261 if( !theTrack->GetMeshDS()->Contains(theN1) ) {
6262 return EXTR_BAD_STARTING_NODE;
6265 conn = nbEdgeConnectivity(theN1);
6267 return EXTR_PATH_NOT_EDGE;
6269 aItE = theN1->GetInverseElementIterator();
6270 prevElem = aItE->next();
6271 currentElem = prevElem;
6273 if(totalNbEdges == 1 ) {
6274 nIt = currentElem->nodesIterator();
6275 currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
6276 if(currentNode == prevNode)
6277 currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
6278 aNodesList.push_back(currentNode);
6280 nIt = currentElem->nodesIterator();
6281 while( nIt->more() ) {
6282 currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
6283 if(currentNode == prevNode)
6284 currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
6285 aNodesList.push_back(currentNode);
6287 //case of the closed mesh
6288 if(currentNode == theN1) {
6293 conn = nbEdgeConnectivity(currentNode);
6295 return EXTR_PATH_NOT_EDGE;
6296 }else if( conn == 1 && nbEdges > 0 ) {
6301 prevNode = currentNode;
6302 aItE = currentNode->GetInverseElementIterator();
6303 currentElem = aItE->next();
6304 if( currentElem == prevElem)
6305 currentElem = aItE->next();
6306 nIt = currentElem->nodesIterator();
6307 prevElem = currentElem;
6313 if(nbEdges != totalNbEdges)
6314 return EXTR_PATH_NOT_EDGE;
6316 TopTools_SequenceOfShape Edges;
6317 list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
6318 int startNid = theN1->GetID();
6319 for ( size_t i = 1; i < aNodesList.size(); i++ )
6321 gp_Pnt p1 = SMESH_TNodeXYZ( aNodesList[i-1] );
6322 gp_Pnt p2 = SMESH_TNodeXYZ( aNodesList[i] );
6323 TopoDS_Edge e = BRepBuilderAPI_MakeEdge( p1, p2 );
6324 list<SMESH_MeshEditor_PathPoint> LPP;
6326 makeEdgePathPoints(aPrms, e, (aNodesList[i-1]->GetID()==startNid), LPP);
6327 LLPPs.push_back(LPP);
6328 if ( aNodesList[i-1]->GetID() == startNid ) startNid = aNodesList[i ]->GetID();
6329 else startNid = aNodesList[i-1]->GetID();
6332 list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
6333 list<SMESH_MeshEditor_PathPoint> firstList = *itLLPP;
6334 list<SMESH_MeshEditor_PathPoint>::iterator itPP = firstList.begin();
6335 for(; itPP!=firstList.end(); itPP++) {
6336 fullList.push_back( *itPP );
6339 SMESH_MeshEditor_PathPoint PP1 = fullList.back();
6340 SMESH_MeshEditor_PathPoint PP2;
6341 fullList.pop_back();
6343 for(; itLLPP!=LLPPs.end(); itLLPP++) {
6344 list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
6345 itPP = currList.begin();
6346 PP2 = currList.front();
6347 gp_Dir D1 = PP1.Tangent();
6348 gp_Dir D2 = PP2.Tangent();
6349 gp_Dir Dnew( 0.5 * ( D1.XYZ() + D2.XYZ() ));
6350 PP1.SetTangent(Dnew);
6351 fullList.push_back(PP1);
6353 for(; itPP!=currList.end(); itPP++) {
6354 fullList.push_back( *itPP );
6356 PP1 = fullList.back();
6357 fullList.pop_back();
6359 fullList.push_back(PP1);
6361 } // Sub-shape for the Pattern must be an Edge or Wire
6362 else if ( aS.ShapeType() == TopAbs_EDGE )
6364 aTrackEdge = TopoDS::Edge( aS );
6365 // the Edge must not be degenerated
6366 if ( SMESH_Algo::isDegenerated( aTrackEdge ) )
6367 return EXTR_BAD_PATH_SHAPE;
6368 TopExp::Vertices( aTrackEdge, aV1, aV2 );
6369 const SMDS_MeshNode* aN1 = SMESH_Algo::VertexNode( aV1, pMeshDS );
6370 const SMDS_MeshNode* aN2 = SMESH_Algo::VertexNode( aV2, pMeshDS );
6371 // starting node must be aN1 or aN2
6372 if ( !( aN1 == theN1 || aN2 == theN1 ) )
6373 return EXTR_BAD_STARTING_NODE;
6374 aItN = pMeshDS->nodesIterator();
6375 while ( aItN->more() ) {
6376 const SMDS_MeshNode* pNode = aItN->next();
6377 if( pNode==aN1 || pNode==aN2 ) continue;
6378 const SMDS_EdgePosition* pEPos =
6379 static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
6380 double aT = pEPos->GetUParameter();
6381 aPrms.push_back( aT );
6383 //Extrusion_Error err =
6384 makeEdgePathPoints(aPrms, aTrackEdge, (aN1==theN1), fullList);
6386 else if( aS.ShapeType() == TopAbs_WIRE ) {
6387 list< SMESH_subMesh* > LSM;
6388 TopTools_SequenceOfShape Edges;
6389 TopExp_Explorer eExp(aS, TopAbs_EDGE);
6390 for(; eExp.More(); eExp.Next()) {
6391 TopoDS_Edge E = TopoDS::Edge( eExp.Current() );
6392 if( SMESH_Algo::isDegenerated(E) ) continue;
6393 SMESH_subMesh* SM = theTrack->GetSubMesh(E);
6399 list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
6400 TopoDS_Vertex aVprev;
6401 TColStd_MapOfInteger UsedNums;
6402 int NbEdges = Edges.Length();
6404 for(; i<=NbEdges; i++) {
6406 list< SMESH_subMesh* >::iterator itLSM = LSM.begin();
6407 for(; itLSM!=LSM.end(); itLSM++) {
6409 if(UsedNums.Contains(k)) continue;
6410 aTrackEdge = TopoDS::Edge( Edges.Value(k) );
6411 SMESH_subMesh* locTrack = *itLSM;
6412 SMESHDS_SubMesh* locMeshDS = locTrack->GetSubMeshDS();
6413 TopExp::Vertices( aTrackEdge, aV1, aV2 );
6414 bool aN1isOK = false, aN2isOK = false;
6415 if ( aVprev.IsNull() ) {
6416 // if previous vertex is not yet defined, it means that we in the beginning of wire
6417 // and we have to find initial vertex corresponding to starting node theN1
6418 const SMDS_MeshNode* aN1 = SMESH_Algo::VertexNode( aV1, pMeshDS );
6419 const SMDS_MeshNode* aN2 = SMESH_Algo::VertexNode( aV2, pMeshDS );
6420 // starting node must be aN1 or aN2
6421 aN1isOK = ( aN1 && aN1 == theN1 );
6422 aN2isOK = ( aN2 && aN2 == theN1 );
6425 // we have specified ending vertex of the previous edge on the previous iteration
6426 // and we have just to check that it corresponds to any vertex in current segment
6427 aN1isOK = aVprev.IsSame( aV1 );
6428 aN2isOK = aVprev.IsSame( aV2 );
6430 if ( !aN1isOK && !aN2isOK ) continue;
6431 // 2. Collect parameters on the track edge
6433 aItN = locMeshDS->GetNodes();
6434 while ( aItN->more() ) {
6435 const SMDS_MeshNode* pNode = aItN->next();
6436 const SMDS_EdgePosition* pEPos =
6437 static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
6438 double aT = pEPos->GetUParameter();
6439 aPrms.push_back( aT );
6441 list<SMESH_MeshEditor_PathPoint> LPP;
6442 //Extrusion_Error err =
6443 makeEdgePathPoints(aPrms, aTrackEdge, aN1isOK, LPP);
6444 LLPPs.push_back(LPP);
6446 // update startN for search following egde
6447 if ( aN1isOK ) aVprev = aV2;
6452 list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
6453 list<SMESH_MeshEditor_PathPoint>& firstList = *itLLPP;
6454 fullList.splice( fullList.end(), firstList );
6456 SMESH_MeshEditor_PathPoint PP1 = fullList.back();
6457 fullList.pop_back();
6459 for(; itLLPP!=LLPPs.end(); itLLPP++) {
6460 list<SMESH_MeshEditor_PathPoint>& currList = *itLLPP;
6461 SMESH_MeshEditor_PathPoint PP2 = currList.front();
6462 gp_Dir D1 = PP1.Tangent();
6463 gp_Dir D2 = PP2.Tangent();
6464 gp_Dir Dnew( D1.XYZ() + D2.XYZ() );
6465 PP1.SetTangent(Dnew);
6466 fullList.push_back(PP1);
6467 fullList.splice( fullList.end(), currList, ++currList.begin(), currList.end() );
6468 PP1 = fullList.back();
6469 fullList.pop_back();
6471 // if wire not closed
6472 fullList.push_back(PP1);
6476 return EXTR_BAD_PATH_SHAPE;
6479 return makeExtrElements(theElements, fullList, theHasAngles, theAngles, theLinearVariation,
6480 theHasRefPoint, theRefPoint, theMakeGroups);
6484 //=======================================================================
6485 //function : makeEdgePathPoints
6486 //purpose : auxiliary for ExtrusionAlongTrack
6487 //=======================================================================
6488 SMESH_MeshEditor::Extrusion_Error
6489 SMESH_MeshEditor::makeEdgePathPoints(std::list<double>& aPrms,
6490 const TopoDS_Edge& aTrackEdge,
6492 list<SMESH_MeshEditor_PathPoint>& LPP)
6494 Standard_Real aTx1, aTx2, aL2, aTolVec, aTolVec2;
6496 aTolVec2=aTolVec*aTolVec;
6498 TopoDS_Vertex aV1, aV2;
6499 TopExp::Vertices( aTrackEdge, aV1, aV2 );
6500 aT1=BRep_Tool::Parameter( aV1, aTrackEdge );
6501 aT2=BRep_Tool::Parameter( aV2, aTrackEdge );
6502 // 2. Collect parameters on the track edge
6503 aPrms.push_front( aT1 );
6504 aPrms.push_back( aT2 );
6507 if( FirstIsStart ) {
6518 SMESH_MeshEditor_PathPoint aPP;
6519 Handle(Geom_Curve) aC3D = BRep_Tool::Curve( aTrackEdge, aTx1, aTx2 );
6520 std::list<double>::iterator aItD = aPrms.begin();
6521 for(; aItD != aPrms.end(); ++aItD) {
6525 aC3D->D1( aT, aP3D, aVec );
6526 aL2 = aVec.SquareMagnitude();
6527 if ( aL2 < aTolVec2 )
6528 return EXTR_CANT_GET_TANGENT;
6529 gp_Dir aTgt( FirstIsStart ? aVec : -aVec );
6531 aPP.SetTangent( aTgt );
6532 aPP.SetParameter( aT );
6539 //=======================================================================
6540 //function : makeExtrElements
6541 //purpose : auxiliary for ExtrusionAlongTrack
6542 //=======================================================================
6543 SMESH_MeshEditor::Extrusion_Error
6544 SMESH_MeshEditor::makeExtrElements(TIDSortedElemSet theElemSets[2],
6545 list<SMESH_MeshEditor_PathPoint>& fullList,
6546 const bool theHasAngles,
6547 list<double>& theAngles,
6548 const bool theLinearVariation,
6549 const bool theHasRefPoint,
6550 const gp_Pnt& theRefPoint,
6551 const bool theMakeGroups)
6553 const int aNbTP = fullList.size();
6556 if( theHasAngles && !theAngles.empty() && theLinearVariation )
6557 linearAngleVariation(aNbTP-1, theAngles);
6559 // fill vector of path points with angles
6560 vector<SMESH_MeshEditor_PathPoint> aPPs;
6561 list<SMESH_MeshEditor_PathPoint>::iterator itPP = fullList.begin();
6562 list<double>::iterator itAngles = theAngles.begin();
6563 aPPs.push_back( *itPP++ );
6564 for( ; itPP != fullList.end(); itPP++) {
6565 aPPs.push_back( *itPP );
6566 if ( theHasAngles && itAngles != theAngles.end() )
6567 aPPs.back().SetAngle( *itAngles++ );
6570 TNodeOfNodeListMap mapNewNodes;
6571 TElemOfVecOfNnlmiMap mapElemNewNodes;
6572 TTElemOfElemListMap newElemsMap;
6573 TIDSortedElemSet::iterator itElem;
6574 // source elements for each generated one
6575 SMESH_SequenceOfElemPtr srcElems, srcNodes;
6577 // 3. Center of rotation aV0
6578 gp_Pnt aV0 = theRefPoint;
6579 if ( !theHasRefPoint )
6581 gp_XYZ aGC( 0.,0.,0. );
6582 TIDSortedElemSet newNodes;
6584 for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
6586 TIDSortedElemSet& theElements = theElemSets[ is2ndSet ];
6587 itElem = theElements.begin();
6588 for ( ; itElem != theElements.end(); itElem++ )
6590 const SMDS_MeshElement* elem = *itElem;
6591 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6592 while ( itN->more() ) {
6593 const SMDS_MeshElement* node = itN->next();
6594 if ( newNodes.insert( node ).second )
6595 aGC += SMESH_TNodeXYZ( node );
6599 aGC /= newNodes.size();
6601 } // if (!theHasRefPoint) {
6603 // 4. Processing the elements
6604 SMESHDS_Mesh* aMesh = GetMeshDS();
6605 list<const SMDS_MeshNode*> emptyList;
6607 setElemsFirst( theElemSets );
6608 for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
6610 TIDSortedElemSet& theElements = theElemSets[ is2ndSet ];
6611 for ( itElem = theElements.begin(); itElem != theElements.end(); itElem++ )
6613 const SMDS_MeshElement* elem = *itElem;
6615 vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
6616 newNodesItVec.reserve( elem->NbNodes() );
6618 // loop on elem nodes
6620 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6621 while ( itN->more() )
6624 // check if a node has been already processed
6625 const SMDS_MeshNode* node = cast2Node( itN->next() );
6626 TNodeOfNodeListMap::iterator nIt = mapNewNodes.insert( make_pair( node, emptyList )).first;
6627 list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
6628 if ( listNewNodes.empty() )
6631 Standard_Real aAngle1x, aAngleT1T0, aTolAng;
6632 gp_Pnt aP0x, aP1x, aPN0, aPN1, aV0x, aV1x;
6633 gp_Ax1 anAx1, anAxT1T0;
6634 gp_Dir aDT1x, aDT0x, aDT1T0;
6639 aPN0 = SMESH_TNodeXYZ( node );
6641 const SMESH_MeshEditor_PathPoint& aPP0 = aPPs[0];
6643 aDT0x= aPP0.Tangent();
6645 for ( int j = 1; j < aNbTP; ++j ) {
6646 const SMESH_MeshEditor_PathPoint& aPP1 = aPPs[j];
6648 aDT1x = aPP1.Tangent();
6649 aAngle1x = aPP1.Angle();
6651 gp_Trsf aTrsf, aTrsfRot, aTrsfRotT1T0;
6653 gp_Vec aV01x( aP0x, aP1x );
6654 aTrsf.SetTranslation( aV01x );
6657 aV1x = aV0x.Transformed( aTrsf );
6658 aPN1 = aPN0.Transformed( aTrsf );
6660 // rotation 1 [ T1,T0 ]
6661 aAngleT1T0=-aDT1x.Angle( aDT0x );
6662 if (fabs(aAngleT1T0) > aTolAng)
6665 anAxT1T0.SetLocation( aV1x );
6666 anAxT1T0.SetDirection( aDT1T0 );
6667 aTrsfRotT1T0.SetRotation( anAxT1T0, aAngleT1T0 );
6669 aPN1 = aPN1.Transformed( aTrsfRotT1T0 );
6673 if ( theHasAngles ) {
6674 anAx1.SetLocation( aV1x );
6675 anAx1.SetDirection( aDT1x );
6676 aTrsfRot.SetRotation( anAx1, aAngle1x );
6678 aPN1 = aPN1.Transformed( aTrsfRot );
6682 if ( elem->IsQuadratic() && !elem->IsMediumNode(node) )
6684 // create additional node
6685 gp_XYZ midP = 0.5 * ( aPN1.XYZ() + aPN0.XYZ() );
6686 const SMDS_MeshNode* newNode = aMesh->AddNode( midP.X(), midP.Y(), midP.Z() );
6687 myLastCreatedNodes.Append(newNode);
6688 srcNodes.Append( node );
6689 listNewNodes.push_back( newNode );
6691 const SMDS_MeshNode* newNode = aMesh->AddNode( aPN1.X(), aPN1.Y(), aPN1.Z() );
6692 myLastCreatedNodes.Append(newNode);
6693 srcNodes.Append( node );
6694 listNewNodes.push_back( newNode );
6702 else if( elem->IsQuadratic() && !elem->IsMediumNode(node) )
6704 // if current elem is quadratic and current node is not medium
6705 // we have to check - may be it is needed to insert additional nodes
6706 list< const SMDS_MeshNode* > & listNewNodes = nIt->second;
6707 if ((int) listNewNodes.size() == aNbTP-1 )
6709 vector<const SMDS_MeshNode*> aNodes(2*(aNbTP-1));
6710 gp_XYZ P(node->X(), node->Y(), node->Z());
6711 list< const SMDS_MeshNode* >::iterator it = listNewNodes.begin();
6713 for(i=0; i<aNbTP-1; i++) {
6714 const SMDS_MeshNode* N = *it;
6715 double x = ( N->X() + P.X() )/2.;
6716 double y = ( N->Y() + P.Y() )/2.;
6717 double z = ( N->Z() + P.Z() )/2.;
6718 const SMDS_MeshNode* newN = aMesh->AddNode(x,y,z);
6719 srcNodes.Append( node );
6720 myLastCreatedNodes.Append(newN);
6723 P = gp_XYZ(N->X(),N->Y(),N->Z());
6725 listNewNodes.clear();
6726 for(i=0; i<2*(aNbTP-1); i++) {
6727 listNewNodes.push_back(aNodes[i]);
6732 newNodesItVec.push_back( nIt );
6735 // make new elements
6736 sweepElement( elem, newNodesItVec, newElemsMap[elem], aNbTP-1, srcElems );
6740 makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElemSets[0], aNbTP-1, srcElems );
6742 if ( theMakeGroups )
6743 generateGroups( srcNodes, srcElems, "extruded");
6749 //=======================================================================
6750 //function : linearAngleVariation
6751 //purpose : spread values over nbSteps
6752 //=======================================================================
6754 void SMESH_MeshEditor::linearAngleVariation(const int nbSteps,
6755 list<double>& Angles)
6757 int nbAngles = Angles.size();
6758 if( nbSteps > nbAngles && nbAngles > 0 )
6760 vector<double> theAngles(nbAngles);
6761 theAngles.assign( Angles.begin(), Angles.end() );
6764 double rAn2St = double( nbAngles ) / double( nbSteps );
6765 double angPrev = 0, angle;
6766 for ( int iSt = 0; iSt < nbSteps; ++iSt )
6768 double angCur = rAn2St * ( iSt+1 );
6769 double angCurFloor = floor( angCur );
6770 double angPrevFloor = floor( angPrev );
6771 if ( angPrevFloor == angCurFloor )
6772 angle = rAn2St * theAngles[ int( angCurFloor ) ];
6774 int iP = int( angPrevFloor );
6775 double angPrevCeil = ceil(angPrev);
6776 angle = ( angPrevCeil - angPrev ) * theAngles[ iP ];
6778 int iC = int( angCurFloor );
6779 if ( iC < nbAngles )
6780 angle += ( angCur - angCurFloor ) * theAngles[ iC ];
6782 iP = int( angPrevCeil );
6784 angle += theAngles[ iC ];
6786 res.push_back(angle);
6794 //================================================================================
6796 * \brief Move or copy theElements applying theTrsf to their nodes
6797 * \param theElems - elements to transform, if theElems is empty then apply to all mesh nodes
6798 * \param theTrsf - transformation to apply
6799 * \param theCopy - if true, create translated copies of theElems
6800 * \param theMakeGroups - if true and theCopy, create translated groups
6801 * \param theTargetMesh - mesh to copy translated elements into
6802 * \return SMESH_MeshEditor::PGroupIDs - list of ids of created groups
6804 //================================================================================
6806 SMESH_MeshEditor::PGroupIDs
6807 SMESH_MeshEditor::Transform (TIDSortedElemSet & theElems,
6808 const gp_Trsf& theTrsf,
6810 const bool theMakeGroups,
6811 SMESH_Mesh* theTargetMesh)
6813 myLastCreatedElems.Clear();
6814 myLastCreatedNodes.Clear();
6816 bool needReverse = false;
6817 string groupPostfix;
6818 switch ( theTrsf.Form() ) {
6821 groupPostfix = "mirrored";
6824 groupPostfix = "mirrored";
6828 groupPostfix = "mirrored";
6831 groupPostfix = "rotated";
6833 case gp_Translation:
6834 groupPostfix = "translated";
6837 groupPostfix = "scaled";
6839 case gp_CompoundTrsf: // different scale by axis
6840 groupPostfix = "scaled";
6843 needReverse = false;
6844 groupPostfix = "transformed";
6847 SMESHDS_Mesh* aTgtMesh = theTargetMesh ? theTargetMesh->GetMeshDS() : 0;
6848 SMESHDS_Mesh* aMesh = GetMeshDS();
6850 SMESH_MeshEditor targetMeshEditor( theTargetMesh );
6851 SMESH_MeshEditor* editor = theTargetMesh ? & targetMeshEditor : theCopy ? this : 0;
6852 SMESH_MeshEditor::ElemFeatures elemType;
6854 // map old node to new one
6855 TNodeNodeMap nodeMap;
6857 // elements sharing moved nodes; those of them which have all
6858 // nodes mirrored but are not in theElems are to be reversed
6859 TIDSortedElemSet inverseElemSet;
6861 // source elements for each generated one
6862 SMESH_SequenceOfElemPtr srcElems, srcNodes;
6864 // issue 021015: EDF 1578 SMESH: Free nodes are removed when translating a mesh
6865 TIDSortedElemSet orphanNode;
6867 if ( theElems.empty() ) // transform the whole mesh
6870 SMDS_ElemIteratorPtr eIt = aMesh->elementsIterator();
6871 while ( eIt->more() ) theElems.insert( eIt->next() );
6873 SMDS_NodeIteratorPtr nIt = aMesh->nodesIterator();
6874 while ( nIt->more() )
6876 const SMDS_MeshNode* node = nIt->next();
6877 if ( node->NbInverseElements() == 0)
6878 orphanNode.insert( node );
6882 // loop on elements to transform nodes : first orphan nodes then elems
6883 TIDSortedElemSet::iterator itElem;
6884 TIDSortedElemSet *elements[] = { &orphanNode, &theElems };
6885 for (int i=0; i<2; i++)
6886 for ( itElem = elements[i]->begin(); itElem != elements[i]->end(); itElem++ )
6888 const SMDS_MeshElement* elem = *itElem;
6892 // loop on elem nodes
6894 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6895 while ( itN->more() )
6897 const SMDS_MeshNode* node = cast2Node( itN->next() );
6898 // check if a node has been already transformed
6899 pair<TNodeNodeMap::iterator,bool> n2n_isnew =
6900 nodeMap.insert( make_pair ( node, node ));
6901 if ( !n2n_isnew.second )
6904 node->GetXYZ( coord );
6905 theTrsf.Transforms( coord[0], coord[1], coord[2] );
6906 if ( theTargetMesh ) {
6907 const SMDS_MeshNode * newNode = aTgtMesh->AddNode( coord[0], coord[1], coord[2] );
6908 n2n_isnew.first->second = newNode;
6909 myLastCreatedNodes.Append(newNode);
6910 srcNodes.Append( node );
6912 else if ( theCopy ) {
6913 const SMDS_MeshNode * newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
6914 n2n_isnew.first->second = newNode;
6915 myLastCreatedNodes.Append(newNode);
6916 srcNodes.Append( node );
6919 aMesh->MoveNode( node, coord[0], coord[1], coord[2] );
6920 // node position on shape becomes invalid
6921 const_cast< SMDS_MeshNode* > ( node )->SetPosition
6922 ( SMDS_SpacePosition::originSpacePosition() );
6925 // keep inverse elements
6926 if ( !theCopy && !theTargetMesh && needReverse ) {
6927 SMDS_ElemIteratorPtr invElemIt = node->GetInverseElementIterator();
6928 while ( invElemIt->more() ) {
6929 const SMDS_MeshElement* iel = invElemIt->next();
6930 inverseElemSet.insert( iel );
6934 } // loop on elems in { &orphanNode, &theElems };
6936 // either create new elements or reverse mirrored ones
6937 if ( !theCopy && !needReverse && !theTargetMesh )
6940 theElems.insert( inverseElemSet.begin(),inverseElemSet.end() );
6942 // Replicate or reverse elements
6944 std::vector<int> iForw;
6945 vector<const SMDS_MeshNode*> nodes;
6946 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
6948 const SMDS_MeshElement* elem = *itElem;
6949 if ( !elem ) continue;
6951 SMDSAbs_GeometryType geomType = elem->GetGeomType();
6952 size_t nbNodes = elem->NbNodes();
6953 if ( geomType == SMDSGeom_NONE ) continue; // node
6955 nodes.resize( nbNodes );
6957 if ( geomType == SMDSGeom_POLYHEDRA ) // ------------------ polyhedral volume
6959 const SMDS_VtkVolume* aPolyedre = dynamic_cast<const SMDS_VtkVolume*>( elem );
6963 bool allTransformed = true;
6964 int nbFaces = aPolyedre->NbFaces();
6965 for (int iface = 1; iface <= nbFaces && allTransformed; iface++)
6967 int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
6968 for (int inode = 1; inode <= nbFaceNodes && allTransformed; inode++)
6970 const SMDS_MeshNode* node = aPolyedre->GetFaceNode(iface, inode);
6971 TNodeNodeMap::iterator nodeMapIt = nodeMap.find(node);
6972 if ( nodeMapIt == nodeMap.end() )
6973 allTransformed = false; // not all nodes transformed
6975 nodes.push_back((*nodeMapIt).second);
6977 if ( needReverse && allTransformed )
6978 std::reverse( nodes.end() - nbFaceNodes, nodes.end() );
6980 if ( !allTransformed )
6981 continue; // not all nodes transformed
6983 else // ----------------------- the rest element types
6985 while ( iForw.size() < nbNodes ) iForw.push_back( iForw.size() );
6986 const vector<int>& iRev = SMDS_MeshCell::reverseSmdsOrder( elem->GetEntityType(), nbNodes );
6987 const vector<int>& i = needReverse ? iRev : iForw;
6989 // find transformed nodes
6991 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6992 while ( itN->more() ) {
6993 const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( itN->next() );
6994 TNodeNodeMap::iterator nodeMapIt = nodeMap.find( node );
6995 if ( nodeMapIt == nodeMap.end() )
6996 break; // not all nodes transformed
6997 nodes[ i [ iNode++ ]] = (*nodeMapIt).second;
6999 if ( iNode != nbNodes )
7000 continue; // not all nodes transformed
7004 // copy in this or a new mesh
7005 if ( editor->AddElement( nodes, elemType.Init( elem, /*basicOnly=*/false )))
7006 srcElems.Append( elem );
7009 // reverse element as it was reversed by transformation
7011 aMesh->ChangeElementNodes( elem, &nodes[0], nbNodes );
7014 } // loop on elements
7016 if ( editor && editor != this )
7017 myLastCreatedElems = editor->myLastCreatedElems;
7019 PGroupIDs newGroupIDs;
7021 if ( ( theMakeGroups && theCopy ) ||
7022 ( theMakeGroups && theTargetMesh ) )
7023 newGroupIDs = generateGroups( srcNodes, srcElems, groupPostfix, theTargetMesh, false );
7028 //=======================================================================
7030 * \brief Create groups of elements made during transformation
7031 * \param nodeGens - nodes making corresponding myLastCreatedNodes
7032 * \param elemGens - elements making corresponding myLastCreatedElems
7033 * \param postfix - to append to names of new groups
7034 * \param targetMesh - mesh to create groups in
7035 * \param topPresent - is there "top" elements that are created by sweeping
7037 //=======================================================================
7039 SMESH_MeshEditor::PGroupIDs
7040 SMESH_MeshEditor::generateGroups(const SMESH_SequenceOfElemPtr& nodeGens,
7041 const SMESH_SequenceOfElemPtr& elemGens,
7042 const std::string& postfix,
7043 SMESH_Mesh* targetMesh,
7044 const bool topPresent)
7046 PGroupIDs newGroupIDs( new list<int> );
7047 SMESH_Mesh* mesh = targetMesh ? targetMesh : GetMesh();
7049 // Sort existing groups by types and collect their names
7051 // containers to store an old group and generated new ones;
7052 // 1st new group is for result elems of different type than a source one;
7053 // 2nd new group is for same type result elems ("top" group at extrusion)
7055 using boost::make_tuple;
7056 typedef tuple< SMESHDS_GroupBase*, SMESHDS_Group*, SMESHDS_Group* > TOldNewGroup;
7057 vector< list< TOldNewGroup > > groupsByType( SMDSAbs_NbElementTypes );
7058 vector< TOldNewGroup* > orderedOldNewGroups; // in order of old groups
7060 set< string > groupNames;
7062 SMESH_Mesh::GroupIteratorPtr groupIt = GetMesh()->GetGroups();
7063 if ( !groupIt->more() ) return newGroupIDs;
7065 int newGroupID = mesh->GetGroupIds().back()+1;
7066 while ( groupIt->more() )
7068 SMESH_Group * group = groupIt->next();
7069 if ( !group ) continue;
7070 SMESHDS_GroupBase* groupDS = group->GetGroupDS();
7071 if ( !groupDS || groupDS->IsEmpty() ) continue;
7072 groupNames.insert ( group->GetName() );
7073 groupDS->SetStoreName( group->GetName() );
7074 const SMDSAbs_ElementType type = groupDS->GetType();
7075 SMESHDS_Group* newGroup = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
7076 SMESHDS_Group* newTopGroup = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
7077 groupsByType[ type ].push_back( make_tuple( groupDS, newGroup, newTopGroup ));
7078 orderedOldNewGroups.push_back( & groupsByType[ type ].back() );
7081 // Loop on nodes and elements to add them in new groups
7083 vector< const SMDS_MeshElement* > resultElems;
7084 for ( int isNodes = 0; isNodes < 2; ++isNodes )
7086 const SMESH_SequenceOfElemPtr& gens = isNodes ? nodeGens : elemGens;
7087 const SMESH_SequenceOfElemPtr& elems = isNodes ? myLastCreatedNodes : myLastCreatedElems;
7088 if ( gens.Length() != elems.Length() )
7089 throw SALOME_Exception("SMESH_MeshEditor::generateGroups(): invalid args");
7091 // loop on created elements
7092 for (int iElem = 1; iElem <= elems.Length(); ++iElem )
7094 const SMDS_MeshElement* sourceElem = gens( iElem );
7095 if ( !sourceElem ) {
7096 MESSAGE("generateGroups(): NULL source element");
7099 list< TOldNewGroup > & groupsOldNew = groupsByType[ sourceElem->GetType() ];
7100 if ( groupsOldNew.empty() ) { // no groups of this type at all
7101 while ( iElem < gens.Length() && gens( iElem+1 ) == sourceElem )
7102 ++iElem; // skip all elements made by sourceElem
7105 // collect all elements made by the iElem-th sourceElem
7106 resultElems.clear();
7107 if ( const SMDS_MeshElement* resElem = elems( iElem ))
7108 if ( resElem != sourceElem )
7109 resultElems.push_back( resElem );
7110 while ( iElem < gens.Length() && gens( iElem+1 ) == sourceElem )
7111 if ( const SMDS_MeshElement* resElem = elems( ++iElem ))
7112 if ( resElem != sourceElem )
7113 resultElems.push_back( resElem );
7115 const SMDS_MeshElement* topElem = 0;
7116 if ( isNodes ) // there must be a top element
7118 topElem = resultElems.back();
7119 resultElems.pop_back();
7123 vector< const SMDS_MeshElement* >::reverse_iterator resElemIt = resultElems.rbegin();
7124 for ( ; resElemIt != resultElems.rend() ; ++resElemIt )
7125 if ( (*resElemIt)->GetType() == sourceElem->GetType() )
7127 topElem = *resElemIt;
7128 *resElemIt = 0; // erase *resElemIt
7132 // add resultElems to groups originted from ones the sourceElem belongs to
7133 list< TOldNewGroup >::iterator gOldNew, gLast = groupsOldNew.end();
7134 for ( gOldNew = groupsOldNew.begin(); gOldNew != gLast; ++gOldNew )
7136 SMESHDS_GroupBase* oldGroup = gOldNew->get<0>();
7137 if ( oldGroup->Contains( sourceElem )) // sourceElem is in oldGroup
7139 // fill in a new group
7140 SMDS_MeshGroup & newGroup = gOldNew->get<1>()->SMDSGroup();
7141 vector< const SMDS_MeshElement* >::iterator resLast = resultElems.end(), resElemIt;
7142 for ( resElemIt = resultElems.begin(); resElemIt != resLast; ++resElemIt )
7144 newGroup.Add( *resElemIt );
7146 // fill a "top" group
7149 SMDS_MeshGroup & newTopGroup = gOldNew->get<2>()->SMDSGroup();
7150 newTopGroup.Add( topElem );
7154 } // loop on created elements
7155 }// loop on nodes and elements
7157 // Create new SMESH_Groups from SMESHDS_Groups and remove empty SMESHDS_Groups
7159 list<int> topGrouIds;
7160 for ( size_t i = 0; i < orderedOldNewGroups.size(); ++i )
7162 SMESHDS_GroupBase* oldGroupDS = orderedOldNewGroups[i]->get<0>();
7163 SMESHDS_Group* newGroups[2] = { orderedOldNewGroups[i]->get<1>(),
7164 orderedOldNewGroups[i]->get<2>() };
7165 for ( int is2nd = 0; is2nd < 2; ++is2nd )
7167 SMESHDS_Group* newGroupDS = newGroups[ is2nd ];
7168 if ( newGroupDS->IsEmpty() )
7170 mesh->GetMeshDS()->RemoveGroup( newGroupDS );
7175 newGroupDS->SetType( newGroupDS->GetElements()->next()->GetType() );
7178 const bool isTop = ( topPresent &&
7179 newGroupDS->GetType() == oldGroupDS->GetType() &&
7182 string name = oldGroupDS->GetStoreName();
7183 { // remove trailing whitespaces (issue 22599)
7184 size_t size = name.size();
7185 while ( size > 1 && isspace( name[ size-1 ]))
7187 if ( size != name.size() )
7189 name.resize( size );
7190 oldGroupDS->SetStoreName( name.c_str() );
7193 if ( !targetMesh ) {
7194 string suffix = ( isTop ? "top": postfix.c_str() );
7198 while ( !groupNames.insert( name ).second ) // name exists
7199 name = SMESH_Comment( oldGroupDS->GetStoreName() ) << "_" << suffix << "_" << nb++;
7204 newGroupDS->SetStoreName( name.c_str() );
7206 // make a SMESH_Groups
7207 mesh->AddGroup( newGroupDS );
7209 topGrouIds.push_back( newGroupDS->GetID() );
7211 newGroupIDs->push_back( newGroupDS->GetID() );
7215 newGroupIDs->splice( newGroupIDs->end(), topGrouIds );
7220 //================================================================================
7222 * * \brief Return list of group of nodes close to each other within theTolerance
7223 * * Search among theNodes or in the whole mesh if theNodes is empty using
7224 * * an Octree algorithm
7225 * \param [in,out] theNodes - the nodes to treat
7226 * \param [in] theTolerance - the tolerance
7227 * \param [out] theGroupsOfNodes - the result groups of coincident nodes
7228 * \param [in] theSeparateCornersAndMedium - if \c true, in quadratic mesh puts
7229 * corner and medium nodes in separate groups
7231 //================================================================================
7233 void SMESH_MeshEditor::FindCoincidentNodes (TIDSortedNodeSet & theNodes,
7234 const double theTolerance,
7235 TListOfListOfNodes & theGroupsOfNodes,
7236 bool theSeparateCornersAndMedium)
7238 myLastCreatedElems.Clear();
7239 myLastCreatedNodes.Clear();
7241 if ( myMesh->NbEdges ( ORDER_QUADRATIC ) +
7242 myMesh->NbFaces ( ORDER_QUADRATIC ) +
7243 myMesh->NbVolumes( ORDER_QUADRATIC ) == 0 )
7244 theSeparateCornersAndMedium = false;
7246 TIDSortedNodeSet& corners = theNodes;
7247 TIDSortedNodeSet medium;
7249 if ( theNodes.empty() ) // get all nodes in the mesh
7251 TIDSortedNodeSet* nodes[2] = { &corners, &medium };
7252 SMDS_NodeIteratorPtr nIt = GetMeshDS()->nodesIterator(/*idInceasingOrder=*/true);
7253 if ( theSeparateCornersAndMedium )
7254 while ( nIt->more() )
7256 const SMDS_MeshNode* n = nIt->next();
7257 TIDSortedNodeSet* & nodeSet = nodes[ SMESH_MesherHelper::IsMedium( n )];
7258 nodeSet->insert( nodeSet->end(), n );
7261 while ( nIt->more() )
7262 theNodes.insert( theNodes.end(), nIt->next() );
7264 else if ( theSeparateCornersAndMedium ) // separate corners from medium nodes
7266 TIDSortedNodeSet::iterator nIt = corners.begin();
7267 while ( nIt != corners.end() )
7268 if ( SMESH_MesherHelper::IsMedium( *nIt ))
7270 medium.insert( medium.end(), *nIt );
7271 corners.erase( nIt++ );
7279 if ( !corners.empty() )
7280 SMESH_OctreeNode::FindCoincidentNodes ( corners, &theGroupsOfNodes, theTolerance );
7281 if ( !medium.empty() )
7282 SMESH_OctreeNode::FindCoincidentNodes ( medium, &theGroupsOfNodes, theTolerance );
7285 //=======================================================================
7286 //function : SimplifyFace
7287 //purpose : split a chain of nodes into several closed chains
7288 //=======================================================================
7290 int SMESH_MeshEditor::SimplifyFace (const vector<const SMDS_MeshNode *>& faceNodes,
7291 vector<const SMDS_MeshNode *>& poly_nodes,
7292 vector<int>& quantities) const
7294 int nbNodes = faceNodes.size();
7295 while ( faceNodes[ 0 ] == faceNodes[ nbNodes-1 ] && nbNodes > 2 )
7299 size_t prevNbQuant = quantities.size();
7301 vector< const SMDS_MeshNode* > simpleNodes; simpleNodes.reserve( nbNodes );
7302 map< const SMDS_MeshNode*, int > nodeIndices; // indices within simpleNodes
7303 map< const SMDS_MeshNode*, int >::iterator nInd;
7305 nodeIndices.insert( make_pair( faceNodes[0], 0 ));
7306 simpleNodes.push_back( faceNodes[0] );
7307 for ( int iCur = 1; iCur < nbNodes; iCur++ )
7309 if ( faceNodes[ iCur ] != simpleNodes.back() )
7311 int index = simpleNodes.size();
7312 nInd = nodeIndices.insert( make_pair( faceNodes[ iCur ], index )).first;
7313 int prevIndex = nInd->second;
7314 if ( prevIndex < index )
7317 int loopLen = index - prevIndex;
7320 // store the sub-loop
7321 quantities.push_back( loopLen );
7322 for ( int i = prevIndex; i < index; i++ )
7323 poly_nodes.push_back( simpleNodes[ i ]);
7325 simpleNodes.resize( prevIndex+1 );
7329 simpleNodes.push_back( faceNodes[ iCur ]);
7334 if ( simpleNodes.size() > 2 )
7336 quantities.push_back( simpleNodes.size() );
7337 poly_nodes.insert ( poly_nodes.end(), simpleNodes.begin(), simpleNodes.end() );
7340 return quantities.size() - prevNbQuant;
7343 //=======================================================================
7344 //function : MergeNodes
7345 //purpose : In each group, the cdr of nodes are substituted by the first one
7347 //=======================================================================
7349 void SMESH_MeshEditor::MergeNodes (TListOfListOfNodes & theGroupsOfNodes,
7350 const bool theAvoidMakingHoles)
7352 myLastCreatedElems.Clear();
7353 myLastCreatedNodes.Clear();
7355 SMESHDS_Mesh* mesh = GetMeshDS();
7357 TNodeNodeMap nodeNodeMap; // node to replace - new node
7358 set<const SMDS_MeshElement*> elems; // all elements with changed nodes
7359 list< int > rmElemIds, rmNodeIds;
7360 vector< ElemFeatures > newElemDefs;
7362 // Fill nodeNodeMap and elems
7364 TListOfListOfNodes::iterator grIt = theGroupsOfNodes.begin();
7365 for ( ; grIt != theGroupsOfNodes.end(); grIt++ )
7367 list<const SMDS_MeshNode*>& nodes = *grIt;
7368 list<const SMDS_MeshNode*>::iterator nIt = nodes.begin();
7369 const SMDS_MeshNode* nToKeep = *nIt;
7370 for ( ++nIt; nIt != nodes.end(); nIt++ )
7372 const SMDS_MeshNode* nToRemove = *nIt;
7373 nodeNodeMap.insert( make_pair( nToRemove, nToKeep ));
7374 SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
7375 while ( invElemIt->more() ) {
7376 const SMDS_MeshElement* elem = invElemIt->next();
7382 // Apply recursive replacements (BUG 0020185)
7383 TNodeNodeMap::iterator nnIt = nodeNodeMap.begin();
7384 for ( ; nnIt != nodeNodeMap.end(); ++nnIt )
7386 const SMDS_MeshNode* nToKeep = nnIt->second;
7387 TNodeNodeMap::iterator nnIt_i = nodeNodeMap.find( nToKeep );
7388 while ( nnIt_i != nodeNodeMap.end() && nnIt_i->second != nnIt->second )
7389 nToKeep = nnIt_i->second;
7390 nnIt->second = nToKeep;
7393 if ( theAvoidMakingHoles )
7395 // find elements whose topology changes
7397 vector<const SMDS_MeshElement*> pbElems;
7398 set<const SMDS_MeshElement*>::iterator eIt = elems.begin();
7399 for ( ; eIt != elems.end(); ++eIt )
7401 const SMDS_MeshElement* elem = *eIt;
7402 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
7403 while ( itN->more() )
7405 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( itN->next() );
7406 TNodeNodeMap::iterator nnIt = nodeNodeMap.find( n );
7407 if ( nnIt != nodeNodeMap.end() && elem->GetNodeIndex( nnIt->second ) >= 0 )
7409 // several nodes of elem stick
7410 pbElems.push_back( elem );
7415 // exclude from merge nodes causing spoiling element
7416 for ( size_t iLoop = 0; iLoop < pbElems.size(); ++iLoop ) // avoid infinite cycle
7418 bool nodesExcluded = false;
7419 for ( size_t i = 0; i < pbElems.size(); ++i )
7421 size_t prevNbMergeNodes = nodeNodeMap.size();
7422 if ( !applyMerge( pbElems[i], newElemDefs, nodeNodeMap, /*noHoles=*/true ) &&
7423 prevNbMergeNodes < nodeNodeMap.size() )
7424 nodesExcluded = true;
7426 if ( !nodesExcluded )
7431 for ( nnIt = nodeNodeMap.begin(); nnIt != nodeNodeMap.end(); ++nnIt )
7433 const SMDS_MeshNode* nToRemove = nnIt->first;
7434 const SMDS_MeshNode* nToKeep = nnIt->second;
7435 if ( nToRemove != nToKeep )
7437 rmNodeIds.push_back( nToRemove->GetID() );
7438 AddToSameGroups( nToKeep, nToRemove, mesh );
7439 // set _alwaysComputed to a sub-mesh of VERTEX to enable further mesh computing
7440 // w/o creating node in place of merged ones.
7441 const SMDS_PositionPtr& pos = nToRemove->GetPosition();
7442 if ( pos && pos->GetTypeOfPosition() == SMDS_TOP_VERTEX )
7443 if ( SMESH_subMesh* sm = myMesh->GetSubMeshContaining( nToRemove->getshapeId() ))
7444 sm->SetIsAlwaysComputed( true );
7448 // Change element nodes or remove an element
7450 set<const SMDS_MeshElement*>::iterator eIt = elems.begin();
7451 for ( ; eIt != elems.end(); eIt++ )
7453 const SMDS_MeshElement* elem = *eIt;
7454 SMESHDS_SubMesh* sm = mesh->MeshElements( elem->getshapeId() );
7456 bool keepElem = applyMerge( elem, newElemDefs, nodeNodeMap, /*noHoles=*/false );
7458 rmElemIds.push_back( elem->GetID() );
7460 for ( size_t i = 0; i < newElemDefs.size(); ++i )
7462 if ( i > 0 || !mesh->ChangeElementNodes( elem,
7463 & newElemDefs[i].myNodes[0],
7464 newElemDefs[i].myNodes.size() ))
7468 newElemDefs[i].SetID( elem->GetID() );
7469 mesh->RemoveFreeElement(elem, sm, /*fromGroups=*/false);
7470 if ( !keepElem ) rmElemIds.pop_back();
7474 newElemDefs[i].SetID( -1 );
7476 SMDS_MeshElement* newElem = this->AddElement( newElemDefs[i].myNodes, newElemDefs[i] );
7477 if ( sm && newElem )
7478 sm->AddElement( newElem );
7479 if ( elem != newElem )
7480 ReplaceElemInGroups( elem, newElem, mesh );
7485 // Remove bad elements, then equal nodes (order important)
7486 Remove( rmElemIds, /*isNodes=*/false );
7487 Remove( rmNodeIds, /*isNodes=*/true );
7492 //=======================================================================
7493 //function : applyMerge
7494 //purpose : Compute new connectivity of an element after merging nodes
7495 // \param [in] elems - the element
7496 // \param [out] newElemDefs - definition(s) of result element(s)
7497 // \param [inout] nodeNodeMap - nodes to merge
7498 // \param [in] avoidMakingHoles - if true and and the element becomes invalid
7499 // after merging (but not degenerated), removes nodes causing
7500 // the invalidity from \a nodeNodeMap.
7501 // \return bool - true if the element should be removed
7502 //=======================================================================
7504 bool SMESH_MeshEditor::applyMerge( const SMDS_MeshElement* elem,
7505 vector< ElemFeatures >& newElemDefs,
7506 TNodeNodeMap& nodeNodeMap,
7507 const bool avoidMakingHoles )
7509 bool toRemove = false; // to remove elem
7510 int nbResElems = 1; // nb new elements
7512 newElemDefs.resize(nbResElems);
7513 newElemDefs[0].Init( elem );
7514 newElemDefs[0].myNodes.clear();
7516 set<const SMDS_MeshNode*> nodeSet;
7517 vector< const SMDS_MeshNode*> curNodes;
7518 vector< const SMDS_MeshNode*> & uniqueNodes = newElemDefs[0].myNodes;
7521 const int nbNodes = elem->NbNodes();
7522 SMDSAbs_EntityType entity = elem->GetEntityType();
7524 curNodes.resize( nbNodes );
7525 uniqueNodes.resize( nbNodes );
7526 iRepl.resize( nbNodes );
7527 int iUnique = 0, iCur = 0, nbRepl = 0;
7529 // Get new seq of nodes
7531 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
7532 while ( itN->more() )
7534 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( itN->next() );
7536 TNodeNodeMap::iterator nnIt = nodeNodeMap.find( n );
7537 if ( nnIt != nodeNodeMap.end() ) {
7540 curNodes[ iCur ] = n;
7541 bool isUnique = nodeSet.insert( n ).second;
7543 uniqueNodes[ iUnique++ ] = n;
7545 iRepl[ nbRepl++ ] = iCur;
7549 // Analyse element topology after replacement
7551 int nbUniqueNodes = nodeSet.size();
7552 if ( nbNodes != nbUniqueNodes ) // some nodes stick
7557 if ( newElemDefs[0].myIsQuad && newElemDefs[0].myType == SMDSAbs_Face && nbNodes > 6 )
7559 // if corner nodes stick, remove medium nodes between them from uniqueNodes
7560 int nbCorners = nbNodes / 2;
7561 for ( int iCur = 0; iCur < nbCorners; ++iCur )
7563 int iNext = ( iCur + 1 ) % nbCorners;
7564 if ( curNodes[ iCur ] == curNodes[ iNext ] ) // corners stick
7566 int iMedium = iCur + nbCorners;
7567 vector< const SMDS_MeshNode* >::iterator i =
7568 std::find( uniqueNodes.begin() + nbCorners - nbRepl,
7570 curNodes[ iMedium ]);
7571 if ( i != uniqueNodes.end() )
7574 for ( ; i+1 != uniqueNodes.end(); ++i )
7583 case SMDSEntity_Polygon:
7584 case SMDSEntity_Quad_Polygon: // Polygon
7586 ElemFeatures* elemType = & newElemDefs[0];
7587 const bool isQuad = elemType->myIsQuad;
7589 SMDS_MeshCell::applyInterlace // interlace medium and corner nodes
7590 ( SMDS_MeshCell::interlacedSmdsOrder( SMDSEntity_Quad_Polygon, nbNodes ), curNodes );
7592 // a polygon can divide into several elements
7593 vector<const SMDS_MeshNode *> polygons_nodes;
7594 vector<int> quantities;
7595 nbResElems = SimplifyFace( curNodes, polygons_nodes, quantities );
7596 newElemDefs.resize( nbResElems );
7597 for ( int inode = 0, iface = 0; iface < nbResElems; iface++ )
7599 ElemFeatures* elemType = & newElemDefs[iface];
7600 if ( iface ) elemType->Init( elem );
7602 vector<const SMDS_MeshNode *>& face_nodes = elemType->myNodes;
7603 int nbNewNodes = quantities[iface];
7604 face_nodes.assign( polygons_nodes.begin() + inode,
7605 polygons_nodes.begin() + inode + nbNewNodes );
7606 inode += nbNewNodes;
7607 if ( isQuad ) // check if a result elem is a valid quadratic polygon
7609 bool isValid = ( nbNewNodes % 2 == 0 );
7610 for ( int i = 0; i < nbNewNodes && isValid; ++i )
7611 isValid = ( elem->IsMediumNode( face_nodes[i]) == bool( i % 2 ));
7612 elemType->SetQuad( isValid );
7613 if ( isValid ) // put medium nodes after corners
7614 SMDS_MeshCell::applyInterlaceRev
7615 ( SMDS_MeshCell::interlacedSmdsOrder( SMDSEntity_Quad_Polygon,
7616 nbNewNodes ), face_nodes );
7618 elemType->SetPoly(( nbNewNodes / ( elemType->myIsQuad + 1 ) > 4 ));
7620 nbUniqueNodes = newElemDefs[0].myNodes.size();
7624 case SMDSEntity_Polyhedra: // Polyhedral volume
7626 if ( nbUniqueNodes >= 4 )
7628 // each face has to be analyzed in order to check volume validity
7629 if ( const SMDS_VtkVolume* aPolyedre = dynamic_cast<const SMDS_VtkVolume*>( elem ))
7631 int nbFaces = aPolyedre->NbFaces();
7633 vector<const SMDS_MeshNode *>& poly_nodes = newElemDefs[0].myNodes;
7634 vector<int> & quantities = newElemDefs[0].myPolyhedQuantities;
7635 vector<const SMDS_MeshNode *> faceNodes;
7639 for (int iface = 1; iface <= nbFaces; iface++)
7641 int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
7642 faceNodes.resize( nbFaceNodes );
7643 for (int inode = 1; inode <= nbFaceNodes; inode++)
7645 const SMDS_MeshNode * faceNode = aPolyedre->GetFaceNode(iface, inode);
7646 TNodeNodeMap::iterator nnIt = nodeNodeMap.find(faceNode);
7647 if ( nnIt != nodeNodeMap.end() ) // faceNode sticks
7648 faceNode = (*nnIt).second;
7649 faceNodes[inode - 1] = faceNode;
7651 SimplifyFace(faceNodes, poly_nodes, quantities);
7654 if ( quantities.size() > 3 )
7656 // TODO: remove coincident faces
7658 nbUniqueNodes = newElemDefs[0].myNodes.size();
7666 // TODO not all the possible cases are solved. Find something more generic?
7667 case SMDSEntity_Edge: //////// EDGE
7668 case SMDSEntity_Triangle: //// TRIANGLE
7669 case SMDSEntity_Quad_Triangle:
7670 case SMDSEntity_Tetra:
7671 case SMDSEntity_Quad_Tetra: // TETRAHEDRON
7675 case SMDSEntity_Quad_Edge:
7679 case SMDSEntity_Quadrangle: //////////////////////////////////// QUADRANGLE
7681 if ( nbUniqueNodes < 3 )
7683 else if ( nbRepl == 1 && curNodes[ iRepl[0]] == curNodes[( iRepl[0]+2 )%4 ])
7684 toRemove = true; // opposite nodes stick
7689 case SMDSEntity_Quad_Quadrangle: // Quadratic QUADRANGLE
7698 if ( nbUniqueNodes == 6 &&
7700 ( nbRepl == 1 || iRepl[1] >= 4 ))
7706 case SMDSEntity_BiQuad_Quadrangle: // Bi-Quadratic QUADRANGLE
7715 if ( nbUniqueNodes == 7 &&
7717 ( nbRepl == 1 || iRepl[1] != 8 ))
7723 case SMDSEntity_Penta: ///////////////////////////////////// PENTAHEDRON
7725 if ( nbUniqueNodes == 4 ) {
7726 // ---------------------------------> tetrahedron
7727 if ( curNodes[3] == curNodes[4] &&
7728 curNodes[3] == curNodes[5] ) {
7732 else if ( curNodes[0] == curNodes[1] &&
7733 curNodes[0] == curNodes[2] ) {
7734 // bottom nodes stick: set a top before
7735 uniqueNodes[ 3 ] = uniqueNodes [ 0 ];
7736 uniqueNodes[ 0 ] = curNodes [ 5 ];
7737 uniqueNodes[ 1 ] = curNodes [ 4 ];
7738 uniqueNodes[ 2 ] = curNodes [ 3 ];
7741 else if (( curNodes[0] == curNodes[3] ) +
7742 ( curNodes[1] == curNodes[4] ) +
7743 ( curNodes[2] == curNodes[5] ) == 2 ) {
7744 // a lateral face turns into a line
7748 else if ( nbUniqueNodes == 5 ) {
7749 // PENTAHEDRON --------------------> pyramid
7750 if ( curNodes[0] == curNodes[3] )
7752 uniqueNodes[ 0 ] = curNodes[ 1 ];
7753 uniqueNodes[ 1 ] = curNodes[ 4 ];
7754 uniqueNodes[ 2 ] = curNodes[ 5 ];
7755 uniqueNodes[ 3 ] = curNodes[ 2 ];
7756 uniqueNodes[ 4 ] = curNodes[ 0 ];
7759 if ( curNodes[1] == curNodes[4] )
7761 uniqueNodes[ 0 ] = curNodes[ 0 ];
7762 uniqueNodes[ 1 ] = curNodes[ 2 ];
7763 uniqueNodes[ 2 ] = curNodes[ 5 ];
7764 uniqueNodes[ 3 ] = curNodes[ 3 ];
7765 uniqueNodes[ 4 ] = curNodes[ 1 ];
7768 if ( curNodes[2] == curNodes[5] )
7770 uniqueNodes[ 0 ] = curNodes[ 0 ];
7771 uniqueNodes[ 1 ] = curNodes[ 3 ];
7772 uniqueNodes[ 2 ] = curNodes[ 4 ];
7773 uniqueNodes[ 3 ] = curNodes[ 1 ];
7774 uniqueNodes[ 4 ] = curNodes[ 2 ];
7780 case SMDSEntity_Hexa:
7782 //////////////////////////////////// HEXAHEDRON
7783 SMDS_VolumeTool hexa (elem);
7784 hexa.SetExternalNormal();
7785 if ( nbUniqueNodes == 4 && nbRepl == 4 ) {
7786 //////////////////////// HEX ---> tetrahedron
7787 for ( int iFace = 0; iFace < 6; iFace++ ) {
7788 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7789 if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7790 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7791 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7792 // one face turns into a point ...
7793 int pickInd = ind[ 0 ];
7794 int iOppFace = hexa.GetOppFaceIndex( iFace );
7795 ind = hexa.GetFaceNodesIndices( iOppFace );
7797 uniqueNodes.clear();
7798 for ( iCur = 0; iCur < 4 && nbStick < 2; iCur++ ) {
7799 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7802 uniqueNodes.push_back( curNodes[ind[ iCur ]]);
7804 if ( nbStick == 1 ) {
7805 // ... and the opposite one - into a triangle.
7807 uniqueNodes.push_back( curNodes[ pickInd ]);
7814 else if ( nbUniqueNodes == 6 && nbRepl == 2 ) {
7815 //////////////////////// HEX ---> prism
7816 int nbTria = 0, iTria[3];
7817 const int *ind; // indices of face nodes
7818 // look for triangular faces
7819 for ( int iFace = 0; iFace < 6 && nbTria < 3; iFace++ ) {
7820 ind = hexa.GetFaceNodesIndices( iFace );
7821 TIDSortedNodeSet faceNodes;
7822 for ( iCur = 0; iCur < 4; iCur++ )
7823 faceNodes.insert( curNodes[ind[iCur]] );
7824 if ( faceNodes.size() == 3 )
7825 iTria[ nbTria++ ] = iFace;
7827 // check if triangles are opposite
7828 if ( nbTria == 2 && iTria[0] == hexa.GetOppFaceIndex( iTria[1] ))
7830 // set nodes of the bottom triangle
7831 ind = hexa.GetFaceNodesIndices( iTria[ 0 ]);
7833 for ( iCur = 0; iCur < 4; iCur++ )
7834 if ( ind[iCur] != iRepl[0] && ind[iCur] != iRepl[1])
7835 indB.push_back( ind[iCur] );
7836 if ( !hexa.IsForward() )
7837 std::swap( indB[0], indB[2] );
7838 for ( iCur = 0; iCur < 3; iCur++ )
7839 uniqueNodes[ iCur ] = curNodes[indB[iCur]];
7840 // set nodes of the top triangle
7841 const int *indT = hexa.GetFaceNodesIndices( iTria[ 1 ]);
7842 for ( iCur = 0; iCur < 3; ++iCur )
7843 for ( int j = 0; j < 4; ++j )
7844 if ( hexa.IsLinked( indB[ iCur ], indT[ j ] ))
7846 uniqueNodes[ iCur + 3 ] = curNodes[ indT[ j ]];
7853 else if (nbUniqueNodes == 5 && nbRepl == 3 ) {
7854 //////////////////// HEXAHEDRON ---> pyramid
7855 for ( int iFace = 0; iFace < 6; iFace++ ) {
7856 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7857 if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7858 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7859 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7860 // one face turns into a point ...
7861 int iOppFace = hexa.GetOppFaceIndex( iFace );
7862 ind = hexa.GetFaceNodesIndices( iOppFace );
7863 uniqueNodes.clear();
7864 for ( iCur = 0; iCur < 4; iCur++ ) {
7865 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7868 uniqueNodes.push_back( curNodes[ind[ iCur ]]);
7870 if ( uniqueNodes.size() == 4 ) {
7871 // ... and the opposite one is a quadrangle
7873 const int* indTop = hexa.GetFaceNodesIndices( iFace );
7874 uniqueNodes.push_back( curNodes[indTop[ 0 ]]);
7882 if ( toRemove && nbUniqueNodes > 4 ) {
7883 ////////////////// HEXAHEDRON ---> polyhedron
7884 hexa.SetExternalNormal();
7885 vector<const SMDS_MeshNode *>& poly_nodes = newElemDefs[0].myNodes;
7886 vector<int> & quantities = newElemDefs[0].myPolyhedQuantities;
7887 poly_nodes.reserve( 6 * 4 ); poly_nodes.clear();
7888 quantities.reserve( 6 ); quantities.clear();
7889 for ( int iFace = 0; iFace < 6; iFace++ )
7891 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7892 if ( curNodes[ind[0]] == curNodes[ind[2]] ||
7893 curNodes[ind[1]] == curNodes[ind[3]] )
7896 break; // opposite nodes stick
7899 for ( iCur = 0; iCur < 4; iCur++ )
7901 if ( nodeSet.insert( curNodes[ind[ iCur ]] ).second )
7902 poly_nodes.push_back( curNodes[ind[ iCur ]]);
7904 if ( nodeSet.size() < 3 )
7905 poly_nodes.resize( poly_nodes.size() - nodeSet.size() );
7907 quantities.push_back( nodeSet.size() );
7909 if ( quantities.size() >= 4 )
7912 nbUniqueNodes = poly_nodes.size();
7913 newElemDefs[0].SetPoly(true);
7917 } // case HEXAHEDRON
7922 } // switch ( entity )
7924 if ( toRemove && nbResElems == 0 && avoidMakingHoles )
7926 // erase from nodeNodeMap nodes whose merge spoils elem
7927 vector< const SMDS_MeshNode* > noMergeNodes;
7928 SMESH_MeshAlgos::DeMerge( elem, curNodes, noMergeNodes );
7929 for ( size_t i = 0; i < noMergeNodes.size(); ++i )
7930 nodeNodeMap.erase( noMergeNodes[i] );
7933 } // if ( nbNodes != nbUniqueNodes ) // some nodes stick
7935 uniqueNodes.resize( nbUniqueNodes );
7937 if ( !toRemove && nbResElems == 0 )
7940 newElemDefs.resize( nbResElems );
7946 // ========================================================
7947 // class : SortableElement
7948 // purpose : allow sorting elements basing on their nodes
7949 // ========================================================
7950 class SortableElement : public set <const SMDS_MeshElement*>
7954 SortableElement( const SMDS_MeshElement* theElem )
7957 SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator();
7958 while ( nodeIt->more() )
7959 this->insert( nodeIt->next() );
7962 const SMDS_MeshElement* Get() const
7966 mutable const SMDS_MeshElement* myElem;
7969 //=======================================================================
7970 //function : FindEqualElements
7971 //purpose : Return list of group of elements built on the same nodes.
7972 // Search among theElements or in the whole mesh if theElements is empty
7973 //=======================================================================
7975 void SMESH_MeshEditor::FindEqualElements(TIDSortedElemSet & theElements,
7976 TListOfListOfElementsID & theGroupsOfElementsID)
7978 myLastCreatedElems.Clear();
7979 myLastCreatedNodes.Clear();
7981 typedef map< SortableElement, int > TMapOfNodeSet;
7982 typedef list<int> TGroupOfElems;
7984 if ( theElements.empty() )
7985 { // get all elements in the mesh
7986 SMDS_ElemIteratorPtr eIt = GetMeshDS()->elementsIterator();
7987 while ( eIt->more() )
7988 theElements.insert( theElements.end(), eIt->next() );
7991 vector< TGroupOfElems > arrayOfGroups;
7992 TGroupOfElems groupOfElems;
7993 TMapOfNodeSet mapOfNodeSet;
7995 TIDSortedElemSet::iterator elemIt = theElements.begin();
7996 for ( int i = 0; elemIt != theElements.end(); ++elemIt )
7998 const SMDS_MeshElement* curElem = *elemIt;
7999 SortableElement SE(curElem);
8001 pair< TMapOfNodeSet::iterator, bool> pp = mapOfNodeSet.insert(make_pair(SE, i));
8002 if ( !pp.second ) { // one more coincident elem
8003 TMapOfNodeSet::iterator& itSE = pp.first;
8004 int ind = (*itSE).second;
8005 arrayOfGroups[ind].push_back( curElem->GetID() );
8008 arrayOfGroups.push_back( groupOfElems );
8009 arrayOfGroups.back().push_back( curElem->GetID() );
8014 groupOfElems.clear();
8015 vector< TGroupOfElems >::iterator groupIt = arrayOfGroups.begin();
8016 for ( ; groupIt != arrayOfGroups.end(); ++groupIt )
8018 if ( groupIt->size() > 1 ) {
8019 //groupOfElems.sort(); -- theElements is sorted already
8020 theGroupsOfElementsID.push_back( groupOfElems );
8021 theGroupsOfElementsID.back().splice( theGroupsOfElementsID.back().end(), *groupIt );
8026 //=======================================================================
8027 //function : MergeElements
8028 //purpose : In each given group, substitute all elements by the first one.
8029 //=======================================================================
8031 void SMESH_MeshEditor::MergeElements(TListOfListOfElementsID & theGroupsOfElementsID)
8033 myLastCreatedElems.Clear();
8034 myLastCreatedNodes.Clear();
8036 typedef list<int> TListOfIDs;
8037 TListOfIDs rmElemIds; // IDs of elems to remove
8039 SMESHDS_Mesh* aMesh = GetMeshDS();
8041 TListOfListOfElementsID::iterator groupsIt = theGroupsOfElementsID.begin();
8042 while ( groupsIt != theGroupsOfElementsID.end() ) {
8043 TListOfIDs& aGroupOfElemID = *groupsIt;
8044 aGroupOfElemID.sort();
8045 int elemIDToKeep = aGroupOfElemID.front();
8046 const SMDS_MeshElement* elemToKeep = aMesh->FindElement(elemIDToKeep);
8047 aGroupOfElemID.pop_front();
8048 TListOfIDs::iterator idIt = aGroupOfElemID.begin();
8049 while ( idIt != aGroupOfElemID.end() ) {
8050 int elemIDToRemove = *idIt;
8051 const SMDS_MeshElement* elemToRemove = aMesh->FindElement(elemIDToRemove);
8052 // add the kept element in groups of removed one (PAL15188)
8053 AddToSameGroups( elemToKeep, elemToRemove, aMesh );
8054 rmElemIds.push_back( elemIDToRemove );
8060 Remove( rmElemIds, false );
8063 //=======================================================================
8064 //function : MergeEqualElements
8065 //purpose : Remove all but one of elements built on the same nodes.
8066 //=======================================================================
8068 void SMESH_MeshEditor::MergeEqualElements()
8070 TIDSortedElemSet aMeshElements; /* empty input ==
8071 to merge equal elements in the whole mesh */
8072 TListOfListOfElementsID aGroupsOfElementsID;
8073 FindEqualElements(aMeshElements, aGroupsOfElementsID);
8074 MergeElements(aGroupsOfElementsID);
8077 //=======================================================================
8078 //function : findAdjacentFace
8080 //=======================================================================
8082 static const SMDS_MeshElement* findAdjacentFace(const SMDS_MeshNode* n1,
8083 const SMDS_MeshNode* n2,
8084 const SMDS_MeshElement* elem)
8086 TIDSortedElemSet elemSet, avoidSet;
8088 avoidSet.insert ( elem );
8089 return SMESH_MeshAlgos::FindFaceInSet( n1, n2, elemSet, avoidSet );
8092 //=======================================================================
8093 //function : findSegment
8094 //purpose : Return a mesh segment by two nodes one of which can be medium
8095 //=======================================================================
8097 static const SMDS_MeshElement* findSegment(const SMDS_MeshNode* n1,
8098 const SMDS_MeshNode* n2)
8100 SMDS_ElemIteratorPtr it = n1->GetInverseElementIterator( SMDSAbs_Edge );
8101 while ( it->more() )
8103 const SMDS_MeshElement* seg = it->next();
8104 if ( seg->GetNodeIndex( n2 ) >= 0 )
8110 //=======================================================================
8111 //function : FindFreeBorder
8113 //=======================================================================
8115 #define ControlFreeBorder SMESH::Controls::FreeEdges::IsFreeEdge
8117 bool SMESH_MeshEditor::FindFreeBorder (const SMDS_MeshNode* theFirstNode,
8118 const SMDS_MeshNode* theSecondNode,
8119 const SMDS_MeshNode* theLastNode,
8120 list< const SMDS_MeshNode* > & theNodes,
8121 list< const SMDS_MeshElement* >& theFaces)
8123 if ( !theFirstNode || !theSecondNode )
8125 // find border face between theFirstNode and theSecondNode
8126 const SMDS_MeshElement* curElem = findAdjacentFace( theFirstNode, theSecondNode, 0 );
8130 theFaces.push_back( curElem );
8131 theNodes.push_back( theFirstNode );
8132 theNodes.push_back( theSecondNode );
8134 const SMDS_MeshNode *nIgnore = theFirstNode, *nStart = theSecondNode;
8135 TIDSortedElemSet foundElems;
8136 bool needTheLast = ( theLastNode != 0 );
8138 while ( nStart != theLastNode ) {
8139 if ( nStart == theFirstNode )
8140 return !needTheLast;
8142 // find all free border faces sharing form nStart
8144 list< const SMDS_MeshElement* > curElemList;
8145 list< const SMDS_MeshNode* > nStartList;
8146 SMDS_ElemIteratorPtr invElemIt = nStart->GetInverseElementIterator(SMDSAbs_Face);
8147 while ( invElemIt->more() ) {
8148 const SMDS_MeshElement* e = invElemIt->next();
8149 if ( e == curElem || foundElems.insert( e ).second ) {
8151 int iNode = 0, nbNodes = e->NbNodes();
8152 vector<const SMDS_MeshNode*> nodes(nbNodes+1);
8154 if ( e->IsQuadratic() ) {
8155 const SMDS_VtkFace* F =
8156 dynamic_cast<const SMDS_VtkFace*>(e);
8157 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8158 // use special nodes iterator
8159 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8160 while( anIter->more() ) {
8161 nodes[ iNode++ ] = cast2Node(anIter->next());
8165 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
8166 while ( nIt->more() )
8167 nodes[ iNode++ ] = static_cast<const SMDS_MeshNode*>( nIt->next() );
8169 nodes[ iNode ] = nodes[ 0 ];
8171 for ( iNode = 0; iNode < nbNodes; iNode++ )
8172 if (((nodes[ iNode ] == nStart && nodes[ iNode + 1] != nIgnore ) ||
8173 (nodes[ iNode + 1] == nStart && nodes[ iNode ] != nIgnore )) &&
8174 ControlFreeBorder( &nodes[ iNode ], e->GetID() ))
8176 nStartList.push_back( nodes[ iNode + ( nodes[ iNode ] == nStart ? 1 : 0 )]);
8177 curElemList.push_back( e );
8181 // analyse the found
8183 int nbNewBorders = curElemList.size();
8184 if ( nbNewBorders == 0 ) {
8185 // no free border furthermore
8186 return !needTheLast;
8188 else if ( nbNewBorders == 1 ) {
8189 // one more element found
8191 nStart = nStartList.front();
8192 curElem = curElemList.front();
8193 theFaces.push_back( curElem );
8194 theNodes.push_back( nStart );
8197 // several continuations found
8198 list< const SMDS_MeshElement* >::iterator curElemIt;
8199 list< const SMDS_MeshNode* >::iterator nStartIt;
8200 // check if one of them reached the last node
8201 if ( needTheLast ) {
8202 for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
8203 curElemIt!= curElemList.end();
8204 curElemIt++, nStartIt++ )
8205 if ( *nStartIt == theLastNode ) {
8206 theFaces.push_back( *curElemIt );
8207 theNodes.push_back( *nStartIt );
8211 // find the best free border by the continuations
8212 list<const SMDS_MeshNode*> contNodes[ 2 ], *cNL;
8213 list<const SMDS_MeshElement*> contFaces[ 2 ], *cFL;
8214 for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
8215 curElemIt!= curElemList.end();
8216 curElemIt++, nStartIt++ )
8218 cNL = & contNodes[ contNodes[0].empty() ? 0 : 1 ];
8219 cFL = & contFaces[ contFaces[0].empty() ? 0 : 1 ];
8220 // find one more free border
8221 if ( ! SMESH_MeshEditor::FindFreeBorder( nStart, *nStartIt, theLastNode, *cNL, *cFL )) {
8225 else if ( !contNodes[0].empty() && !contNodes[1].empty() ) {
8226 // choice: clear a worse one
8227 int iLongest = ( contNodes[0].size() < contNodes[1].size() ? 1 : 0 );
8228 int iWorse = ( needTheLast ? 1 - iLongest : iLongest );
8229 contNodes[ iWorse ].clear();
8230 contFaces[ iWorse ].clear();
8233 if ( contNodes[0].empty() && contNodes[1].empty() )
8236 // append the best free border
8237 cNL = & contNodes[ contNodes[0].empty() ? 1 : 0 ];
8238 cFL = & contFaces[ contFaces[0].empty() ? 1 : 0 ];
8239 theNodes.pop_back(); // remove nIgnore
8240 theNodes.pop_back(); // remove nStart
8241 theFaces.pop_back(); // remove curElem
8242 list< const SMDS_MeshNode* >::iterator nIt = cNL->begin();
8243 list< const SMDS_MeshElement* >::iterator fIt = cFL->begin();
8244 for ( ; nIt != cNL->end(); nIt++ ) theNodes.push_back( *nIt );
8245 for ( ; fIt != cFL->end(); fIt++ ) theFaces.push_back( *fIt );
8248 } // several continuations found
8249 } // while ( nStart != theLastNode )
8254 //=======================================================================
8255 //function : CheckFreeBorderNodes
8256 //purpose : Return true if the tree nodes are on a free border
8257 //=======================================================================
8259 bool SMESH_MeshEditor::CheckFreeBorderNodes(const SMDS_MeshNode* theNode1,
8260 const SMDS_MeshNode* theNode2,
8261 const SMDS_MeshNode* theNode3)
8263 list< const SMDS_MeshNode* > nodes;
8264 list< const SMDS_MeshElement* > faces;
8265 return FindFreeBorder( theNode1, theNode2, theNode3, nodes, faces);
8268 //=======================================================================
8269 //function : SewFreeBorder
8271 //warning : for border-to-side sewing theSideSecondNode is considered as
8272 // the last side node and theSideThirdNode is not used
8273 //=======================================================================
8275 SMESH_MeshEditor::Sew_Error
8276 SMESH_MeshEditor::SewFreeBorder (const SMDS_MeshNode* theBordFirstNode,
8277 const SMDS_MeshNode* theBordSecondNode,
8278 const SMDS_MeshNode* theBordLastNode,
8279 const SMDS_MeshNode* theSideFirstNode,
8280 const SMDS_MeshNode* theSideSecondNode,
8281 const SMDS_MeshNode* theSideThirdNode,
8282 const bool theSideIsFreeBorder,
8283 const bool toCreatePolygons,
8284 const bool toCreatePolyedrs)
8286 myLastCreatedElems.Clear();
8287 myLastCreatedNodes.Clear();
8289 Sew_Error aResult = SEW_OK;
8291 // ====================================
8292 // find side nodes and elements
8293 // ====================================
8295 list< const SMDS_MeshNode* > nSide[ 2 ];
8296 list< const SMDS_MeshElement* > eSide[ 2 ];
8297 list< const SMDS_MeshNode* >::iterator nIt[ 2 ];
8298 list< const SMDS_MeshElement* >::iterator eIt[ 2 ];
8302 if (!FindFreeBorder(theBordFirstNode,theBordSecondNode,theBordLastNode,
8303 nSide[0], eSide[0])) {
8304 MESSAGE(" Free Border 1 not found " );
8305 aResult = SEW_BORDER1_NOT_FOUND;
8307 if (theSideIsFreeBorder) {
8310 if (!FindFreeBorder(theSideFirstNode, theSideSecondNode, theSideThirdNode,
8311 nSide[1], eSide[1])) {
8312 MESSAGE(" Free Border 2 not found " );
8313 aResult = ( aResult != SEW_OK ? SEW_BOTH_BORDERS_NOT_FOUND : SEW_BORDER2_NOT_FOUND );
8316 if ( aResult != SEW_OK )
8319 if (!theSideIsFreeBorder) {
8323 // -------------------------------------------------------------------------
8325 // 1. If nodes to merge are not coincident, move nodes of the free border
8326 // from the coord sys defined by the direction from the first to last
8327 // nodes of the border to the correspondent sys of the side 2
8328 // 2. On the side 2, find the links most co-directed with the correspondent
8329 // links of the free border
8330 // -------------------------------------------------------------------------
8332 // 1. Since sewing may break if there are volumes to split on the side 2,
8333 // we won't move nodes but just compute new coordinates for them
8334 typedef map<const SMDS_MeshNode*, gp_XYZ> TNodeXYZMap;
8335 TNodeXYZMap nBordXYZ;
8336 list< const SMDS_MeshNode* >& bordNodes = nSide[ 0 ];
8337 list< const SMDS_MeshNode* >::iterator nBordIt;
8339 gp_XYZ Pb1( theBordFirstNode->X(), theBordFirstNode->Y(), theBordFirstNode->Z() );
8340 gp_XYZ Pb2( theBordLastNode->X(), theBordLastNode->Y(), theBordLastNode->Z() );
8341 gp_XYZ Ps1( theSideFirstNode->X(), theSideFirstNode->Y(), theSideFirstNode->Z() );
8342 gp_XYZ Ps2( theSideSecondNode->X(), theSideSecondNode->Y(), theSideSecondNode->Z() );
8343 double tol2 = 1.e-8;
8344 gp_Vec Vbs1( Pb1 - Ps1 ),Vbs2( Pb2 - Ps2 );
8345 if ( Vbs1.SquareMagnitude() > tol2 || Vbs2.SquareMagnitude() > tol2 ) {
8346 // Need node movement.
8348 // find X and Z axes to create trsf
8349 gp_Vec Zb( Pb1 - Pb2 ), Zs( Ps1 - Ps2 );
8351 if ( X.SquareMagnitude() <= gp::Resolution() * gp::Resolution() )
8353 X = gp_Ax2( gp::Origin(), Zb ).XDirection();
8356 gp_Ax3 toBordAx( Pb1, Zb, X );
8357 gp_Ax3 fromSideAx( Ps1, Zs, X );
8358 gp_Ax3 toGlobalAx( gp::Origin(), gp::DZ(), gp::DX() );
8360 gp_Trsf toBordSys, fromSide2Sys;
8361 toBordSys.SetTransformation( toBordAx );
8362 fromSide2Sys.SetTransformation( fromSideAx, toGlobalAx );
8363 fromSide2Sys.SetScaleFactor( Zs.Magnitude() / Zb.Magnitude() );
8366 for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
8367 const SMDS_MeshNode* n = *nBordIt;
8368 gp_XYZ xyz( n->X(),n->Y(),n->Z() );
8369 toBordSys.Transforms( xyz );
8370 fromSide2Sys.Transforms( xyz );
8371 nBordXYZ.insert( TNodeXYZMap::value_type( n, xyz ));
8375 // just insert nodes XYZ in the nBordXYZ map
8376 for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
8377 const SMDS_MeshNode* n = *nBordIt;
8378 nBordXYZ.insert( TNodeXYZMap::value_type( n, gp_XYZ( n->X(),n->Y(),n->Z() )));
8382 // 2. On the side 2, find the links most co-directed with the correspondent
8383 // links of the free border
8385 list< const SMDS_MeshElement* >& sideElems = eSide[ 1 ];
8386 list< const SMDS_MeshNode* >& sideNodes = nSide[ 1 ];
8387 sideNodes.push_back( theSideFirstNode );
8389 bool hasVolumes = false;
8390 LinkID_Gen aLinkID_Gen( GetMeshDS() );
8391 set<long> foundSideLinkIDs, checkedLinkIDs;
8392 SMDS_VolumeTool volume;
8393 //const SMDS_MeshNode* faceNodes[ 4 ];
8395 const SMDS_MeshNode* sideNode;
8396 const SMDS_MeshElement* sideElem = 0;
8397 const SMDS_MeshNode* prevSideNode = theSideFirstNode;
8398 const SMDS_MeshNode* prevBordNode = theBordFirstNode;
8399 nBordIt = bordNodes.begin();
8401 // border node position and border link direction to compare with
8402 gp_XYZ bordPos = nBordXYZ[ *nBordIt ];
8403 gp_XYZ bordDir = bordPos - nBordXYZ[ prevBordNode ];
8404 // choose next side node by link direction or by closeness to
8405 // the current border node:
8406 bool searchByDir = ( *nBordIt != theBordLastNode );
8408 // find the next node on the Side 2
8410 double maxDot = -DBL_MAX, minDist = DBL_MAX;
8412 checkedLinkIDs.clear();
8413 gp_XYZ prevXYZ( prevSideNode->X(), prevSideNode->Y(), prevSideNode->Z() );
8415 // loop on inverse elements of current node (prevSideNode) on the Side 2
8416 SMDS_ElemIteratorPtr invElemIt = prevSideNode->GetInverseElementIterator();
8417 while ( invElemIt->more() )
8419 const SMDS_MeshElement* elem = invElemIt->next();
8420 // prepare data for a loop on links coming to prevSideNode, of a face or a volume
8421 int iPrevNode = 0, iNode = 0, nbNodes = elem->NbNodes();
8422 vector< const SMDS_MeshNode* > faceNodes( nbNodes, (const SMDS_MeshNode*)0 );
8423 bool isVolume = volume.Set( elem );
8424 const SMDS_MeshNode** nodes = isVolume ? volume.GetNodes() : & faceNodes[0];
8425 if ( isVolume ) // --volume
8427 else if ( elem->GetType()==SMDSAbs_Face ) { // --face
8428 // retrieve all face nodes and find iPrevNode - an index of the prevSideNode
8429 if(elem->IsQuadratic()) {
8430 const SMDS_VtkFace* F =
8431 dynamic_cast<const SMDS_VtkFace*>(elem);
8432 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8433 // use special nodes iterator
8434 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8435 while( anIter->more() ) {
8436 nodes[ iNode ] = cast2Node(anIter->next());
8437 if ( nodes[ iNode++ ] == prevSideNode )
8438 iPrevNode = iNode - 1;
8442 SMDS_ElemIteratorPtr nIt = elem->nodesIterator();
8443 while ( nIt->more() ) {
8444 nodes[ iNode ] = cast2Node( nIt->next() );
8445 if ( nodes[ iNode++ ] == prevSideNode )
8446 iPrevNode = iNode - 1;
8449 // there are 2 links to check
8454 // loop on links, to be precise, on the second node of links
8455 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
8456 const SMDS_MeshNode* n = nodes[ iNode ];
8458 if ( !volume.IsLinked( n, prevSideNode ))
8462 if ( iNode ) // a node before prevSideNode
8463 n = nodes[ iPrevNode == 0 ? elem->NbNodes() - 1 : iPrevNode - 1 ];
8464 else // a node after prevSideNode
8465 n = nodes[ iPrevNode + 1 == elem->NbNodes() ? 0 : iPrevNode + 1 ];
8467 // check if this link was already used
8468 long iLink = aLinkID_Gen.GetLinkID( prevSideNode, n );
8469 bool isJustChecked = !checkedLinkIDs.insert( iLink ).second;
8470 if (!isJustChecked &&
8471 foundSideLinkIDs.find( iLink ) == foundSideLinkIDs.end() )
8473 // test a link geometrically
8474 gp_XYZ nextXYZ ( n->X(), n->Y(), n->Z() );
8475 bool linkIsBetter = false;
8476 double dot = 0.0, dist = 0.0;
8477 if ( searchByDir ) { // choose most co-directed link
8478 dot = bordDir * ( nextXYZ - prevXYZ ).Normalized();
8479 linkIsBetter = ( dot > maxDot );
8481 else { // choose link with the node closest to bordPos
8482 dist = ( nextXYZ - bordPos ).SquareModulus();
8483 linkIsBetter = ( dist < minDist );
8485 if ( linkIsBetter ) {
8494 } // loop on inverse elements of prevSideNode
8497 MESSAGE(" Can't find path by links of the Side 2 ");
8498 return SEW_BAD_SIDE_NODES;
8500 sideNodes.push_back( sideNode );
8501 sideElems.push_back( sideElem );
8502 foundSideLinkIDs.insert ( linkID );
8503 prevSideNode = sideNode;
8505 if ( *nBordIt == theBordLastNode )
8506 searchByDir = false;
8508 // find the next border link to compare with
8509 gp_XYZ sidePos( sideNode->X(), sideNode->Y(), sideNode->Z() );
8510 searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8511 // move to next border node if sideNode is before forward border node (bordPos)
8512 while ( *nBordIt != theBordLastNode && !searchByDir ) {
8513 prevBordNode = *nBordIt;
8515 bordPos = nBordXYZ[ *nBordIt ];
8516 bordDir = bordPos - nBordXYZ[ prevBordNode ];
8517 searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8521 while ( sideNode != theSideSecondNode );
8523 if ( hasVolumes && sideNodes.size () != bordNodes.size() && !toCreatePolyedrs) {
8524 MESSAGE("VOLUME SPLITTING IS FORBIDDEN");
8525 return SEW_VOLUMES_TO_SPLIT; // volume splitting is forbidden
8527 } // end nodes search on the side 2
8529 // ============================
8530 // sew the border to the side 2
8531 // ============================
8533 int nbNodes[] = { (int)nSide[0].size(), (int)nSide[1].size() };
8534 int maxNbNodes = Max( nbNodes[0], nbNodes[1] );
8536 bool toMergeConformal = ( nbNodes[0] == nbNodes[1] );
8537 if ( toMergeConformal && toCreatePolygons )
8539 // do not merge quadrangles if polygons are OK (IPAL0052824)
8540 eIt[0] = eSide[0].begin();
8541 eIt[1] = eSide[1].begin();
8542 bool allQuads[2] = { true, true };
8543 for ( int iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8544 for ( ; allQuads[iBord] && eIt[iBord] != eSide[iBord].end(); ++eIt[iBord] )
8545 allQuads[iBord] = ( (*eIt[iBord])->NbCornerNodes() == 4 );
8547 toMergeConformal = ( !allQuads[0] && !allQuads[1] );
8550 TListOfListOfNodes nodeGroupsToMerge;
8551 if (( toMergeConformal ) ||
8552 ( theSideIsFreeBorder && !theSideThirdNode )) {
8554 // all nodes are to be merged
8556 for (nIt[0] = nSide[0].begin(), nIt[1] = nSide[1].begin();
8557 nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end();
8558 nIt[0]++, nIt[1]++ )
8560 nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8561 nodeGroupsToMerge.back().push_back( *nIt[1] ); // to keep
8562 nodeGroupsToMerge.back().push_back( *nIt[0] ); // to remove
8567 // insert new nodes into the border and the side to get equal nb of segments
8569 // get normalized parameters of nodes on the borders
8570 vector< double > param[ 2 ];
8571 param[0].resize( maxNbNodes );
8572 param[1].resize( maxNbNodes );
8574 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8575 list< const SMDS_MeshNode* >& nodes = nSide[ iBord ];
8576 list< const SMDS_MeshNode* >::iterator nIt = nodes.begin();
8577 const SMDS_MeshNode* nPrev = *nIt;
8578 double bordLength = 0;
8579 for ( iNode = 0; nIt != nodes.end(); nIt++, iNode++ ) { // loop on border nodes
8580 const SMDS_MeshNode* nCur = *nIt;
8581 gp_XYZ segment (nCur->X() - nPrev->X(),
8582 nCur->Y() - nPrev->Y(),
8583 nCur->Z() - nPrev->Z());
8584 double segmentLen = segment.Modulus();
8585 bordLength += segmentLen;
8586 param[ iBord ][ iNode ] = bordLength;
8589 // normalize within [0,1]
8590 for ( iNode = 0; iNode < nbNodes[ iBord ]; iNode++ ) {
8591 param[ iBord ][ iNode ] /= bordLength;
8595 // loop on border segments
8596 const SMDS_MeshNode *nPrev[ 2 ] = { 0, 0 };
8597 int i[ 2 ] = { 0, 0 };
8598 nIt[0] = nSide[0].begin(); eIt[0] = eSide[0].begin();
8599 nIt[1] = nSide[1].begin(); eIt[1] = eSide[1].begin();
8601 TElemOfNodeListMap insertMap;
8602 TElemOfNodeListMap::iterator insertMapIt;
8604 // key: elem to insert nodes into
8605 // value: 2 nodes to insert between + nodes to be inserted
8607 bool next[ 2 ] = { false, false };
8609 // find min adjacent segment length after sewing
8610 double nextParam = 10., prevParam = 0;
8611 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8612 if ( i[ iBord ] + 1 < nbNodes[ iBord ])
8613 nextParam = Min( nextParam, param[iBord][ i[iBord] + 1 ]);
8614 if ( i[ iBord ] > 0 )
8615 prevParam = Max( prevParam, param[iBord][ i[iBord] - 1 ]);
8617 double minParam = Min( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8618 double maxParam = Max( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8619 double minSegLen = Min( nextParam - minParam, maxParam - prevParam );
8621 // choose to insert or to merge nodes
8622 double du = param[ 1 ][ i[1] ] - param[ 0 ][ i[0] ];
8623 if ( Abs( du ) <= minSegLen * 0.2 ) {
8626 nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8627 const SMDS_MeshNode* n0 = *nIt[0];
8628 const SMDS_MeshNode* n1 = *nIt[1];
8629 nodeGroupsToMerge.back().push_back( n1 );
8630 nodeGroupsToMerge.back().push_back( n0 );
8631 // position of node of the border changes due to merge
8632 param[ 0 ][ i[0] ] += du;
8633 // move n1 for the sake of elem shape evaluation during insertion.
8634 // n1 will be removed by MergeNodes() anyway
8635 const_cast<SMDS_MeshNode*>( n0 )->setXYZ( n1->X(), n1->Y(), n1->Z() );
8636 next[0] = next[1] = true;
8641 int intoBord = ( du < 0 ) ? 0 : 1;
8642 const SMDS_MeshElement* elem = *eIt [ intoBord ];
8643 const SMDS_MeshNode* n1 = nPrev[ intoBord ];
8644 const SMDS_MeshNode* n2 = *nIt [ intoBord ];
8645 const SMDS_MeshNode* nIns = *nIt [ 1 - intoBord ];
8646 if ( intoBord == 1 ) {
8647 // move node of the border to be on a link of elem of the side
8648 gp_XYZ p1 (n1->X(), n1->Y(), n1->Z());
8649 gp_XYZ p2 (n2->X(), n2->Y(), n2->Z());
8650 double ratio = du / ( param[ 1 ][ i[1] ] - param[ 1 ][ i[1]-1 ]);
8651 gp_XYZ p = p2 * ( 1 - ratio ) + p1 * ratio;
8652 GetMeshDS()->MoveNode( nIns, p.X(), p.Y(), p.Z() );
8654 insertMapIt = insertMap.find( elem );
8655 bool notFound = ( insertMapIt == insertMap.end() );
8656 bool otherLink = ( !notFound && (*insertMapIt).second.front() != n1 );
8658 // insert into another link of the same element:
8659 // 1. perform insertion into the other link of the elem
8660 list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8661 const SMDS_MeshNode* n12 = nodeList.front(); nodeList.pop_front();
8662 const SMDS_MeshNode* n22 = nodeList.front(); nodeList.pop_front();
8663 InsertNodesIntoLink( elem, n12, n22, nodeList, toCreatePolygons );
8664 // 2. perform insertion into the link of adjacent faces
8665 while ( const SMDS_MeshElement* adjElem = findAdjacentFace( n12, n22, elem )) {
8666 InsertNodesIntoLink( adjElem, n12, n22, nodeList, toCreatePolygons );
8668 while ( const SMDS_MeshElement* seg = findSegment( n12, n22 )) {
8669 InsertNodesIntoLink( seg, n12, n22, nodeList );
8671 if (toCreatePolyedrs) {
8672 // perform insertion into the links of adjacent volumes
8673 UpdateVolumes(n12, n22, nodeList);
8675 // 3. find an element appeared on n1 and n2 after the insertion
8676 insertMap.erase( elem );
8677 elem = findAdjacentFace( n1, n2, 0 );
8679 if ( notFound || otherLink ) {
8680 // add element and nodes of the side into the insertMap
8681 insertMapIt = insertMap.insert( make_pair( elem, list<const SMDS_MeshNode*>() )).first;
8682 (*insertMapIt).second.push_back( n1 );
8683 (*insertMapIt).second.push_back( n2 );
8685 // add node to be inserted into elem
8686 (*insertMapIt).second.push_back( nIns );
8687 next[ 1 - intoBord ] = true;
8690 // go to the next segment
8691 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8692 if ( next[ iBord ] ) {
8693 if ( i[ iBord ] != 0 && eIt[ iBord ] != eSide[ iBord ].end())
8695 nPrev[ iBord ] = *nIt[ iBord ];
8696 nIt[ iBord ]++; i[ iBord ]++;
8700 while ( nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end());
8702 // perform insertion of nodes into elements
8704 for (insertMapIt = insertMap.begin();
8705 insertMapIt != insertMap.end();
8708 const SMDS_MeshElement* elem = (*insertMapIt).first;
8709 list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8710 const SMDS_MeshNode* n1 = nodeList.front(); nodeList.pop_front();
8711 const SMDS_MeshNode* n2 = nodeList.front(); nodeList.pop_front();
8713 InsertNodesIntoLink( elem, n1, n2, nodeList, toCreatePolygons );
8715 while ( const SMDS_MeshElement* seg = findSegment( n1, n2 )) {
8716 InsertNodesIntoLink( seg, n1, n2, nodeList );
8719 if ( !theSideIsFreeBorder ) {
8720 // look for and insert nodes into the faces adjacent to elem
8721 while ( const SMDS_MeshElement* adjElem = findAdjacentFace( n1, n2, elem )) {
8722 InsertNodesIntoLink( adjElem, n1, n2, nodeList, toCreatePolygons );
8725 if (toCreatePolyedrs) {
8726 // perform insertion into the links of adjacent volumes
8727 UpdateVolumes(n1, n2, nodeList);
8730 } // end: insert new nodes
8732 MergeNodes ( nodeGroupsToMerge );
8735 // Remove coincident segments
8738 TIDSortedElemSet segments;
8739 SMESH_SequenceOfElemPtr newFaces;
8740 for ( int i = 1; i <= myLastCreatedElems.Length(); ++i )
8742 if ( !myLastCreatedElems(i) ) continue;
8743 if ( myLastCreatedElems(i)->GetType() == SMDSAbs_Edge )
8744 segments.insert( segments.end(), myLastCreatedElems(i) );
8746 newFaces.Append( myLastCreatedElems(i) );
8748 // get segments adjacent to merged nodes
8749 TListOfListOfNodes::iterator groupIt = nodeGroupsToMerge.begin();
8750 for ( ; groupIt != nodeGroupsToMerge.end(); groupIt++ )
8752 const list<const SMDS_MeshNode*>& nodes = *groupIt;
8753 SMDS_ElemIteratorPtr segIt = nodes.front()->GetInverseElementIterator( SMDSAbs_Edge );
8754 while ( segIt->more() )
8755 segments.insert( segIt->next() );
8759 TListOfListOfElementsID equalGroups;
8760 if ( !segments.empty() )
8761 FindEqualElements( segments, equalGroups );
8762 if ( !equalGroups.empty() )
8764 // remove from segments those that will be removed
8765 TListOfListOfElementsID::iterator itGroups = equalGroups.begin();
8766 for ( ; itGroups != equalGroups.end(); ++itGroups )
8768 list< int >& group = *itGroups;
8769 list< int >::iterator id = group.begin();
8770 for ( ++id; id != group.end(); ++id )
8771 if ( const SMDS_MeshElement* seg = GetMeshDS()->FindElement( *id ))
8772 segments.erase( seg );
8774 // remove equal segments
8775 MergeElements( equalGroups );
8777 // restore myLastCreatedElems
8778 myLastCreatedElems = newFaces;
8779 TIDSortedElemSet::iterator seg = segments.begin();
8780 for ( ; seg != segments.end(); ++seg )
8781 myLastCreatedElems.Append( *seg );
8787 //=======================================================================
8788 //function : InsertNodesIntoLink
8789 //purpose : insert theNodesToInsert into theElement between theBetweenNode1
8790 // and theBetweenNode2 and split theElement
8791 //=======================================================================
8793 void SMESH_MeshEditor::InsertNodesIntoLink(const SMDS_MeshElement* theElement,
8794 const SMDS_MeshNode* theBetweenNode1,
8795 const SMDS_MeshNode* theBetweenNode2,
8796 list<const SMDS_MeshNode*>& theNodesToInsert,
8797 const bool toCreatePoly)
8799 if ( !theElement ) return;
8801 SMESHDS_Mesh *aMesh = GetMeshDS();
8802 vector<const SMDS_MeshElement*> newElems;
8804 if ( theElement->GetType() == SMDSAbs_Edge )
8806 theNodesToInsert.push_front( theBetweenNode1 );
8807 theNodesToInsert.push_back ( theBetweenNode2 );
8808 list<const SMDS_MeshNode*>::iterator n = theNodesToInsert.begin();
8809 const SMDS_MeshNode* n1 = *n;
8810 for ( ++n; n != theNodesToInsert.end(); ++n )
8812 const SMDS_MeshNode* n2 = *n;
8813 if ( const SMDS_MeshElement* seg = aMesh->FindEdge( n1, n2 ))
8814 AddToSameGroups( seg, theElement, aMesh );
8816 newElems.push_back( aMesh->AddEdge ( n1, n2 ));
8819 theNodesToInsert.pop_front();
8820 theNodesToInsert.pop_back();
8822 if ( theElement->IsQuadratic() ) // add a not split part
8824 vector<const SMDS_MeshNode*> nodes( theElement->begin_nodes(),
8825 theElement->end_nodes() );
8826 int iOther = 0, nbN = nodes.size();
8827 for ( ; iOther < nbN; ++iOther )
8828 if ( nodes[iOther] != theBetweenNode1 &&
8829 nodes[iOther] != theBetweenNode2 )
8833 if ( const SMDS_MeshElement* seg = aMesh->FindEdge( nodes[0], nodes[1] ))
8834 AddToSameGroups( seg, theElement, aMesh );
8836 newElems.push_back( aMesh->AddEdge ( nodes[0], nodes[1] ));
8838 else if ( iOther == 2 )
8840 if ( const SMDS_MeshElement* seg = aMesh->FindEdge( nodes[1], nodes[2] ))
8841 AddToSameGroups( seg, theElement, aMesh );
8843 newElems.push_back( aMesh->AddEdge ( nodes[1], nodes[2] ));
8846 // treat new elements
8847 for ( size_t i = 0; i < newElems.size(); ++i )
8850 aMesh->SetMeshElementOnShape( newElems[i], theElement->getshapeId() );
8851 myLastCreatedElems.Append( newElems[i] );
8853 ReplaceElemInGroups( theElement, newElems, aMesh );
8854 aMesh->RemoveElement( theElement );
8857 } // if ( theElement->GetType() == SMDSAbs_Edge )
8859 const SMDS_MeshElement* theFace = theElement;
8860 if ( theFace->GetType() != SMDSAbs_Face ) return;
8862 // find indices of 2 link nodes and of the rest nodes
8863 int iNode = 0, il1, il2, i3, i4;
8864 il1 = il2 = i3 = i4 = -1;
8865 vector<const SMDS_MeshNode*> nodes( theFace->NbNodes() );
8867 SMDS_NodeIteratorPtr nodeIt = theFace->interlacedNodesIterator();
8868 while ( nodeIt->more() ) {
8869 const SMDS_MeshNode* n = nodeIt->next();
8870 if ( n == theBetweenNode1 )
8872 else if ( n == theBetweenNode2 )
8878 nodes[ iNode++ ] = n;
8880 if ( il1 < 0 || il2 < 0 || i3 < 0 )
8883 // arrange link nodes to go one after another regarding the face orientation
8884 bool reverse = ( Abs( il2 - il1 ) == 1 ? il2 < il1 : il1 < il2 );
8885 list<const SMDS_MeshNode *> aNodesToInsert = theNodesToInsert;
8890 aNodesToInsert.reverse();
8892 // check that not link nodes of a quadrangles are in good order
8893 int nbFaceNodes = theFace->NbNodes();
8894 if ( nbFaceNodes == 4 && i4 - i3 != 1 ) {
8900 if (toCreatePoly || theFace->IsPoly()) {
8903 vector<const SMDS_MeshNode *> poly_nodes (nbFaceNodes + aNodesToInsert.size());
8905 // add nodes of face up to first node of link
8908 if ( theFace->IsQuadratic() ) {
8909 const SMDS_VtkFace* F = dynamic_cast<const SMDS_VtkFace*>(theFace);
8910 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8911 // use special nodes iterator
8912 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8913 while( anIter->more() && !isFLN ) {
8914 const SMDS_MeshNode* n = cast2Node(anIter->next());
8915 poly_nodes[iNode++] = n;
8916 if (n == nodes[il1]) {
8920 // add nodes to insert
8921 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8922 for (; nIt != aNodesToInsert.end(); nIt++) {
8923 poly_nodes[iNode++] = *nIt;
8925 // add nodes of face starting from last node of link
8926 while ( anIter->more() ) {
8927 poly_nodes[iNode++] = cast2Node(anIter->next());
8931 SMDS_ElemIteratorPtr nodeIt = theFace->nodesIterator();
8932 while ( nodeIt->more() && !isFLN ) {
8933 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8934 poly_nodes[iNode++] = n;
8935 if (n == nodes[il1]) {
8939 // add nodes to insert
8940 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8941 for (; nIt != aNodesToInsert.end(); nIt++) {
8942 poly_nodes[iNode++] = *nIt;
8944 // add nodes of face starting from last node of link
8945 while ( nodeIt->more() ) {
8946 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8947 poly_nodes[iNode++] = n;
8952 newElems.push_back( aMesh->AddPolygonalFace( poly_nodes ));
8955 else if ( !theFace->IsQuadratic() )
8957 // put aNodesToInsert between theBetweenNode1 and theBetweenNode2
8958 int nbLinkNodes = 2 + aNodesToInsert.size();
8959 //const SMDS_MeshNode* linkNodes[ nbLinkNodes ];
8960 vector<const SMDS_MeshNode*> linkNodes( nbLinkNodes );
8961 linkNodes[ 0 ] = nodes[ il1 ];
8962 linkNodes[ nbLinkNodes - 1 ] = nodes[ il2 ];
8963 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8964 for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
8965 linkNodes[ iNode++ ] = *nIt;
8967 // decide how to split a quadrangle: compare possible variants
8968 // and choose which of splits to be a quadrangle
8969 int i1, i2, iSplit, nbSplits = nbLinkNodes - 1, iBestQuad = 0;
8970 if ( nbFaceNodes == 3 ) {
8971 iBestQuad = nbSplits;
8974 else if ( nbFaceNodes == 4 ) {
8975 SMESH::Controls::NumericalFunctorPtr aCrit( new SMESH::Controls::AspectRatio);
8976 double aBestRate = DBL_MAX;
8977 for ( int iQuad = 0; iQuad < nbSplits; iQuad++ ) {
8979 double aBadRate = 0;
8980 // evaluate elements quality
8981 for ( iSplit = 0; iSplit < nbSplits; iSplit++ ) {
8982 if ( iSplit == iQuad ) {
8983 SMDS_FaceOfNodes quad (linkNodes[ i1++ ],
8987 aBadRate += getBadRate( &quad, aCrit );
8990 SMDS_FaceOfNodes tria (linkNodes[ i1++ ],
8992 nodes[ iSplit < iQuad ? i4 : i3 ]);
8993 aBadRate += getBadRate( &tria, aCrit );
8997 if ( aBadRate < aBestRate ) {
8999 aBestRate = aBadRate;
9004 // create new elements
9006 for ( iSplit = 0; iSplit < nbSplits - 1; iSplit++ )
9008 if ( iSplit == iBestQuad )
9009 newElems.push_back( aMesh->AddFace (linkNodes[ i1++ ],
9014 newElems.push_back( aMesh->AddFace (linkNodes[ i1++ ],
9016 nodes[ iSplit < iBestQuad ? i4 : i3 ]));
9019 const SMDS_MeshNode* newNodes[ 4 ];
9020 newNodes[ 0 ] = linkNodes[ i1 ];
9021 newNodes[ 1 ] = linkNodes[ i2 ];
9022 newNodes[ 2 ] = nodes[ iSplit >= iBestQuad ? i3 : i4 ];
9023 newNodes[ 3 ] = nodes[ i4 ];
9024 if (iSplit == iBestQuad)
9025 newElems.push_back( aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2], newNodes[3] ));
9027 newElems.push_back( aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2] ));
9029 } // end if(!theFace->IsQuadratic())
9031 else { // theFace is quadratic
9032 // we have to split theFace on simple triangles and one simple quadrangle
9034 int nbshift = tmp*2;
9035 // shift nodes in nodes[] by nbshift
9037 for(i=0; i<nbshift; i++) {
9038 const SMDS_MeshNode* n = nodes[0];
9039 for(j=0; j<nbFaceNodes-1; j++) {
9040 nodes[j] = nodes[j+1];
9042 nodes[nbFaceNodes-1] = n;
9044 il1 = il1 - nbshift;
9045 // now have to insert nodes between n0 and n1 or n1 and n2 (see below)
9046 // n0 n1 n2 n0 n1 n2
9047 // +-----+-----+ +-----+-----+
9056 // create new elements
9058 if ( nbFaceNodes == 6 ) { // quadratic triangle
9059 newElems.push_back( aMesh->AddFace( nodes[3], nodes[4], nodes[5] ));
9060 if ( theFace->IsMediumNode(nodes[il1]) ) {
9061 // create quadrangle
9062 newElems.push_back( aMesh->AddFace( nodes[0], nodes[1], nodes[3], nodes[5] ));
9068 // create quadrangle
9069 newElems.push_back( aMesh->AddFace( nodes[1], nodes[2], nodes[3], nodes[5] ));
9075 else { // nbFaceNodes==8 - quadratic quadrangle
9076 newElems.push_back( aMesh->AddFace( nodes[3], nodes[4], nodes[5] ));
9077 newElems.push_back( aMesh->AddFace( nodes[5], nodes[6], nodes[7] ));
9078 newElems.push_back( aMesh->AddFace( nodes[5], nodes[7], nodes[3] ));
9079 if ( theFace->IsMediumNode( nodes[ il1 ])) {
9080 // create quadrangle
9081 newElems.push_back( aMesh->AddFace( nodes[0], nodes[1], nodes[3], nodes[7] ));
9087 // create quadrangle
9088 newElems.push_back( aMesh->AddFace( nodes[1], nodes[2], nodes[3], nodes[7] ));
9094 // create needed triangles using n1,n2,n3 and inserted nodes
9095 int nbn = 2 + aNodesToInsert.size();
9096 vector<const SMDS_MeshNode*> aNodes(nbn);
9097 aNodes[0 ] = nodes[n1];
9098 aNodes[nbn-1] = nodes[n2];
9099 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
9100 for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
9101 aNodes[iNode++] = *nIt;
9103 for ( i = 1; i < nbn; i++ )
9104 newElems.push_back( aMesh->AddFace( aNodes[i-1], aNodes[i], nodes[n3] ));
9107 // remove the old face
9108 for ( size_t i = 0; i < newElems.size(); ++i )
9111 aMesh->SetMeshElementOnShape( newElems[i], theFace->getshapeId() );
9112 myLastCreatedElems.Append( newElems[i] );
9114 ReplaceElemInGroups( theFace, newElems, aMesh );
9115 aMesh->RemoveElement(theFace);
9117 } // InsertNodesIntoLink()
9119 //=======================================================================
9120 //function : UpdateVolumes
9122 //=======================================================================
9124 void SMESH_MeshEditor::UpdateVolumes (const SMDS_MeshNode* theBetweenNode1,
9125 const SMDS_MeshNode* theBetweenNode2,
9126 list<const SMDS_MeshNode*>& theNodesToInsert)
9128 myLastCreatedElems.Clear();
9129 myLastCreatedNodes.Clear();
9131 SMDS_ElemIteratorPtr invElemIt = theBetweenNode1->GetInverseElementIterator(SMDSAbs_Volume);
9132 while (invElemIt->more()) { // loop on inverse elements of theBetweenNode1
9133 const SMDS_MeshElement* elem = invElemIt->next();
9135 // check, if current volume has link theBetweenNode1 - theBetweenNode2
9136 SMDS_VolumeTool aVolume (elem);
9137 if (!aVolume.IsLinked(theBetweenNode1, theBetweenNode2))
9140 // insert new nodes in all faces of the volume, sharing link theBetweenNode1 - theBetweenNode2
9141 int iface, nbFaces = aVolume.NbFaces();
9142 vector<const SMDS_MeshNode *> poly_nodes;
9143 vector<int> quantities (nbFaces);
9145 for (iface = 0; iface < nbFaces; iface++) {
9146 int nbFaceNodes = aVolume.NbFaceNodes(iface), nbInserted = 0;
9147 // faceNodes will contain (nbFaceNodes + 1) nodes, last = first
9148 const SMDS_MeshNode** faceNodes = aVolume.GetFaceNodes(iface);
9150 for (int inode = 0; inode < nbFaceNodes; inode++) {
9151 poly_nodes.push_back(faceNodes[inode]);
9153 if (nbInserted == 0) {
9154 if (faceNodes[inode] == theBetweenNode1) {
9155 if (faceNodes[inode + 1] == theBetweenNode2) {
9156 nbInserted = theNodesToInsert.size();
9158 // add nodes to insert
9159 list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.begin();
9160 for (; nIt != theNodesToInsert.end(); nIt++) {
9161 poly_nodes.push_back(*nIt);
9165 else if (faceNodes[inode] == theBetweenNode2) {
9166 if (faceNodes[inode + 1] == theBetweenNode1) {
9167 nbInserted = theNodesToInsert.size();
9169 // add nodes to insert in reversed order
9170 list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.end();
9172 for (; nIt != theNodesToInsert.begin(); nIt--) {
9173 poly_nodes.push_back(*nIt);
9175 poly_nodes.push_back(*nIt);
9182 quantities[iface] = nbFaceNodes + nbInserted;
9185 // Replace the volume
9186 SMESHDS_Mesh *aMesh = GetMeshDS();
9188 if ( SMDS_MeshElement* newElem = aMesh->AddPolyhedralVolume( poly_nodes, quantities ))
9190 aMesh->SetMeshElementOnShape( newElem, elem->getshapeId() );
9191 myLastCreatedElems.Append( newElem );
9192 ReplaceElemInGroups( elem, newElem, aMesh );
9194 aMesh->RemoveElement( elem );
9200 //================================================================================
9202 * \brief Transform any volume into data of SMDSEntity_Polyhedra
9204 //================================================================================
9206 void volumeToPolyhedron( const SMDS_MeshElement* elem,
9207 vector<const SMDS_MeshNode *> & nodes,
9208 vector<int> & nbNodeInFaces )
9211 nbNodeInFaces.clear();
9212 SMDS_VolumeTool vTool ( elem );
9213 for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
9215 const SMDS_MeshNode** fNodes = vTool.GetFaceNodes( iF );
9216 nodes.insert( nodes.end(), fNodes, fNodes + vTool.NbFaceNodes( iF ));
9217 nbNodeInFaces.push_back( vTool.NbFaceNodes( iF ));
9222 //=======================================================================
9224 * \brief Convert elements contained in a sub-mesh to quadratic
9225 * \return int - nb of checked elements
9227 //=======================================================================
9229 int SMESH_MeshEditor::convertElemToQuadratic(SMESHDS_SubMesh * theSm,
9230 SMESH_MesherHelper& theHelper,
9231 const bool theForce3d)
9233 //MESSAGE("convertElemToQuadratic");
9235 if( !theSm ) return nbElem;
9237 vector<int> nbNodeInFaces;
9238 vector<const SMDS_MeshNode *> nodes;
9239 SMDS_ElemIteratorPtr ElemItr = theSm->GetElements();
9240 while(ElemItr->more())
9243 const SMDS_MeshElement* elem = ElemItr->next();
9244 if( !elem ) continue;
9246 // analyse a necessity of conversion
9247 const SMDSAbs_ElementType aType = elem->GetType();
9248 if ( aType < SMDSAbs_Edge || aType > SMDSAbs_Volume )
9250 const SMDSAbs_EntityType aGeomType = elem->GetEntityType();
9251 bool hasCentralNodes = false;
9252 if ( elem->IsQuadratic() )
9255 switch ( aGeomType ) {
9256 case SMDSEntity_Quad_Triangle:
9257 case SMDSEntity_Quad_Quadrangle:
9258 case SMDSEntity_Quad_Hexa:
9259 case SMDSEntity_Quad_Penta:
9260 alreadyOK = !theHelper.GetIsBiQuadratic(); break;
9262 case SMDSEntity_BiQuad_Triangle:
9263 case SMDSEntity_BiQuad_Quadrangle:
9264 case SMDSEntity_TriQuad_Hexa:
9265 case SMDSEntity_BiQuad_Penta:
9266 alreadyOK = theHelper.GetIsBiQuadratic();
9267 hasCentralNodes = true;
9272 // take into account already present medium nodes
9274 case SMDSAbs_Volume:
9275 theHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( elem )); break;
9277 theHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( elem )); break;
9279 theHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( elem )); break;
9285 // get elem data needed to re-create it
9287 const int id = elem->GetID();
9288 const int nbNodes = elem->NbCornerNodes();
9289 nodes.assign(elem->begin_nodes(), elem->end_nodes());
9290 if ( aGeomType == SMDSEntity_Polyhedra )
9291 nbNodeInFaces = static_cast<const SMDS_VtkVolume* >( elem )->GetQuantities();
9292 else if ( aGeomType == SMDSEntity_Hexagonal_Prism )
9293 volumeToPolyhedron( elem, nodes, nbNodeInFaces );
9295 // remove a linear element
9296 GetMeshDS()->RemoveFreeElement(elem, theSm, /*fromGroups=*/false);
9298 // remove central nodes of biquadratic elements (biquad->quad conversion)
9299 if ( hasCentralNodes )
9300 for ( size_t i = nbNodes * 2; i < nodes.size(); ++i )
9301 if ( nodes[i]->NbInverseElements() == 0 )
9302 GetMeshDS()->RemoveFreeNode( nodes[i], theSm, /*fromGroups=*/true );
9304 const SMDS_MeshElement* NewElem = 0;
9310 NewElem = theHelper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9318 NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
9321 NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9324 NewElem = theHelper.AddPolygonalFace(nodes, id, theForce3d);
9328 case SMDSAbs_Volume :
9332 case SMDSEntity_Tetra:
9333 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9335 case SMDSEntity_Pyramid:
9336 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], id, theForce3d);
9338 case SMDSEntity_Penta:
9339 case SMDSEntity_Quad_Penta:
9340 case SMDSEntity_BiQuad_Penta:
9341 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], nodes[5], id, theForce3d);
9343 case SMDSEntity_Hexa:
9344 case SMDSEntity_Quad_Hexa:
9345 case SMDSEntity_TriQuad_Hexa:
9346 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9347 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9349 case SMDSEntity_Hexagonal_Prism:
9351 NewElem = theHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9358 ReplaceElemInGroups( elem, NewElem, GetMeshDS());
9359 if( NewElem && NewElem->getshapeId() < 1 )
9360 theSm->AddElement( NewElem );
9364 //=======================================================================
9365 //function : ConvertToQuadratic
9367 //=======================================================================
9369 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d, const bool theToBiQuad)
9371 //MESSAGE("ConvertToQuadratic "<< theForce3d << " " << theToBiQuad);
9372 SMESHDS_Mesh* meshDS = GetMeshDS();
9374 SMESH_MesherHelper aHelper(*myMesh);
9376 aHelper.SetIsQuadratic( true );
9377 aHelper.SetIsBiQuadratic( theToBiQuad );
9378 aHelper.SetElementsOnShape(true);
9379 aHelper.ToFixNodeParameters( true );
9381 // convert elements assigned to sub-meshes
9382 int nbCheckedElems = 0;
9383 if ( myMesh->HasShapeToMesh() )
9385 if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9387 SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9388 while ( smIt->more() ) {
9389 SMESH_subMesh* sm = smIt->next();
9390 if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() ) {
9391 aHelper.SetSubShape( sm->GetSubShape() );
9392 nbCheckedElems += convertElemToQuadratic(smDS, aHelper, theForce3d);
9398 // convert elements NOT assigned to sub-meshes
9399 int totalNbElems = meshDS->NbEdges() + meshDS->NbFaces() + meshDS->NbVolumes();
9400 if ( nbCheckedElems < totalNbElems ) // not all elements are in sub-meshes
9402 aHelper.SetElementsOnShape(false);
9403 SMESHDS_SubMesh *smDS = 0;
9406 SMDS_EdgeIteratorPtr aEdgeItr = meshDS->edgesIterator();
9407 while( aEdgeItr->more() )
9409 const SMDS_MeshEdge* edge = aEdgeItr->next();
9410 if ( !edge->IsQuadratic() )
9412 int id = edge->GetID();
9413 const SMDS_MeshNode* n1 = edge->GetNode(0);
9414 const SMDS_MeshNode* n2 = edge->GetNode(1);
9416 meshDS->RemoveFreeElement(edge, smDS, /*fromGroups=*/false);
9418 const SMDS_MeshEdge* NewEdge = aHelper.AddEdge(n1, n2, id, theForce3d);
9419 ReplaceElemInGroups( edge, NewEdge, GetMeshDS());
9423 aHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( edge ));
9428 SMDS_FaceIteratorPtr aFaceItr = meshDS->facesIterator();
9429 while( aFaceItr->more() )
9431 const SMDS_MeshFace* face = aFaceItr->next();
9432 if ( !face ) continue;
9434 const SMDSAbs_EntityType type = face->GetEntityType();
9438 case SMDSEntity_Quad_Triangle:
9439 case SMDSEntity_Quad_Quadrangle:
9440 alreadyOK = !theToBiQuad;
9441 aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
9443 case SMDSEntity_BiQuad_Triangle:
9444 case SMDSEntity_BiQuad_Quadrangle:
9445 alreadyOK = theToBiQuad;
9446 aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
9448 default: alreadyOK = false;
9453 const int id = face->GetID();
9454 vector<const SMDS_MeshNode *> nodes ( face->begin_nodes(), face->end_nodes());
9456 meshDS->RemoveFreeElement(face, smDS, /*fromGroups=*/false);
9458 SMDS_MeshFace * NewFace = 0;
9461 case SMDSEntity_Triangle:
9462 case SMDSEntity_Quad_Triangle:
9463 case SMDSEntity_BiQuad_Triangle:
9464 NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
9465 if ( nodes.size() == 7 && nodes[6]->NbInverseElements() == 0 ) // rm a central node
9466 GetMeshDS()->RemoveFreeNode( nodes[6], /*sm=*/0, /*fromGroups=*/true );
9469 case SMDSEntity_Quadrangle:
9470 case SMDSEntity_Quad_Quadrangle:
9471 case SMDSEntity_BiQuad_Quadrangle:
9472 NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9473 if ( nodes.size() == 9 && nodes[8]->NbInverseElements() == 0 ) // rm a central node
9474 GetMeshDS()->RemoveFreeNode( nodes[8], /*sm=*/0, /*fromGroups=*/true );
9478 NewFace = aHelper.AddPolygonalFace(nodes, id, theForce3d);
9480 ReplaceElemInGroups( face, NewFace, GetMeshDS());
9484 vector<int> nbNodeInFaces;
9485 SMDS_VolumeIteratorPtr aVolumeItr = meshDS->volumesIterator();
9486 while(aVolumeItr->more())
9488 const SMDS_MeshVolume* volume = aVolumeItr->next();
9489 if ( !volume ) continue;
9491 const SMDSAbs_EntityType type = volume->GetEntityType();
9492 if ( volume->IsQuadratic() )
9497 case SMDSEntity_Quad_Hexa: alreadyOK = !theToBiQuad; break;
9498 case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; break;
9499 case SMDSEntity_Quad_Penta: alreadyOK = !theToBiQuad; break;
9500 case SMDSEntity_BiQuad_Penta: alreadyOK = theToBiQuad; break;
9501 default: alreadyOK = true;
9505 aHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( volume ));
9509 const int id = volume->GetID();
9510 vector<const SMDS_MeshNode *> nodes (volume->begin_nodes(), volume->end_nodes());
9511 if ( type == SMDSEntity_Polyhedra )
9512 nbNodeInFaces = static_cast<const SMDS_VtkVolume* >(volume)->GetQuantities();
9513 else if ( type == SMDSEntity_Hexagonal_Prism )
9514 volumeToPolyhedron( volume, nodes, nbNodeInFaces );
9516 meshDS->RemoveFreeElement(volume, smDS, /*fromGroups=*/false);
9518 SMDS_MeshVolume * NewVolume = 0;
9521 case SMDSEntity_Tetra:
9522 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d );
9524 case SMDSEntity_Hexa:
9525 case SMDSEntity_Quad_Hexa:
9526 case SMDSEntity_TriQuad_Hexa:
9527 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9528 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9529 for ( size_t i = 20; i < nodes.size(); ++i ) // rm central nodes
9530 if ( nodes[i]->NbInverseElements() == 0 )
9531 GetMeshDS()->RemoveFreeNode( nodes[i], /*sm=*/0, /*fromGroups=*/true );
9533 case SMDSEntity_Pyramid:
9534 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9535 nodes[3], nodes[4], id, theForce3d);
9537 case SMDSEntity_Penta:
9538 case SMDSEntity_Quad_Penta:
9539 case SMDSEntity_BiQuad_Penta:
9540 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9541 nodes[3], nodes[4], nodes[5], id, theForce3d);
9542 for ( size_t i = 15; i < nodes.size(); ++i ) // rm central nodes
9543 if ( nodes[i]->NbInverseElements() == 0 )
9544 GetMeshDS()->RemoveFreeNode( nodes[i], /*sm=*/0, /*fromGroups=*/true );
9546 case SMDSEntity_Hexagonal_Prism:
9548 NewVolume = aHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9550 ReplaceElemInGroups(volume, NewVolume, meshDS);
9555 { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9556 // aHelper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9557 // aHelper.FixQuadraticElements(myError);
9558 SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
9562 //================================================================================
9564 * \brief Makes given elements quadratic
9565 * \param theForce3d - if true, the medium nodes will be placed in the middle of link
9566 * \param theElements - elements to make quadratic
9568 //================================================================================
9570 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d,
9571 TIDSortedElemSet& theElements,
9572 const bool theToBiQuad)
9574 if ( theElements.empty() ) return;
9576 // we believe that all theElements are of the same type
9577 const SMDSAbs_ElementType elemType = (*theElements.begin())->GetType();
9579 // get all nodes shared by theElements
9580 TIDSortedNodeSet allNodes;
9581 TIDSortedElemSet::iterator eIt = theElements.begin();
9582 for ( ; eIt != theElements.end(); ++eIt )
9583 allNodes.insert( (*eIt)->begin_nodes(), (*eIt)->end_nodes() );
9585 // complete theElements with elements of lower dim whose all nodes are in allNodes
9587 TIDSortedElemSet quadAdjacentElems [ SMDSAbs_NbElementTypes ]; // quadratic adjacent elements
9588 TIDSortedElemSet checkedAdjacentElems [ SMDSAbs_NbElementTypes ];
9589 TIDSortedNodeSet::iterator nIt = allNodes.begin();
9590 for ( ; nIt != allNodes.end(); ++nIt )
9592 const SMDS_MeshNode* n = *nIt;
9593 SMDS_ElemIteratorPtr invIt = n->GetInverseElementIterator();
9594 while ( invIt->more() )
9596 const SMDS_MeshElement* e = invIt->next();
9597 const SMDSAbs_ElementType type = e->GetType();
9598 if ( e->IsQuadratic() )
9600 quadAdjacentElems[ type ].insert( e );
9603 switch ( e->GetEntityType() ) {
9604 case SMDSEntity_Quad_Triangle:
9605 case SMDSEntity_Quad_Quadrangle:
9606 case SMDSEntity_Quad_Hexa: alreadyOK = !theToBiQuad; break;
9607 case SMDSEntity_BiQuad_Triangle:
9608 case SMDSEntity_BiQuad_Quadrangle:
9609 case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; break;
9610 default: alreadyOK = true;
9615 if ( type >= elemType )
9616 continue; // same type or more complex linear element
9618 if ( !checkedAdjacentElems[ type ].insert( e ).second )
9619 continue; // e is already checked
9623 SMDS_NodeIteratorPtr nodeIt = e->nodeIterator();
9624 while ( nodeIt->more() && allIn )
9625 allIn = allNodes.count( nodeIt->next() );
9627 theElements.insert(e );
9631 SMESH_MesherHelper helper(*myMesh);
9632 helper.SetIsQuadratic( true );
9633 helper.SetIsBiQuadratic( theToBiQuad );
9635 // add links of quadratic adjacent elements to the helper
9637 if ( !quadAdjacentElems[SMDSAbs_Edge].empty() )
9638 for ( eIt = quadAdjacentElems[SMDSAbs_Edge].begin();
9639 eIt != quadAdjacentElems[SMDSAbs_Edge].end(); ++eIt )
9641 helper.AddTLinks( static_cast< const SMDS_MeshEdge*> (*eIt) );
9643 if ( !quadAdjacentElems[SMDSAbs_Face].empty() )
9644 for ( eIt = quadAdjacentElems[SMDSAbs_Face].begin();
9645 eIt != quadAdjacentElems[SMDSAbs_Face].end(); ++eIt )
9647 helper.AddTLinks( static_cast< const SMDS_MeshFace*> (*eIt) );
9649 if ( !quadAdjacentElems[SMDSAbs_Volume].empty() )
9650 for ( eIt = quadAdjacentElems[SMDSAbs_Volume].begin();
9651 eIt != quadAdjacentElems[SMDSAbs_Volume].end(); ++eIt )
9653 helper.AddTLinks( static_cast< const SMDS_MeshVolume*> (*eIt) );
9656 // make quadratic (or bi-tri-quadratic) elements instead of linear ones
9658 SMESHDS_Mesh* meshDS = GetMeshDS();
9659 SMESHDS_SubMesh* smDS = 0;
9660 for ( eIt = theElements.begin(); eIt != theElements.end(); ++eIt )
9662 const SMDS_MeshElement* elem = *eIt;
9665 int nbCentralNodes = 0;
9666 switch ( elem->GetEntityType() ) {
9667 // linear convertible
9668 case SMDSEntity_Edge:
9669 case SMDSEntity_Triangle:
9670 case SMDSEntity_Quadrangle:
9671 case SMDSEntity_Tetra:
9672 case SMDSEntity_Pyramid:
9673 case SMDSEntity_Hexa:
9674 case SMDSEntity_Penta: alreadyOK = false; nbCentralNodes = 0; break;
9675 // quadratic that can become bi-quadratic
9676 case SMDSEntity_Quad_Triangle:
9677 case SMDSEntity_Quad_Quadrangle:
9678 case SMDSEntity_Quad_Hexa: alreadyOK =!theToBiQuad; nbCentralNodes = 0; break;
9680 case SMDSEntity_BiQuad_Triangle:
9681 case SMDSEntity_BiQuad_Quadrangle: alreadyOK = theToBiQuad; nbCentralNodes = 1; break;
9682 case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; nbCentralNodes = 7; break;
9684 default: alreadyOK = true;
9686 if ( alreadyOK ) continue;
9688 const SMDSAbs_ElementType type = elem->GetType();
9689 const int id = elem->GetID();
9690 const int nbNodes = elem->NbCornerNodes();
9691 vector<const SMDS_MeshNode *> nodes ( elem->begin_nodes(), elem->end_nodes());
9693 helper.SetSubShape( elem->getshapeId() );
9695 if ( !smDS || !smDS->Contains( elem ))
9696 smDS = meshDS->MeshElements( elem->getshapeId() );
9697 meshDS->RemoveFreeElement(elem, smDS, /*fromGroups=*/false);
9699 SMDS_MeshElement * newElem = 0;
9702 case 4: // cases for most frequently used element types go first (for optimization)
9703 if ( type == SMDSAbs_Volume )
9704 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9706 newElem = helper.AddFace (nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9709 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9710 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9713 newElem = helper.AddFace (nodes[0], nodes[1], nodes[2], id, theForce3d);
9716 newElem = helper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9719 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9720 nodes[4], id, theForce3d);
9723 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9724 nodes[4], nodes[5], id, theForce3d);
9728 ReplaceElemInGroups( elem, newElem, meshDS);
9729 if( newElem && smDS )
9730 smDS->AddElement( newElem );
9732 // remove central nodes
9733 for ( size_t i = nodes.size() - nbCentralNodes; i < nodes.size(); ++i )
9734 if ( nodes[i]->NbInverseElements() == 0 )
9735 meshDS->RemoveFreeNode( nodes[i], smDS, /*fromGroups=*/true );
9737 } // loop on theElements
9740 { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9741 // helper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9742 // helper.FixQuadraticElements( myError );
9743 SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
9747 //=======================================================================
9749 * \brief Convert quadratic elements to linear ones and remove quadratic nodes
9750 * \return int - nb of checked elements
9752 //=======================================================================
9754 int SMESH_MeshEditor::removeQuadElem(SMESHDS_SubMesh * theSm,
9755 SMDS_ElemIteratorPtr theItr,
9756 const int theShapeID)
9759 SMESHDS_Mesh* meshDS = GetMeshDS();
9760 ElemFeatures elemType;
9761 vector<const SMDS_MeshNode *> nodes;
9763 while( theItr->more() )
9765 const SMDS_MeshElement* elem = theItr->next();
9767 if( elem && elem->IsQuadratic())
9770 int nbCornerNodes = elem->NbCornerNodes();
9771 nodes.assign( elem->begin_nodes(), elem->end_nodes() );
9773 elemType.Init( elem, /*basicOnly=*/false ).SetID( elem->GetID() ).SetQuad( false );
9775 //remove a quadratic element
9776 if ( !theSm || !theSm->Contains( elem ))
9777 theSm = meshDS->MeshElements( elem->getshapeId() );
9778 meshDS->RemoveFreeElement( elem, theSm, /*fromGroups=*/false );
9780 // remove medium nodes
9781 for ( size_t i = nbCornerNodes; i < nodes.size(); ++i )
9782 if ( nodes[i]->NbInverseElements() == 0 )
9783 meshDS->RemoveFreeNode( nodes[i], theSm );
9785 // add a linear element
9786 nodes.resize( nbCornerNodes );
9787 SMDS_MeshElement * newElem = AddElement( nodes, elemType );
9788 ReplaceElemInGroups(elem, newElem, meshDS);
9789 if( theSm && newElem )
9790 theSm->AddElement( newElem );
9796 //=======================================================================
9797 //function : ConvertFromQuadratic
9799 //=======================================================================
9801 bool SMESH_MeshEditor::ConvertFromQuadratic()
9803 int nbCheckedElems = 0;
9804 if ( myMesh->HasShapeToMesh() )
9806 if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9808 SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9809 while ( smIt->more() ) {
9810 SMESH_subMesh* sm = smIt->next();
9811 if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() )
9812 nbCheckedElems += removeQuadElem( smDS, smDS->GetElements(), sm->GetId() );
9818 GetMeshDS()->NbEdges() + GetMeshDS()->NbFaces() + GetMeshDS()->NbVolumes();
9819 if ( nbCheckedElems < totalNbElems ) // not all elements are in submeshes
9821 SMESHDS_SubMesh *aSM = 0;
9822 removeQuadElem( aSM, GetMeshDS()->elementsIterator(), 0 );
9830 //================================================================================
9832 * \brief Return true if all medium nodes of the element are in the node set
9834 //================================================================================
9836 bool allMediumNodesIn(const SMDS_MeshElement* elem, TIDSortedNodeSet& nodeSet )
9838 for ( int i = elem->NbCornerNodes(); i < elem->NbNodes(); ++i )
9839 if ( !nodeSet.count( elem->GetNode(i) ))
9845 //================================================================================
9847 * \brief Makes given elements linear
9849 //================================================================================
9851 void SMESH_MeshEditor::ConvertFromQuadratic(TIDSortedElemSet& theElements)
9853 if ( theElements.empty() ) return;
9855 // collect IDs of medium nodes of theElements; some of these nodes will be removed
9856 set<int> mediumNodeIDs;
9857 TIDSortedElemSet::iterator eIt = theElements.begin();
9858 for ( ; eIt != theElements.end(); ++eIt )
9860 const SMDS_MeshElement* e = *eIt;
9861 for ( int i = e->NbCornerNodes(); i < e->NbNodes(); ++i )
9862 mediumNodeIDs.insert( e->GetNode(i)->GetID() );
9865 // replace given elements by linear ones
9866 SMDS_ElemIteratorPtr elemIt = elemSetIterator( theElements );
9867 removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9869 // we need to convert remaining elements whose all medium nodes are in mediumNodeIDs
9870 // except those elements sharing medium nodes of quadratic element whose medium nodes
9871 // are not all in mediumNodeIDs
9873 // get remaining medium nodes
9874 TIDSortedNodeSet mediumNodes;
9875 set<int>::iterator nIdsIt = mediumNodeIDs.begin();
9876 for ( ; nIdsIt != mediumNodeIDs.end(); ++nIdsIt )
9877 if ( const SMDS_MeshNode* n = GetMeshDS()->FindNode( *nIdsIt ))
9878 mediumNodes.insert( mediumNodes.end(), n );
9880 // find more quadratic elements to convert
9881 TIDSortedElemSet moreElemsToConvert;
9882 TIDSortedNodeSet::iterator nIt = mediumNodes.begin();
9883 for ( ; nIt != mediumNodes.end(); ++nIt )
9885 SMDS_ElemIteratorPtr invIt = (*nIt)->GetInverseElementIterator();
9886 while ( invIt->more() )
9888 const SMDS_MeshElement* e = invIt->next();
9889 if ( e->IsQuadratic() && allMediumNodesIn( e, mediumNodes ))
9891 // find a more complex element including e and
9892 // whose medium nodes are not in mediumNodes
9893 bool complexFound = false;
9894 for ( int type = e->GetType() + 1; type < SMDSAbs_0DElement; ++type )
9896 SMDS_ElemIteratorPtr invIt2 =
9897 (*nIt)->GetInverseElementIterator( SMDSAbs_ElementType( type ));
9898 while ( invIt2->more() )
9900 const SMDS_MeshElement* eComplex = invIt2->next();
9901 if ( eComplex->IsQuadratic() && !allMediumNodesIn( eComplex, mediumNodes))
9903 int nbCommonNodes = SMESH_MeshAlgos::GetCommonNodes( e, eComplex ).size();
9904 if ( nbCommonNodes == e->NbNodes())
9906 complexFound = true;
9907 type = SMDSAbs_NbElementTypes; // to quit from the outer loop
9913 if ( !complexFound )
9914 moreElemsToConvert.insert( e );
9918 elemIt = elemSetIterator( moreElemsToConvert );
9919 removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9922 //=======================================================================
9923 //function : SewSideElements
9925 //=======================================================================
9927 SMESH_MeshEditor::Sew_Error
9928 SMESH_MeshEditor::SewSideElements (TIDSortedElemSet& theSide1,
9929 TIDSortedElemSet& theSide2,
9930 const SMDS_MeshNode* theFirstNode1,
9931 const SMDS_MeshNode* theFirstNode2,
9932 const SMDS_MeshNode* theSecondNode1,
9933 const SMDS_MeshNode* theSecondNode2)
9935 myLastCreatedElems.Clear();
9936 myLastCreatedNodes.Clear();
9938 if ( theSide1.size() != theSide2.size() )
9939 return SEW_DIFF_NB_OF_ELEMENTS;
9941 Sew_Error aResult = SEW_OK;
9943 // 1. Build set of faces representing each side
9944 // 2. Find which nodes of the side 1 to merge with ones on the side 2
9945 // 3. Replace nodes in elements of the side 1 and remove replaced nodes
9947 // =======================================================================
9948 // 1. Build set of faces representing each side:
9949 // =======================================================================
9950 // a. build set of nodes belonging to faces
9951 // b. complete set of faces: find missing faces whose nodes are in set of nodes
9952 // c. create temporary faces representing side of volumes if correspondent
9953 // face does not exist
9955 SMESHDS_Mesh* aMesh = GetMeshDS();
9956 // TODO algorithm not OK with vtkUnstructuredGrid: 2 meshes can't share nodes
9957 //SMDS_Mesh aTmpFacesMesh; // try to use the same mesh
9958 TIDSortedElemSet faceSet1, faceSet2;
9959 set<const SMDS_MeshElement*> volSet1, volSet2;
9960 set<const SMDS_MeshNode*> nodeSet1, nodeSet2;
9961 TIDSortedElemSet * faceSetPtr[] = { &faceSet1, &faceSet2 };
9962 set<const SMDS_MeshElement*> * volSetPtr[] = { &volSet1, &volSet2 };
9963 set<const SMDS_MeshNode*> * nodeSetPtr[] = { &nodeSet1, &nodeSet2 };
9964 TIDSortedElemSet * elemSetPtr[] = { &theSide1, &theSide2 };
9965 int iSide, iFace, iNode;
9967 list<const SMDS_MeshElement* > tempFaceList;
9968 for ( iSide = 0; iSide < 2; iSide++ ) {
9969 set<const SMDS_MeshNode*> * nodeSet = nodeSetPtr[ iSide ];
9970 TIDSortedElemSet * elemSet = elemSetPtr[ iSide ];
9971 TIDSortedElemSet * faceSet = faceSetPtr[ iSide ];
9972 set<const SMDS_MeshElement*> * volSet = volSetPtr [ iSide ];
9973 set<const SMDS_MeshElement*>::iterator vIt;
9974 TIDSortedElemSet::iterator eIt;
9975 set<const SMDS_MeshNode*>::iterator nIt;
9977 // check that given nodes belong to given elements
9978 const SMDS_MeshNode* n1 = ( iSide == 0 ) ? theFirstNode1 : theFirstNode2;
9979 const SMDS_MeshNode* n2 = ( iSide == 0 ) ? theSecondNode1 : theSecondNode2;
9980 int firstIndex = -1, secondIndex = -1;
9981 for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9982 const SMDS_MeshElement* elem = *eIt;
9983 if ( firstIndex < 0 ) firstIndex = elem->GetNodeIndex( n1 );
9984 if ( secondIndex < 0 ) secondIndex = elem->GetNodeIndex( n2 );
9985 if ( firstIndex > -1 && secondIndex > -1 ) break;
9987 if ( firstIndex < 0 || secondIndex < 0 ) {
9988 // we can simply return until temporary faces created
9989 return (iSide == 0 ) ? SEW_BAD_SIDE1_NODES : SEW_BAD_SIDE2_NODES;
9992 // -----------------------------------------------------------
9993 // 1a. Collect nodes of existing faces
9994 // and build set of face nodes in order to detect missing
9995 // faces corresponding to sides of volumes
9996 // -----------------------------------------------------------
9998 set< set <const SMDS_MeshNode*> > setOfFaceNodeSet;
10000 // loop on the given element of a side
10001 for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
10002 //const SMDS_MeshElement* elem = *eIt;
10003 const SMDS_MeshElement* elem = *eIt;
10004 if ( elem->GetType() == SMDSAbs_Face ) {
10005 faceSet->insert( elem );
10006 set <const SMDS_MeshNode*> faceNodeSet;
10007 SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
10008 while ( nodeIt->more() ) {
10009 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10010 nodeSet->insert( n );
10011 faceNodeSet.insert( n );
10013 setOfFaceNodeSet.insert( faceNodeSet );
10015 else if ( elem->GetType() == SMDSAbs_Volume )
10016 volSet->insert( elem );
10018 // ------------------------------------------------------------------------------
10019 // 1b. Complete set of faces: find missing faces whose nodes are in set of nodes
10020 // ------------------------------------------------------------------------------
10022 for ( nIt = nodeSet->begin(); nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
10023 SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
10024 while ( fIt->more() ) { // loop on faces sharing a node
10025 const SMDS_MeshElement* f = fIt->next();
10026 if ( faceSet->find( f ) == faceSet->end() ) {
10027 // check if all nodes are in nodeSet and
10028 // complete setOfFaceNodeSet if they are
10029 set <const SMDS_MeshNode*> faceNodeSet;
10030 SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
10031 bool allInSet = true;
10032 while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
10033 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10034 if ( nodeSet->find( n ) == nodeSet->end() )
10037 faceNodeSet.insert( n );
10040 faceSet->insert( f );
10041 setOfFaceNodeSet.insert( faceNodeSet );
10047 // -------------------------------------------------------------------------
10048 // 1c. Create temporary faces representing sides of volumes if correspondent
10049 // face does not exist
10050 // -------------------------------------------------------------------------
10052 if ( !volSet->empty() ) {
10053 //int nodeSetSize = nodeSet->size();
10055 // loop on given volumes
10056 for ( vIt = volSet->begin(); vIt != volSet->end(); vIt++ ) {
10057 SMDS_VolumeTool vol (*vIt);
10058 // loop on volume faces: find free faces
10059 // --------------------------------------
10060 list<const SMDS_MeshElement* > freeFaceList;
10061 for ( iFace = 0; iFace < vol.NbFaces(); iFace++ ) {
10062 if ( !vol.IsFreeFace( iFace ))
10064 // check if there is already a face with same nodes in a face set
10065 const SMDS_MeshElement* aFreeFace = 0;
10066 const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iFace );
10067 int nbNodes = vol.NbFaceNodes( iFace );
10068 set <const SMDS_MeshNode*> faceNodeSet;
10069 vol.GetFaceNodes( iFace, faceNodeSet );
10070 bool isNewFace = setOfFaceNodeSet.insert( faceNodeSet ).second;
10072 // no such a face is given but it still can exist, check it
10073 vector<const SMDS_MeshNode *> nodes ( fNodes, fNodes + nbNodes);
10074 aFreeFace = aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false );
10076 if ( !aFreeFace ) {
10077 // create a temporary face
10078 if ( nbNodes == 3 ) {
10079 //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2] );
10080 aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2] );
10082 else if ( nbNodes == 4 ) {
10083 //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
10084 aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
10087 vector<const SMDS_MeshNode *> poly_nodes ( fNodes, & fNodes[nbNodes]);
10088 //aFreeFace = aTmpFacesMesh.AddPolygonalFace(poly_nodes);
10089 aFreeFace = aMesh->AddPolygonalFace(poly_nodes);
10092 tempFaceList.push_back( aFreeFace );
10096 freeFaceList.push_back( aFreeFace );
10098 } // loop on faces of a volume
10100 // choose one of several free faces of a volume
10101 // --------------------------------------------
10102 if ( freeFaceList.size() > 1 ) {
10103 // choose a face having max nb of nodes shared by other elems of a side
10104 int maxNbNodes = -1;
10105 list<const SMDS_MeshElement* >::iterator fIt = freeFaceList.begin();
10106 while ( fIt != freeFaceList.end() ) { // loop on free faces
10107 int nbSharedNodes = 0;
10108 SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
10109 while ( nodeIt->more() ) { // loop on free face nodes
10110 const SMDS_MeshNode* n =
10111 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10112 SMDS_ElemIteratorPtr invElemIt = n->GetInverseElementIterator();
10113 while ( invElemIt->more() ) {
10114 const SMDS_MeshElement* e = invElemIt->next();
10115 nbSharedNodes += faceSet->count( e );
10116 nbSharedNodes += elemSet->count( e );
10119 if ( nbSharedNodes > maxNbNodes ) {
10120 maxNbNodes = nbSharedNodes;
10121 freeFaceList.erase( freeFaceList.begin(), fIt++ );
10123 else if ( nbSharedNodes == maxNbNodes ) {
10127 freeFaceList.erase( fIt++ ); // here fIt++ occurs before erase
10130 if ( freeFaceList.size() > 1 )
10132 // could not choose one face, use another way
10133 // choose a face most close to the bary center of the opposite side
10134 gp_XYZ aBC( 0., 0., 0. );
10135 set <const SMDS_MeshNode*> addedNodes;
10136 TIDSortedElemSet * elemSet2 = elemSetPtr[ 1 - iSide ];
10137 eIt = elemSet2->begin();
10138 for ( eIt = elemSet2->begin(); eIt != elemSet2->end(); eIt++ ) {
10139 SMDS_ElemIteratorPtr nodeIt = (*eIt)->nodesIterator();
10140 while ( nodeIt->more() ) { // loop on free face nodes
10141 const SMDS_MeshNode* n =
10142 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10143 if ( addedNodes.insert( n ).second )
10144 aBC += gp_XYZ( n->X(),n->Y(),n->Z() );
10147 aBC /= addedNodes.size();
10148 double minDist = DBL_MAX;
10149 fIt = freeFaceList.begin();
10150 while ( fIt != freeFaceList.end() ) { // loop on free faces
10152 SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
10153 while ( nodeIt->more() ) { // loop on free face nodes
10154 const SMDS_MeshNode* n =
10155 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10156 gp_XYZ p( n->X(),n->Y(),n->Z() );
10157 dist += ( aBC - p ).SquareModulus();
10159 if ( dist < minDist ) {
10161 freeFaceList.erase( freeFaceList.begin(), fIt++ );
10164 fIt = freeFaceList.erase( fIt++ );
10167 } // choose one of several free faces of a volume
10169 if ( freeFaceList.size() == 1 ) {
10170 const SMDS_MeshElement* aFreeFace = freeFaceList.front();
10171 faceSet->insert( aFreeFace );
10172 // complete a node set with nodes of a found free face
10173 // for ( iNode = 0; iNode < ; iNode++ )
10174 // nodeSet->insert( fNodes[ iNode ] );
10177 } // loop on volumes of a side
10179 // // complete a set of faces if new nodes in a nodeSet appeared
10180 // // ----------------------------------------------------------
10181 // if ( nodeSetSize != nodeSet->size() ) {
10182 // for ( ; nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
10183 // SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
10184 // while ( fIt->more() ) { // loop on faces sharing a node
10185 // const SMDS_MeshElement* f = fIt->next();
10186 // if ( faceSet->find( f ) == faceSet->end() ) {
10187 // // check if all nodes are in nodeSet and
10188 // // complete setOfFaceNodeSet if they are
10189 // set <const SMDS_MeshNode*> faceNodeSet;
10190 // SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
10191 // bool allInSet = true;
10192 // while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
10193 // const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10194 // if ( nodeSet->find( n ) == nodeSet->end() )
10195 // allInSet = false;
10197 // faceNodeSet.insert( n );
10199 // if ( allInSet ) {
10200 // faceSet->insert( f );
10201 // setOfFaceNodeSet.insert( faceNodeSet );
10207 } // Create temporary faces, if there are volumes given
10210 if ( faceSet1.size() != faceSet2.size() ) {
10211 // delete temporary faces: they are in reverseElements of actual nodes
10212 // SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
10213 // while ( tmpFaceIt->more() )
10214 // aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
10215 // list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
10216 // for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
10217 // aMesh->RemoveElement(*tmpFaceIt);
10218 MESSAGE("Diff nb of faces");
10219 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10222 // ============================================================
10223 // 2. Find nodes to merge:
10224 // bind a node to remove to a node to put instead
10225 // ============================================================
10227 TNodeNodeMap nReplaceMap; // bind a node to remove to a node to put instead
10228 if ( theFirstNode1 != theFirstNode2 )
10229 nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
10230 if ( theSecondNode1 != theSecondNode2 )
10231 nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
10233 LinkID_Gen aLinkID_Gen( GetMeshDS() );
10234 set< long > linkIdSet; // links to process
10235 linkIdSet.insert( aLinkID_Gen.GetLinkID( theFirstNode1, theSecondNode1 ));
10237 typedef pair< const SMDS_MeshNode*, const SMDS_MeshNode* > NLink;
10238 list< NLink > linkList[2];
10239 linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
10240 linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
10241 // loop on links in linkList; find faces by links and append links
10242 // of the found faces to linkList
10243 list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
10244 for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ )
10246 NLink link[] = { *linkIt[0], *linkIt[1] };
10247 long linkID = aLinkID_Gen.GetLinkID( link[0].first, link[0].second );
10248 if ( !linkIdSet.count( linkID ) )
10251 // by links, find faces in the face sets,
10252 // and find indices of link nodes in the found faces;
10253 // in a face set, there is only one or no face sharing a link
10254 // ---------------------------------------------------------------
10256 const SMDS_MeshElement* face[] = { 0, 0 };
10257 vector<const SMDS_MeshNode*> fnodes[2];
10258 int iLinkNode[2][2];
10259 TIDSortedElemSet avoidSet;
10260 for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
10261 const SMDS_MeshNode* n1 = link[iSide].first;
10262 const SMDS_MeshNode* n2 = link[iSide].second;
10263 //cout << "Side " << iSide << " ";
10264 //cout << "L( " << n1->GetID() << ", " << n2->GetID() << " ) " << endl;
10265 // find a face by two link nodes
10266 face[ iSide ] = SMESH_MeshAlgos::FindFaceInSet( n1, n2,
10267 *faceSetPtr[ iSide ], avoidSet,
10268 &iLinkNode[iSide][0],
10269 &iLinkNode[iSide][1] );
10270 if ( face[ iSide ])
10272 //cout << " F " << face[ iSide]->GetID() <<endl;
10273 faceSetPtr[ iSide ]->erase( face[ iSide ]);
10274 // put face nodes to fnodes
10275 if ( face[ iSide ]->IsQuadratic() )
10277 // use interlaced nodes iterator
10278 const SMDS_VtkFace* F = dynamic_cast<const SMDS_VtkFace*>( face[ iSide ]);
10279 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
10280 SMDS_ElemIteratorPtr nIter = F->interlacedNodesElemIterator();
10281 while ( nIter->more() )
10282 fnodes[ iSide ].push_back( cast2Node( nIter->next() ));
10286 fnodes[ iSide ].assign( face[ iSide ]->begin_nodes(),
10287 face[ iSide ]->end_nodes() );
10289 fnodes[ iSide ].push_back( fnodes[ iSide ].front());
10293 // check similarity of elements of the sides
10294 if (aResult == SEW_OK && (( face[0] && !face[1] ) || ( !face[0] && face[1] ))) {
10295 MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
10296 if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
10297 aResult = ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10300 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10302 break; // do not return because it's necessary to remove tmp faces
10305 // set nodes to merge
10306 // -------------------
10308 if ( face[0] && face[1] ) {
10309 const int nbNodes = face[0]->NbNodes();
10310 if ( nbNodes != face[1]->NbNodes() ) {
10311 MESSAGE("Diff nb of face nodes");
10312 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10313 break; // do not return because it s necessary to remove tmp faces
10315 bool reverse[] = { false, false }; // order of nodes in the link
10316 for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
10317 // analyse link orientation in faces
10318 int i1 = iLinkNode[ iSide ][ 0 ];
10319 int i2 = iLinkNode[ iSide ][ 1 ];
10320 reverse[ iSide ] = Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1;
10322 int di1 = reverse[0] ? -1 : +1, i1 = iLinkNode[0][1] + di1;
10323 int di2 = reverse[1] ? -1 : +1, i2 = iLinkNode[1][1] + di2;
10324 for ( int i = nbNodes - 2; i > 0; --i, i1 += di1, i2 += di2 )
10326 nReplaceMap.insert ( make_pair ( fnodes[0][ ( i1 + nbNodes ) % nbNodes ],
10327 fnodes[1][ ( i2 + nbNodes ) % nbNodes ]));
10330 // add other links of the faces to linkList
10331 // -----------------------------------------
10333 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
10334 linkID = aLinkID_Gen.GetLinkID( fnodes[0][iNode], fnodes[0][iNode+1] );
10335 pair< set<long>::iterator, bool > iter_isnew = linkIdSet.insert( linkID );
10336 if ( !iter_isnew.second ) { // already in a set: no need to process
10337 linkIdSet.erase( iter_isnew.first );
10339 else // new in set == encountered for the first time: add
10341 const SMDS_MeshNode* n1 = fnodes[0][ iNode ];
10342 const SMDS_MeshNode* n2 = fnodes[0][ iNode + 1];
10343 linkList[0].push_back ( NLink( n1, n2 ));
10344 linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10349 if ( faceSetPtr[0]->empty() || faceSetPtr[1]->empty() )
10352 } // loop on link lists
10354 if ( aResult == SEW_OK &&
10355 ( //linkIt[0] != linkList[0].end() ||
10356 !faceSetPtr[0]->empty() || !faceSetPtr[1]->empty() )) {
10357 MESSAGE( (linkIt[0] != linkList[0].end()) <<" "<< (faceSetPtr[0]->empty()) <<
10358 " " << (faceSetPtr[1]->empty()));
10359 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10362 // ====================================================================
10363 // 3. Replace nodes in elements of the side 1 and remove replaced nodes
10364 // ====================================================================
10366 // delete temporary faces
10367 // SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
10368 // while ( tmpFaceIt->more() )
10369 // aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
10370 list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
10371 for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
10372 aMesh->RemoveElement(*tmpFaceIt);
10374 if ( aResult != SEW_OK)
10377 list< int > nodeIDsToRemove;
10378 vector< const SMDS_MeshNode*> nodes;
10379 ElemFeatures elemType;
10381 // loop on nodes replacement map
10382 TNodeNodeMap::iterator nReplaceMapIt = nReplaceMap.begin(), nnIt;
10383 for ( ; nReplaceMapIt != nReplaceMap.end(); nReplaceMapIt++ )
10384 if ( (*nReplaceMapIt).first != (*nReplaceMapIt).second )
10386 const SMDS_MeshNode* nToRemove = (*nReplaceMapIt).first;
10387 nodeIDsToRemove.push_back( nToRemove->GetID() );
10388 // loop on elements sharing nToRemove
10389 SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
10390 while ( invElemIt->more() ) {
10391 const SMDS_MeshElement* e = invElemIt->next();
10392 // get a new suite of nodes: make replacement
10393 int nbReplaced = 0, i = 0, nbNodes = e->NbNodes();
10394 nodes.resize( nbNodes );
10395 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
10396 while ( nIt->more() ) {
10397 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nIt->next() );
10398 nnIt = nReplaceMap.find( n );
10399 if ( nnIt != nReplaceMap.end() ) {
10401 n = (*nnIt).second;
10405 // if ( nbReplaced == nbNodes && e->GetType() == SMDSAbs_Face )
10406 // elemIDsToRemove.push_back( e->GetID() );
10410 elemType.Init( e, /*basicOnly=*/false ).SetID( e->GetID() );
10411 aMesh->RemoveElement( e );
10413 if ( SMDS_MeshElement* newElem = this->AddElement( nodes, elemType ))
10415 AddToSameGroups( newElem, e, aMesh );
10416 if ( int aShapeId = e->getshapeId() )
10417 aMesh->SetMeshElementOnShape( newElem, aShapeId );
10423 Remove( nodeIDsToRemove, true );
10428 //================================================================================
10430 * \brief Find corresponding nodes in two sets of faces
10431 * \param theSide1 - first face set
10432 * \param theSide2 - second first face
10433 * \param theFirstNode1 - a boundary node of set 1
10434 * \param theFirstNode2 - a node of set 2 corresponding to theFirstNode1
10435 * \param theSecondNode1 - a boundary node of set 1 linked with theFirstNode1
10436 * \param theSecondNode2 - a node of set 2 corresponding to theSecondNode1
10437 * \param nReplaceMap - output map of corresponding nodes
10438 * \return bool - is a success or not
10440 //================================================================================
10443 //#define DEBUG_MATCHING_NODES
10446 SMESH_MeshEditor::Sew_Error
10447 SMESH_MeshEditor::FindMatchingNodes(set<const SMDS_MeshElement*>& theSide1,
10448 set<const SMDS_MeshElement*>& theSide2,
10449 const SMDS_MeshNode* theFirstNode1,
10450 const SMDS_MeshNode* theFirstNode2,
10451 const SMDS_MeshNode* theSecondNode1,
10452 const SMDS_MeshNode* theSecondNode2,
10453 TNodeNodeMap & nReplaceMap)
10455 set<const SMDS_MeshElement*> * faceSetPtr[] = { &theSide1, &theSide2 };
10457 nReplaceMap.clear();
10458 if ( theFirstNode1 != theFirstNode2 )
10459 nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
10460 if ( theSecondNode1 != theSecondNode2 )
10461 nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
10463 set< SMESH_TLink > linkSet; // set of nodes where order of nodes is ignored
10464 linkSet.insert( SMESH_TLink( theFirstNode1, theSecondNode1 ));
10466 list< NLink > linkList[2];
10467 linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
10468 linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
10470 // loop on links in linkList; find faces by links and append links
10471 // of the found faces to linkList
10472 list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
10473 for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ ) {
10474 NLink link[] = { *linkIt[0], *linkIt[1] };
10475 if ( linkSet.find( link[0] ) == linkSet.end() )
10478 // by links, find faces in the face sets,
10479 // and find indices of link nodes in the found faces;
10480 // in a face set, there is only one or no face sharing a link
10481 // ---------------------------------------------------------------
10483 const SMDS_MeshElement* face[] = { 0, 0 };
10484 list<const SMDS_MeshNode*> notLinkNodes[2];
10485 //bool reverse[] = { false, false }; // order of notLinkNodes
10487 for ( int iSide = 0; iSide < 2; iSide++ ) // loop on 2 sides
10489 const SMDS_MeshNode* n1 = link[iSide].first;
10490 const SMDS_MeshNode* n2 = link[iSide].second;
10491 set<const SMDS_MeshElement*> * faceSet = faceSetPtr[ iSide ];
10492 set< const SMDS_MeshElement* > facesOfNode1;
10493 for ( int iNode = 0; iNode < 2; iNode++ ) // loop on 2 nodes of a link
10495 // during a loop of the first node, we find all faces around n1,
10496 // during a loop of the second node, we find one face sharing both n1 and n2
10497 const SMDS_MeshNode* n = iNode ? n1 : n2; // a node of a link
10498 SMDS_ElemIteratorPtr fIt = n->GetInverseElementIterator(SMDSAbs_Face);
10499 while ( fIt->more() ) { // loop on faces sharing a node
10500 const SMDS_MeshElement* f = fIt->next();
10501 if (faceSet->find( f ) != faceSet->end() && // f is in face set
10502 ! facesOfNode1.insert( f ).second ) // f encounters twice
10504 if ( face[ iSide ] ) {
10505 MESSAGE( "2 faces per link " );
10506 return ( iSide ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10509 faceSet->erase( f );
10511 // get not link nodes
10512 int nbN = f->NbNodes();
10513 if ( f->IsQuadratic() )
10515 nbNodes[ iSide ] = nbN;
10516 list< const SMDS_MeshNode* > & nodes = notLinkNodes[ iSide ];
10517 int i1 = f->GetNodeIndex( n1 );
10518 int i2 = f->GetNodeIndex( n2 );
10519 int iEnd = nbN, iBeg = -1, iDelta = 1;
10520 bool reverse = ( Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1 );
10522 std::swap( iEnd, iBeg ); iDelta = -1;
10527 if ( i == iEnd ) i = iBeg + iDelta;
10528 if ( i == i1 ) break;
10529 nodes.push_back ( f->GetNode( i ) );
10535 // check similarity of elements of the sides
10536 if (( face[0] && !face[1] ) || ( !face[0] && face[1] )) {
10537 MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
10538 if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
10539 return ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10542 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10546 // set nodes to merge
10547 // -------------------
10549 if ( face[0] && face[1] ) {
10550 if ( nbNodes[0] != nbNodes[1] ) {
10551 MESSAGE("Diff nb of face nodes");
10552 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10554 #ifdef DEBUG_MATCHING_NODES
10555 MESSAGE ( " Link 1: " << link[0].first->GetID() <<" "<< link[0].second->GetID()
10556 << " F 1: " << face[0] << "| Link 2: " << link[1].first->GetID() <<" "
10557 << link[1].second->GetID() << " F 2: " << face[1] << " | Bind: " ) ;
10559 int nbN = nbNodes[0];
10561 list<const SMDS_MeshNode*>::iterator n1 = notLinkNodes[0].begin();
10562 list<const SMDS_MeshNode*>::iterator n2 = notLinkNodes[1].begin();
10563 for ( int i = 0 ; i < nbN - 2; ++i ) {
10564 #ifdef DEBUG_MATCHING_NODES
10565 MESSAGE ( (*n1)->GetID() << " to " << (*n2)->GetID() );
10567 nReplaceMap.insert( make_pair( *(n1++), *(n2++) ));
10571 // add other links of the face 1 to linkList
10572 // -----------------------------------------
10574 const SMDS_MeshElement* f0 = face[0];
10575 const SMDS_MeshNode* n1 = f0->GetNode( nbN - 1 );
10576 for ( int i = 0; i < nbN; i++ )
10578 const SMDS_MeshNode* n2 = f0->GetNode( i );
10579 pair< set< SMESH_TLink >::iterator, bool > iter_isnew =
10580 linkSet.insert( SMESH_TLink( n1, n2 ));
10581 if ( !iter_isnew.second ) { // already in a set: no need to process
10582 linkSet.erase( iter_isnew.first );
10584 else // new in set == encountered for the first time: add
10586 #ifdef DEBUG_MATCHING_NODES
10587 MESSAGE ( "Add link 1: " << n1->GetID() << " " << n2->GetID() << " "
10588 << " | link 2: " << nReplaceMap[n1]->GetID() << " " << nReplaceMap[n2]->GetID() << " " );
10590 linkList[0].push_back ( NLink( n1, n2 ));
10591 linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10596 } // loop on link lists
10601 //================================================================================
10603 * \brief Create elements equal (on same nodes) to given ones
10604 * \param [in] theElements - a set of elems to duplicate. If it is empty, all
10605 * elements of the uppest dimension are duplicated.
10607 //================================================================================
10609 void SMESH_MeshEditor::DoubleElements( const TIDSortedElemSet& theElements )
10611 ClearLastCreated();
10612 SMESHDS_Mesh* mesh = GetMeshDS();
10614 // get an element type and an iterator over elements
10616 SMDSAbs_ElementType type = SMDSAbs_All;
10617 SMDS_ElemIteratorPtr elemIt;
10618 vector< const SMDS_MeshElement* > allElems;
10619 if ( theElements.empty() )
10621 if ( mesh->NbNodes() == 0 )
10623 // get most complex type
10624 SMDSAbs_ElementType types[SMDSAbs_NbElementTypes] = {
10625 SMDSAbs_Volume, SMDSAbs_Face, SMDSAbs_Edge,
10626 SMDSAbs_0DElement, SMDSAbs_Ball, SMDSAbs_Node
10628 for ( int i = 0; i < SMDSAbs_NbElementTypes; ++i )
10629 if ( mesh->GetMeshInfo().NbElements( types[i] ))
10634 // put all elements in the vector <allElems>
10635 allElems.reserve( mesh->GetMeshInfo().NbElements( type ));
10636 elemIt = mesh->elementsIterator( type );
10637 while ( elemIt->more() )
10638 allElems.push_back( elemIt->next());
10639 elemIt = elemSetIterator( allElems );
10643 type = (*theElements.begin())->GetType();
10644 elemIt = elemSetIterator( theElements );
10647 // duplicate elements
10649 ElemFeatures elemType;
10651 vector< const SMDS_MeshNode* > nodes;
10652 while ( elemIt->more() )
10654 const SMDS_MeshElement* elem = elemIt->next();
10655 if ( elem->GetType() != type )
10658 elemType.Init( elem, /*basicOnly=*/false );
10659 nodes.assign( elem->begin_nodes(), elem->end_nodes() );
10661 AddElement( nodes, elemType );
10665 //================================================================================
10667 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10668 \param theElems - the list of elements (edges or faces) to be replicated
10669 The nodes for duplication could be found from these elements
10670 \param theNodesNot - list of nodes to NOT replicate
10671 \param theAffectedElems - the list of elements (cells and edges) to which the
10672 replicated nodes should be associated to.
10673 \return TRUE if operation has been completed successfully, FALSE otherwise
10675 //================================================================================
10677 bool SMESH_MeshEditor::DoubleNodes( const TIDSortedElemSet& theElems,
10678 const TIDSortedElemSet& theNodesNot,
10679 const TIDSortedElemSet& theAffectedElems )
10681 myLastCreatedElems.Clear();
10682 myLastCreatedNodes.Clear();
10684 if ( theElems.size() == 0 )
10687 SMESHDS_Mesh* aMeshDS = GetMeshDS();
10692 TNodeNodeMap anOldNodeToNewNode;
10693 // duplicate elements and nodes
10694 res = doubleNodes( aMeshDS, theElems, theNodesNot, anOldNodeToNewNode, true );
10695 // replce nodes by duplications
10696 res = doubleNodes( aMeshDS, theAffectedElems, theNodesNot, anOldNodeToNewNode, false );
10700 //================================================================================
10702 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10703 \param theMeshDS - mesh instance
10704 \param theElems - the elements replicated or modified (nodes should be changed)
10705 \param theNodesNot - nodes to NOT replicate
10706 \param theNodeNodeMap - relation of old node to new created node
10707 \param theIsDoubleElem - flag os to replicate element or modify
10708 \return TRUE if operation has been completed successfully, FALSE otherwise
10710 //================================================================================
10712 bool SMESH_MeshEditor::doubleNodes(SMESHDS_Mesh* theMeshDS,
10713 const TIDSortedElemSet& theElems,
10714 const TIDSortedElemSet& theNodesNot,
10715 TNodeNodeMap& theNodeNodeMap,
10716 const bool theIsDoubleElem )
10718 // iterate through element and duplicate them (by nodes duplication)
10720 std::vector<const SMDS_MeshNode*> newNodes;
10721 ElemFeatures elemType;
10723 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10724 for ( ; elemItr != theElems.end(); ++elemItr )
10726 const SMDS_MeshElement* anElem = *elemItr;
10730 // duplicate nodes to duplicate element
10731 bool isDuplicate = false;
10732 newNodes.resize( anElem->NbNodes() );
10733 SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
10735 while ( anIter->more() )
10737 const SMDS_MeshNode* aCurrNode = static_cast<const SMDS_MeshNode*>( anIter->next() );
10738 const SMDS_MeshNode* aNewNode = aCurrNode;
10739 TNodeNodeMap::iterator n2n = theNodeNodeMap.find( aCurrNode );
10740 if ( n2n != theNodeNodeMap.end() )
10742 aNewNode = n2n->second;
10744 else if ( theIsDoubleElem && !theNodesNot.count( aCurrNode ))
10747 aNewNode = theMeshDS->AddNode( aCurrNode->X(), aCurrNode->Y(), aCurrNode->Z() );
10748 copyPosition( aCurrNode, aNewNode );
10749 theNodeNodeMap[ aCurrNode ] = aNewNode;
10750 myLastCreatedNodes.Append( aNewNode );
10752 isDuplicate |= (aCurrNode != aNewNode);
10753 newNodes[ ind++ ] = aNewNode;
10755 if ( !isDuplicate )
10758 if ( theIsDoubleElem )
10759 AddElement( newNodes, elemType.Init( anElem, /*basicOnly=*/false ));
10761 theMeshDS->ChangeElementNodes( anElem, &newNodes[ 0 ], newNodes.size() );
10768 //================================================================================
10770 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10771 \param theNodes - identifiers of nodes to be doubled
10772 \param theModifiedElems - identifiers of elements to be updated by the new (doubled)
10773 nodes. If list of element identifiers is empty then nodes are doubled but
10774 they not assigned to elements
10775 \return TRUE if operation has been completed successfully, FALSE otherwise
10777 //================================================================================
10779 bool SMESH_MeshEditor::DoubleNodes( const std::list< int >& theListOfNodes,
10780 const std::list< int >& theListOfModifiedElems )
10782 myLastCreatedElems.Clear();
10783 myLastCreatedNodes.Clear();
10785 if ( theListOfNodes.size() == 0 )
10788 SMESHDS_Mesh* aMeshDS = GetMeshDS();
10792 // iterate through nodes and duplicate them
10794 std::map< const SMDS_MeshNode*, const SMDS_MeshNode* > anOldNodeToNewNode;
10796 std::list< int >::const_iterator aNodeIter;
10797 for ( aNodeIter = theListOfNodes.begin(); aNodeIter != theListOfNodes.end(); ++aNodeIter )
10799 int aCurr = *aNodeIter;
10800 SMDS_MeshNode* aNode = (SMDS_MeshNode*)aMeshDS->FindNode( aCurr );
10806 const SMDS_MeshNode* aNewNode = aMeshDS->AddNode( aNode->X(), aNode->Y(), aNode->Z() );
10809 copyPosition( aNode, aNewNode );
10810 anOldNodeToNewNode[ aNode ] = aNewNode;
10811 myLastCreatedNodes.Append( aNewNode );
10815 // Create map of new nodes for modified elements
10817 std::map< SMDS_MeshElement*, vector<const SMDS_MeshNode*> > anElemToNodes;
10819 std::list< int >::const_iterator anElemIter;
10820 for ( anElemIter = theListOfModifiedElems.begin();
10821 anElemIter != theListOfModifiedElems.end(); ++anElemIter )
10823 int aCurr = *anElemIter;
10824 SMDS_MeshElement* anElem = (SMDS_MeshElement*)aMeshDS->FindElement( aCurr );
10828 vector<const SMDS_MeshNode*> aNodeArr( anElem->NbNodes() );
10830 SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
10832 while ( anIter->more() )
10834 SMDS_MeshNode* aCurrNode = (SMDS_MeshNode*)anIter->next();
10835 if ( aCurr && anOldNodeToNewNode.find( aCurrNode ) != anOldNodeToNewNode.end() )
10837 const SMDS_MeshNode* aNewNode = anOldNodeToNewNode[ aCurrNode ];
10838 aNodeArr[ ind++ ] = aNewNode;
10841 aNodeArr[ ind++ ] = aCurrNode;
10843 anElemToNodes[ anElem ] = aNodeArr;
10846 // Change nodes of elements
10848 std::map< SMDS_MeshElement*, vector<const SMDS_MeshNode*> >::iterator
10849 anElemToNodesIter = anElemToNodes.begin();
10850 for ( ; anElemToNodesIter != anElemToNodes.end(); ++anElemToNodesIter )
10852 const SMDS_MeshElement* anElem = anElemToNodesIter->first;
10853 vector<const SMDS_MeshNode*> aNodeArr = anElemToNodesIter->second;
10856 aMeshDS->ChangeElementNodes( anElem, &aNodeArr[ 0 ], anElem->NbNodes() );
10865 //================================================================================
10867 \brief Check if element located inside shape
10868 \return TRUE if IN or ON shape, FALSE otherwise
10870 //================================================================================
10872 template<class Classifier>
10873 bool isInside(const SMDS_MeshElement* theElem,
10874 Classifier& theClassifier,
10875 const double theTol)
10877 gp_XYZ centerXYZ (0, 0, 0);
10878 SMDS_ElemIteratorPtr aNodeItr = theElem->nodesIterator();
10879 while (aNodeItr->more())
10880 centerXYZ += SMESH_TNodeXYZ(cast2Node( aNodeItr->next()));
10882 gp_Pnt aPnt = centerXYZ / theElem->NbNodes();
10883 theClassifier.Perform(aPnt, theTol);
10884 TopAbs_State aState = theClassifier.State();
10885 return (aState == TopAbs_IN || aState == TopAbs_ON );
10888 //================================================================================
10890 * \brief Classifier of the 3D point on the TopoDS_Face
10891 * with interaface suitable for isInside()
10893 //================================================================================
10895 struct _FaceClassifier
10897 Extrema_ExtPS _extremum;
10898 BRepAdaptor_Surface _surface;
10899 TopAbs_State _state;
10901 _FaceClassifier(const TopoDS_Face& face):_extremum(),_surface(face),_state(TopAbs_OUT)
10903 _extremum.Initialize( _surface,
10904 _surface.FirstUParameter(), _surface.LastUParameter(),
10905 _surface.FirstVParameter(), _surface.LastVParameter(),
10906 _surface.Tolerance(), _surface.Tolerance() );
10908 void Perform(const gp_Pnt& aPnt, double theTol)
10911 _state = TopAbs_OUT;
10912 _extremum.Perform(aPnt);
10913 if ( _extremum.IsDone() )
10914 for ( int iSol = 1; iSol <= _extremum.NbExt() && _state == TopAbs_OUT; ++iSol)
10915 _state = ( _extremum.SquareDistance(iSol) <= theTol ? TopAbs_IN : TopAbs_OUT );
10917 TopAbs_State State() const
10924 //================================================================================
10926 \brief Identify the elements that will be affected by node duplication (actual duplication is not performed).
10927 This method is the first step of DoubleNodeElemGroupsInRegion.
10928 \param theElems - list of groups of elements (edges or faces) to be replicated
10929 \param theNodesNot - list of groups of nodes not to replicated
10930 \param theShape - shape to detect affected elements (element which geometric center
10931 located on or inside shape). If the shape is null, detection is done on faces orientations
10932 (select elements with a gravity center on the side given by faces normals).
10933 This mode (null shape) is faster, but works only when theElems are faces, with coherents orientations.
10934 The replicated nodes should be associated to affected elements.
10935 \return groups of affected elements
10936 \sa DoubleNodeElemGroupsInRegion()
10938 //================================================================================
10940 bool SMESH_MeshEditor::AffectedElemGroupsInRegion( const TIDSortedElemSet& theElems,
10941 const TIDSortedElemSet& theNodesNot,
10942 const TopoDS_Shape& theShape,
10943 TIDSortedElemSet& theAffectedElems)
10945 if ( theShape.IsNull() )
10947 std::set<const SMDS_MeshNode*> alreadyCheckedNodes;
10948 std::set<const SMDS_MeshElement*> alreadyCheckedElems;
10949 std::set<const SMDS_MeshElement*> edgesToCheck;
10950 alreadyCheckedNodes.clear();
10951 alreadyCheckedElems.clear();
10952 edgesToCheck.clear();
10954 // --- iterates on elements to be replicated and get elements by back references from their nodes
10956 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10957 for ( ; elemItr != theElems.end(); ++elemItr )
10959 SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
10960 if (!anElem || (anElem->GetType() != SMDSAbs_Face))
10963 SMESH_MeshAlgos::FaceNormal( anElem, normal, /*normalized=*/true );
10964 std::set<const SMDS_MeshNode*> nodesElem;
10966 SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
10967 while ( nodeItr->more() )
10969 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10970 nodesElem.insert(aNode);
10972 std::set<const SMDS_MeshNode*>::iterator nodit = nodesElem.begin();
10973 for (; nodit != nodesElem.end(); nodit++)
10975 const SMDS_MeshNode* aNode = *nodit;
10976 if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
10978 if (alreadyCheckedNodes.find(aNode) != alreadyCheckedNodes.end())
10980 alreadyCheckedNodes.insert(aNode);
10981 SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
10982 while ( backElemItr->more() )
10984 const SMDS_MeshElement* curElem = backElemItr->next();
10985 if (alreadyCheckedElems.find(curElem) != alreadyCheckedElems.end())
10987 if (theElems.find(curElem) != theElems.end())
10989 alreadyCheckedElems.insert(curElem);
10990 double x=0, y=0, z=0;
10992 SMDS_ElemIteratorPtr nodeItr2 = curElem->nodesIterator();
10993 while ( nodeItr2->more() )
10995 const SMDS_MeshNode* anotherNode = cast2Node(nodeItr2->next());
10996 x += anotherNode->X();
10997 y += anotherNode->Y();
10998 z += anotherNode->Z();
11002 p.SetCoord( x/nb -aNode->X(),
11004 z/nb -aNode->Z() );
11007 theAffectedElems.insert( curElem );
11009 else if (curElem->GetType() == SMDSAbs_Edge)
11010 edgesToCheck.insert(curElem);
11014 // --- add also edges lying on the set of faces (all nodes in alreadyCheckedNodes)
11015 std::set<const SMDS_MeshElement*>::iterator eit = edgesToCheck.begin();
11016 for( ; eit != edgesToCheck.end(); eit++)
11018 bool onside = true;
11019 const SMDS_MeshElement* anEdge = *eit;
11020 SMDS_ElemIteratorPtr nodeItr = anEdge->nodesIterator();
11021 while ( nodeItr->more() )
11023 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
11024 if (alreadyCheckedNodes.find(aNode) == alreadyCheckedNodes.end())
11032 theAffectedElems.insert(anEdge);
11038 const double aTol = Precision::Confusion();
11039 auto_ptr< BRepClass3d_SolidClassifier> bsc3d;
11040 auto_ptr<_FaceClassifier> aFaceClassifier;
11041 if ( theShape.ShapeType() == TopAbs_SOLID )
11043 bsc3d.reset( new BRepClass3d_SolidClassifier(theShape));;
11044 bsc3d->PerformInfinitePoint(aTol);
11046 else if (theShape.ShapeType() == TopAbs_FACE )
11048 aFaceClassifier.reset( new _FaceClassifier(TopoDS::Face(theShape)));
11051 // iterates on indicated elements and get elements by back references from their nodes
11052 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
11053 for ( ; elemItr != theElems.end(); ++elemItr )
11055 SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
11058 SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
11059 while ( nodeItr->more() )
11061 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
11062 if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
11064 SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
11065 while ( backElemItr->more() )
11067 const SMDS_MeshElement* curElem = backElemItr->next();
11068 if ( curElem && theElems.find(curElem) == theElems.end() &&
11070 isInside( curElem, *bsc3d, aTol ) :
11071 isInside( curElem, *aFaceClassifier, aTol )))
11072 theAffectedElems.insert( curElem );
11080 //================================================================================
11082 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
11083 \param theElems - group of of elements (edges or faces) to be replicated
11084 \param theNodesNot - group of nodes not to replicate
11085 \param theShape - shape to detect affected elements (element which geometric center
11086 located on or inside shape).
11087 The replicated nodes should be associated to affected elements.
11088 \return TRUE if operation has been completed successfully, FALSE otherwise
11090 //================================================================================
11092 bool SMESH_MeshEditor::DoubleNodesInRegion( const TIDSortedElemSet& theElems,
11093 const TIDSortedElemSet& theNodesNot,
11094 const TopoDS_Shape& theShape )
11096 if ( theShape.IsNull() )
11099 const double aTol = Precision::Confusion();
11100 SMESHUtils::Deleter< BRepClass3d_SolidClassifier> bsc3d;
11101 SMESHUtils::Deleter<_FaceClassifier> aFaceClassifier;
11102 if ( theShape.ShapeType() == TopAbs_SOLID )
11104 bsc3d._obj = new BRepClass3d_SolidClassifier( theShape );
11105 bsc3d->PerformInfinitePoint(aTol);
11107 else if (theShape.ShapeType() == TopAbs_FACE )
11109 aFaceClassifier._obj = new _FaceClassifier( TopoDS::Face( theShape ));
11112 // iterates on indicated elements and get elements by back references from their nodes
11113 TIDSortedElemSet anAffected;
11114 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
11115 for ( ; elemItr != theElems.end(); ++elemItr )
11117 SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
11121 SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
11122 while ( nodeItr->more() )
11124 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
11125 if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
11127 SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
11128 while ( backElemItr->more() )
11130 const SMDS_MeshElement* curElem = backElemItr->next();
11131 if ( curElem && theElems.find(curElem) == theElems.end() &&
11133 isInside( curElem, *bsc3d, aTol ) :
11134 isInside( curElem, *aFaceClassifier, aTol )))
11135 anAffected.insert( curElem );
11139 return DoubleNodes( theElems, theNodesNot, anAffected );
11143 * \brief compute an oriented angle between two planes defined by four points.
11144 * The vector (p0,p1) defines the intersection of the 2 planes (p0,p1,g1) and (p0,p1,g2)
11145 * @param p0 base of the rotation axe
11146 * @param p1 extremity of the rotation axe
11147 * @param g1 belongs to the first plane
11148 * @param g2 belongs to the second plane
11150 double SMESH_MeshEditor::OrientedAngle(const gp_Pnt& p0, const gp_Pnt& p1, const gp_Pnt& g1, const gp_Pnt& g2)
11152 gp_Vec vref(p0, p1);
11155 gp_Vec n1 = vref.Crossed(v1);
11156 gp_Vec n2 = vref.Crossed(v2);
11158 return n2.AngleWithRef(n1, vref);
11160 catch ( Standard_Failure ) {
11162 return Max( v1.Magnitude(), v2.Magnitude() );
11166 * \brief Double nodes on shared faces between groups of volumes and create flat elements on demand.
11167 * The list of groups must contain at least two groups. The groups have to be disjoint: no common element into two different groups.
11168 * The nodes of the internal faces at the boundaries of the groups are doubled. Optionally, the internal faces are replaced by flat elements.
11169 * Triangles are transformed into prisms, and quadrangles into hexahedrons.
11170 * The flat elements are stored in groups of volumes. These groups are named according to the position of the group in the list:
11171 * 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.
11172 * 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.
11173 * All the flat elements are gathered into the group named "joints3D" (or "joints2D" in 2D situation).
11174 * The flat element of the multiple junctions between the simple junction are stored in a group named "jointsMultiples".
11175 * \param theElems - list of groups of volumes, where a group of volume is a set of
11176 * SMDS_MeshElements sorted by Id.
11177 * \param createJointElems - if TRUE, create the elements
11178 * \param onAllBoundaries - if TRUE, the nodes and elements are also created on
11179 * the boundary between \a theDomains and the rest mesh
11180 * \return TRUE if operation has been completed successfully, FALSE otherwise
11182 bool SMESH_MeshEditor::DoubleNodesOnGroupBoundaries( const std::vector<TIDSortedElemSet>& theElems,
11183 bool createJointElems,
11184 bool onAllBoundaries)
11186 // MESSAGE("----------------------------------------------");
11187 // MESSAGE("SMESH_MeshEditor::doubleNodesOnGroupBoundaries");
11188 // MESSAGE("----------------------------------------------");
11190 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11191 meshDS->BuildDownWardConnectivity(true);
11193 SMDS_UnstructuredGrid *grid = meshDS->getGrid();
11195 // --- build the list of faces shared by 2 domains (group of elements), with their domain and volume indexes
11196 // build the list of cells with only a node or an edge on the border, with their domain and volume indexes
11197 // build the list of nodes shared by 2 or more domains, with their domain indexes
11199 std::map<DownIdType, std::map<int,int>, DownIdCompare> faceDomains; // face --> (id domain --> id volume)
11200 std::map<int,int>celldom; // cell vtkId --> domain
11201 std::map<DownIdType, std::map<int,int>, DownIdCompare> cellDomains; // oldNode --> (id domain --> id cell)
11202 std::map<int, std::map<int,int> > nodeDomains; // oldId --> (domainId --> newId)
11203 faceDomains.clear();
11205 cellDomains.clear();
11206 nodeDomains.clear();
11207 std::map<int,int> emptyMap;
11208 std::set<int> emptySet;
11211 //MESSAGE(".. Number of domains :"<<theElems.size());
11213 TIDSortedElemSet theRestDomElems;
11214 const int iRestDom = -1;
11215 const int idom0 = onAllBoundaries ? iRestDom : 0;
11216 const int nbDomains = theElems.size();
11218 // Check if the domains do not share an element
11219 for (int idom = 0; idom < nbDomains-1; idom++)
11221 // MESSAGE("... Check of domain #" << idom);
11222 const TIDSortedElemSet& domain = theElems[idom];
11223 TIDSortedElemSet::const_iterator elemItr = domain.begin();
11224 for (; elemItr != domain.end(); ++elemItr)
11226 const SMDS_MeshElement* anElem = *elemItr;
11227 int idombisdeb = idom + 1 ;
11228 // check if the element belongs to a domain further in the list
11229 for ( size_t idombis = idombisdeb; idombis < theElems.size(); idombis++ )
11231 const TIDSortedElemSet& domainbis = theElems[idombis];
11232 if ( domainbis.count( anElem ))
11234 MESSAGE(".... Domain #" << idom);
11235 MESSAGE(".... Domain #" << idombis);
11236 throw SALOME_Exception("The domains are not disjoint.");
11243 for (int idom = 0; idom < nbDomains; idom++)
11246 // --- build a map (face to duplicate --> volume to modify)
11247 // with all the faces shared by 2 domains (group of elements)
11248 // and corresponding volume of this domain, for each shared face.
11249 // a volume has a face shared by 2 domains if it has a neighbor which is not in his domain.
11251 //MESSAGE("... Neighbors of domain #" << idom);
11252 const TIDSortedElemSet& domain = theElems[idom];
11253 TIDSortedElemSet::const_iterator elemItr = domain.begin();
11254 for (; elemItr != domain.end(); ++elemItr)
11256 const SMDS_MeshElement* anElem = *elemItr;
11259 int vtkId = anElem->getVtkId();
11260 //MESSAGE(" vtkId " << vtkId << " smdsId " << anElem->GetID());
11261 int neighborsVtkIds[NBMAXNEIGHBORS];
11262 int downIds[NBMAXNEIGHBORS];
11263 unsigned char downTypes[NBMAXNEIGHBORS];
11264 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
11265 for (int n = 0; n < nbNeighbors; n++)
11267 int smdsId = meshDS->fromVtkToSmds(neighborsVtkIds[n]);
11268 const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
11269 if (elem && ! domain.count(elem)) // neighbor is in another domain : face is shared
11272 for ( size_t idombis = 0; idombis < theElems.size() && !ok; idombis++) // check if the neighbor belongs to another domain of the list
11274 // MESSAGE("Domain " << idombis);
11275 const TIDSortedElemSet& domainbis = theElems[idombis];
11276 if ( domainbis.count(elem)) ok = true ; // neighbor is in a correct domain : face is kept
11278 if ( ok || onAllBoundaries ) // the characteristics of the face is stored
11280 DownIdType face(downIds[n], downTypes[n]);
11281 if (!faceDomains[face].count(idom))
11283 faceDomains[face][idom] = vtkId; // volume associated to face in this domain
11284 celldom[vtkId] = idom;
11285 //MESSAGE(" cell with a border " << vtkId << " domain " << idom);
11289 theRestDomElems.insert( elem );
11290 faceDomains[face][iRestDom] = neighborsVtkIds[n];
11291 celldom[neighborsVtkIds[n]] = iRestDom;
11299 //MESSAGE("Number of shared faces " << faceDomains.size());
11300 std::map<DownIdType, std::map<int, int>, DownIdCompare>::iterator itface;
11302 // --- explore the shared faces domain by domain,
11303 // explore the nodes of the face and see if they belong to a cell in the domain,
11304 // which has only a node or an edge on the border (not a shared face)
11306 for (int idomain = idom0; idomain < nbDomains; idomain++)
11308 //MESSAGE("Domain " << idomain);
11309 const TIDSortedElemSet& domain = (idomain == iRestDom) ? theRestDomElems : theElems[idomain];
11310 itface = faceDomains.begin();
11311 for (; itface != faceDomains.end(); ++itface)
11313 const std::map<int, int>& domvol = itface->second;
11314 if (!domvol.count(idomain))
11316 DownIdType face = itface->first;
11317 //MESSAGE(" --- face " << face.cellId);
11318 std::set<int> oldNodes;
11320 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11321 std::set<int>::iterator itn = oldNodes.begin();
11322 for (; itn != oldNodes.end(); ++itn)
11325 //MESSAGE(" node " << oldId);
11326 vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
11327 for (int i=0; i<l.ncells; i++)
11329 int vtkId = l.cells[i];
11330 const SMDS_MeshElement* anElem = GetMeshDS()->FindElement(GetMeshDS()->fromVtkToSmds(vtkId));
11331 if (!domain.count(anElem))
11333 int vtkType = grid->GetCellType(vtkId);
11334 int downId = grid->CellIdToDownId(vtkId);
11337 MESSAGE("doubleNodesOnGroupBoundaries: internal algorithm problem");
11338 continue; // not OK at this stage of the algorithm:
11339 //no cells created after BuildDownWardConnectivity
11341 DownIdType aCell(downId, vtkType);
11342 cellDomains[aCell][idomain] = vtkId;
11343 celldom[vtkId] = idomain;
11344 //MESSAGE(" cell " << vtkId << " domain " << idomain);
11350 // --- explore the shared faces domain by domain, to duplicate the nodes in a coherent way
11351 // for each shared face, get the nodes
11352 // for each node, for each domain of the face, create a clone of the node
11354 // --- edges at the intersection of 3 or 4 domains, with the order of domains to build
11355 // junction elements of type prism or hexa. the key is the pair of nodesId (lower first)
11356 // the value is the ordered domain ids. (more than 4 domains not taken into account)
11358 std::map<std::vector<int>, std::vector<int> > edgesMultiDomains; // nodes of edge --> ordered domains
11359 std::map<int, std::vector<int> > mutipleNodes; // nodes multi domains with domain order
11360 std::map<int, std::vector<int> > mutipleNodesToFace; // nodes multi domains with domain order to transform in Face (junction between 3 or more 2D domains)
11362 //MESSAGE(".. Duplication of the nodes");
11363 for (int idomain = idom0; idomain < nbDomains; idomain++)
11365 itface = faceDomains.begin();
11366 for (; itface != faceDomains.end(); ++itface)
11368 const std::map<int, int>& domvol = itface->second;
11369 if (!domvol.count(idomain))
11371 DownIdType face = itface->first;
11372 //MESSAGE(" --- face " << face.cellId);
11373 std::set<int> oldNodes;
11375 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11376 std::set<int>::iterator itn = oldNodes.begin();
11377 for (; itn != oldNodes.end(); ++itn)
11380 if (nodeDomains[oldId].empty())
11382 nodeDomains[oldId][idomain] = oldId; // keep the old node in the first domain
11383 //MESSAGE("-+-+-b oldNode " << oldId << " domain " << idomain);
11385 std::map<int, int>::const_iterator itdom = domvol.begin();
11386 for (; itdom != domvol.end(); ++itdom)
11388 int idom = itdom->first;
11389 //MESSAGE(" domain " << idom);
11390 if (!nodeDomains[oldId].count(idom)) // --- node to clone
11392 if (nodeDomains[oldId].size() >= 2) // a multiple node
11394 vector<int> orderedDoms;
11395 //MESSAGE("multiple node " << oldId);
11396 if (mutipleNodes.count(oldId))
11397 orderedDoms = mutipleNodes[oldId];
11400 map<int,int>::iterator it = nodeDomains[oldId].begin();
11401 for (; it != nodeDomains[oldId].end(); ++it)
11402 orderedDoms.push_back(it->first);
11404 orderedDoms.push_back(idom); // TODO order ==> push_front or back
11405 //stringstream txt;
11406 //for (int i=0; i<orderedDoms.size(); i++)
11407 // txt << orderedDoms[i] << " ";
11408 //MESSAGE("orderedDoms " << txt.str());
11409 mutipleNodes[oldId] = orderedDoms;
11411 double *coords = grid->GetPoint(oldId);
11412 SMDS_MeshNode *newNode = meshDS->AddNode(coords[0], coords[1], coords[2]);
11413 copyPosition( meshDS->FindNodeVtk( oldId ), newNode );
11414 int newId = newNode->getVtkId();
11415 nodeDomains[oldId][idom] = newId; // cloned node for other domains
11416 //MESSAGE("-+-+-c oldNode " << oldId << " domain " << idomain << " newNode " << newId << " domain " << idom << " size=" <<nodeDomains[oldId].size());
11423 //MESSAGE(".. Creation of elements");
11424 for (int idomain = idom0; idomain < nbDomains; idomain++)
11426 itface = faceDomains.begin();
11427 for (; itface != faceDomains.end(); ++itface)
11429 std::map<int, int> domvol = itface->second;
11430 if (!domvol.count(idomain))
11432 DownIdType face = itface->first;
11433 //MESSAGE(" --- face " << face.cellId);
11434 std::set<int> oldNodes;
11436 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11437 int nbMultipleNodes = 0;
11438 std::set<int>::iterator itn = oldNodes.begin();
11439 for (; itn != oldNodes.end(); ++itn)
11442 if (mutipleNodes.count(oldId))
11445 if (nbMultipleNodes > 1) // check if an edge of the face is shared between 3 or more domains
11447 //MESSAGE("multiple Nodes detected on a shared face");
11448 int downId = itface->first.cellId;
11449 unsigned char cellType = itface->first.cellType;
11450 // --- shared edge or shared face ?
11451 if ((cellType == VTK_LINE) || (cellType == VTK_QUADRATIC_EDGE)) // shared edge (between two faces)
11454 int nbNodes = grid->getDownArray(cellType)->getNodes(downId, nodes);
11455 for (int i=0; i< nbNodes; i=i+nbNodes-1) // i=0 , i=nbNodes-1
11456 if (mutipleNodes.count(nodes[i]))
11457 if (!mutipleNodesToFace.count(nodes[i]))
11458 mutipleNodesToFace[nodes[i]] = mutipleNodes[nodes[i]];
11460 else // shared face (between two volumes)
11462 int nbEdges = grid->getDownArray(cellType)->getNumberOfDownCells(downId);
11463 const int* downEdgeIds = grid->getDownArray(cellType)->getDownCells(downId);
11464 const unsigned char* edgeType = grid->getDownArray(cellType)->getDownTypes(downId);
11465 for (int ie =0; ie < nbEdges; ie++)
11468 int nbNodes = grid->getDownArray(edgeType[ie])->getNodes(downEdgeIds[ie], nodes);
11469 if ( mutipleNodes.count(nodes[0]) && mutipleNodes.count( nodes[ nbNodes-1 ]))
11471 vector<int> vn0 = mutipleNodes[nodes[0]];
11472 vector<int> vn1 = mutipleNodes[nodes[nbNodes - 1]];
11474 for ( size_t i0 = 0; i0 < vn0.size(); i0++ )
11475 for ( size_t i1 = 0; i1 < vn1.size(); i1++ )
11476 if ( vn0[i0] == vn1[i1] )
11477 doms.push_back( vn0[ i0 ]);
11478 if ( doms.size() > 2 )
11480 //MESSAGE(" detect edgesMultiDomains " << nodes[0] << " " << nodes[nbNodes - 1]);
11481 double *coords = grid->GetPoint(nodes[0]);
11482 gp_Pnt p0(coords[0], coords[1], coords[2]);
11483 coords = grid->GetPoint(nodes[nbNodes - 1]);
11484 gp_Pnt p1(coords[0], coords[1], coords[2]);
11486 int vtkVolIds[1000]; // an edge can belong to a lot of volumes
11487 map<int, SMDS_VtkVolume*> domvol; // domain --> a volume with the edge
11488 map<int, double> angleDom; // oriented angles between planes defined by edge and volume centers
11489 int nbvol = grid->GetParentVolumes(vtkVolIds, downEdgeIds[ie], edgeType[ie]);
11490 for ( size_t id = 0; id < doms.size(); id++ )
11492 int idom = doms[id];
11493 const TIDSortedElemSet& domain = (idom == iRestDom) ? theRestDomElems : theElems[idom];
11494 for ( int ivol = 0; ivol < nbvol; ivol++ )
11496 int smdsId = meshDS->fromVtkToSmds(vtkVolIds[ivol]);
11497 SMDS_MeshElement* elem = (SMDS_MeshElement*)meshDS->FindElement(smdsId);
11498 if (domain.count(elem))
11500 SMDS_VtkVolume* svol = dynamic_cast<SMDS_VtkVolume*>(elem);
11501 domvol[idom] = svol;
11502 //MESSAGE(" domain " << idom << " volume " << elem->GetID());
11504 vtkIdType npts = 0;
11505 vtkIdType* pts = 0;
11506 grid->GetCellPoints(vtkVolIds[ivol], npts, pts);
11507 SMDS_VtkVolume::gravityCenter(grid, pts, npts, values);
11510 gref.SetXYZ(gp_XYZ(values[0], values[1], values[2]));
11511 angleDom[idom] = 0;
11515 gp_Pnt g(values[0], values[1], values[2]);
11516 angleDom[idom] = OrientedAngle(p0, p1, gref, g); // -pi<angle<+pi
11517 //MESSAGE(" angle=" << angleDom[idom]);
11523 map<double, int> sortedDom; // sort domains by angle
11524 for (map<int, double>::iterator ia = angleDom.begin(); ia != angleDom.end(); ++ia)
11525 sortedDom[ia->second] = ia->first;
11526 vector<int> vnodes;
11528 for (map<double, int>::iterator ib = sortedDom.begin(); ib != sortedDom.end(); ++ib)
11530 vdom.push_back(ib->second);
11531 //MESSAGE(" ordered domain " << ib->second << " angle " << ib->first);
11533 for (int ino = 0; ino < nbNodes; ino++)
11534 vnodes.push_back(nodes[ino]);
11535 edgesMultiDomains[vnodes] = vdom; // nodes vector --> ordered domains
11544 // --- iterate on shared faces (volumes to modify, face to extrude)
11545 // get node id's of the face (id SMDS = id VTK)
11546 // create flat element with old and new nodes if requested
11548 // --- new quad nodes on flat quad elements: oldId --> ((domain1 X domain2) --> newId)
11549 // (domain1 X domain2) = domain1 + MAXINT*domain2
11551 std::map<int, std::map<long,int> > nodeQuadDomains;
11552 std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11554 //MESSAGE(".. Creation of elements: simple junction");
11555 if (createJointElems)
11558 string joints2DName = "joints2D";
11559 mapOfJunctionGroups[joints2DName] = this->myMesh->AddGroup(SMDSAbs_Face, joints2DName.c_str(), idg);
11560 SMESHDS_Group *joints2DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints2DName]->GetGroupDS());
11561 string joints3DName = "joints3D";
11562 mapOfJunctionGroups[joints3DName] = this->myMesh->AddGroup(SMDSAbs_Volume, joints3DName.c_str(), idg);
11563 SMESHDS_Group *joints3DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints3DName]->GetGroupDS());
11565 itface = faceDomains.begin();
11566 for (; itface != faceDomains.end(); ++itface)
11568 DownIdType face = itface->first;
11569 std::set<int> oldNodes;
11570 std::set<int>::iterator itn;
11572 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11574 std::map<int, int> domvol = itface->second;
11575 std::map<int, int>::iterator itdom = domvol.begin();
11576 int dom1 = itdom->first;
11577 int vtkVolId = itdom->second;
11579 int dom2 = itdom->first;
11580 SMDS_MeshCell *vol = grid->extrudeVolumeFromFace(vtkVolId, dom1, dom2, oldNodes, nodeDomains,
11582 stringstream grpname;
11585 grpname << dom1 << "_" << dom2;
11587 grpname << dom2 << "_" << dom1;
11588 string namegrp = grpname.str();
11589 if (!mapOfJunctionGroups.count(namegrp))
11590 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(vol->GetType(), namegrp.c_str(), idg);
11591 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11593 sgrp->Add(vol->GetID());
11594 if (vol->GetType() == SMDSAbs_Volume)
11595 joints3DGrp->Add(vol->GetID());
11596 else if (vol->GetType() == SMDSAbs_Face)
11597 joints2DGrp->Add(vol->GetID());
11601 // --- create volumes on multiple domain intersection if requested
11602 // iterate on mutipleNodesToFace
11603 // iterate on edgesMultiDomains
11605 //MESSAGE(".. Creation of elements: multiple junction");
11606 if (createJointElems)
11608 // --- iterate on mutipleNodesToFace
11610 std::map<int, std::vector<int> >::iterator itn = mutipleNodesToFace.begin();
11611 for (; itn != mutipleNodesToFace.end(); ++itn)
11613 int node = itn->first;
11614 vector<int> orderDom = itn->second;
11615 vector<vtkIdType> orderedNodes;
11616 for ( size_t idom = 0; idom < orderDom.size(); idom++ )
11617 orderedNodes.push_back( nodeDomains[ node ][ orderDom[ idom ]]);
11618 SMDS_MeshFace* face = this->GetMeshDS()->AddFaceFromVtkIds(orderedNodes);
11620 stringstream grpname;
11622 grpname << 0 << "_" << 0;
11624 string namegrp = grpname.str();
11625 if (!mapOfJunctionGroups.count(namegrp))
11626 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Face, namegrp.c_str(), idg);
11627 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11629 sgrp->Add(face->GetID());
11632 // --- iterate on edgesMultiDomains
11634 std::map<std::vector<int>, std::vector<int> >::iterator ite = edgesMultiDomains.begin();
11635 for (; ite != edgesMultiDomains.end(); ++ite)
11637 vector<int> nodes = ite->first;
11638 vector<int> orderDom = ite->second;
11639 vector<vtkIdType> orderedNodes;
11640 if (nodes.size() == 2)
11642 //MESSAGE(" use edgesMultiDomains " << nodes[0] << " " << nodes[1]);
11643 for ( size_t ino = 0; ino < nodes.size(); ino++ )
11644 if ( orderDom.size() == 3 )
11645 for ( size_t idom = 0; idom < orderDom.size(); idom++ )
11646 orderedNodes.push_back( nodeDomains[ nodes[ ino ]][ orderDom[ idom ]]);
11648 for (int idom = orderDom.size()-1; idom >=0; idom--)
11649 orderedNodes.push_back( nodeDomains[ nodes[ ino ]][ orderDom[ idom ]]);
11650 SMDS_MeshVolume* vol = this->GetMeshDS()->AddVolumeFromVtkIds(orderedNodes);
11653 string namegrp = "jointsMultiples";
11654 if (!mapOfJunctionGroups.count(namegrp))
11655 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
11656 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11658 sgrp->Add(vol->GetID());
11662 //INFOS("Quadratic multiple joints not implemented");
11663 // TODO quadratic nodes
11668 // --- list the explicit faces and edges of the mesh that need to be modified,
11669 // i.e. faces and edges built with one or more duplicated nodes.
11670 // associate these faces or edges to their corresponding domain.
11671 // only the first domain found is kept when a face or edge is shared
11673 std::map<DownIdType, std::map<int,int>, DownIdCompare> faceOrEdgeDom; // cellToModify --> (id domain --> id cell)
11674 std::map<int,int> feDom; // vtk id of cell to modify --> id domain
11675 faceOrEdgeDom.clear();
11678 //MESSAGE(".. Modification of elements");
11679 for (int idomain = idom0; idomain < nbDomains; idomain++)
11681 std::map<int, std::map<int, int> >::const_iterator itnod = nodeDomains.begin();
11682 for (; itnod != nodeDomains.end(); ++itnod)
11684 int oldId = itnod->first;
11685 //MESSAGE(" node " << oldId);
11686 vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
11687 for (int i = 0; i < l.ncells; i++)
11689 int vtkId = l.cells[i];
11690 int vtkType = grid->GetCellType(vtkId);
11691 int downId = grid->CellIdToDownId(vtkId);
11693 continue; // new cells: not to be modified
11694 DownIdType aCell(downId, vtkType);
11695 int volParents[1000];
11696 int nbvol = grid->GetParentVolumes(volParents, vtkId);
11697 for (int j = 0; j < nbvol; j++)
11698 if (celldom.count(volParents[j]) && (celldom[volParents[j]] == idomain))
11699 if (!feDom.count(vtkId))
11701 feDom[vtkId] = idomain;
11702 faceOrEdgeDom[aCell] = emptyMap;
11703 faceOrEdgeDom[aCell][idomain] = vtkId; // affect face or edge to the first domain only
11704 //MESSAGE("affect cell " << this->GetMeshDS()->fromVtkToSmds(vtkId) << " domain " << idomain
11705 // << " type " << vtkType << " downId " << downId);
11711 // --- iterate on shared faces (volumes to modify, face to extrude)
11712 // get node id's of the face
11713 // replace old nodes by new nodes in volumes, and update inverse connectivity
11715 std::map<DownIdType, std::map<int,int>, DownIdCompare>* maps[3] = {&faceDomains, &cellDomains, &faceOrEdgeDom};
11716 for (int m=0; m<3; m++)
11718 std::map<DownIdType, std::map<int,int>, DownIdCompare>* amap = maps[m];
11719 itface = (*amap).begin();
11720 for (; itface != (*amap).end(); ++itface)
11722 DownIdType face = itface->first;
11723 std::set<int> oldNodes;
11724 std::set<int>::iterator itn;
11726 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11727 //MESSAGE("examine cell, downId " << face.cellId << " type " << int(face.cellType));
11728 std::map<int, int> localClonedNodeIds;
11730 std::map<int, int> domvol = itface->second;
11731 std::map<int, int>::iterator itdom = domvol.begin();
11732 for (; itdom != domvol.end(); ++itdom)
11734 int idom = itdom->first;
11735 int vtkVolId = itdom->second;
11736 //MESSAGE("modify nodes of cell " << this->GetMeshDS()->fromVtkToSmds(vtkVolId) << " domain " << idom);
11737 localClonedNodeIds.clear();
11738 for (itn = oldNodes.begin(); itn != oldNodes.end(); ++itn)
11741 if (nodeDomains[oldId].count(idom))
11743 localClonedNodeIds[oldId] = nodeDomains[oldId][idom];
11744 //MESSAGE(" node " << oldId << " --> " << localClonedNodeIds[oldId]);
11747 meshDS->ModifyCellNodes(vtkVolId, localClonedNodeIds);
11752 // Remove empty groups (issue 0022812)
11753 std::map<std::string, SMESH_Group*>::iterator name_group = mapOfJunctionGroups.begin();
11754 for ( ; name_group != mapOfJunctionGroups.end(); ++name_group )
11756 if ( name_group->second && name_group->second->GetGroupDS()->IsEmpty() )
11757 myMesh->RemoveGroup( name_group->second->GetGroupDS()->GetID() );
11760 meshDS->CleanDownWardConnectivity(); // Mesh has been modified, downward connectivity is no more usable, free memory
11761 grid->DeleteLinks();
11769 * \brief Double nodes on some external faces and create flat elements.
11770 * Flat elements are mainly used by some types of mechanic calculations.
11772 * Each group of the list must be constituted of faces.
11773 * Triangles are transformed in prisms, and quadrangles in hexahedrons.
11774 * @param theElems - list of groups of faces, where a group of faces is a set of
11775 * SMDS_MeshElements sorted by Id.
11776 * @return TRUE if operation has been completed successfully, FALSE otherwise
11778 bool SMESH_MeshEditor::CreateFlatElementsOnFacesGroups(const std::vector<TIDSortedElemSet>& theElems)
11780 // MESSAGE("-------------------------------------------------");
11781 // MESSAGE("SMESH_MeshEditor::CreateFlatElementsOnFacesGroups");
11782 // MESSAGE("-------------------------------------------------");
11784 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11786 // --- For each group of faces
11787 // duplicate the nodes, create a flat element based on the face
11788 // replace the nodes of the faces by their clones
11790 std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> clonedNodes;
11791 std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> intermediateNodes;
11792 clonedNodes.clear();
11793 intermediateNodes.clear();
11794 std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11795 mapOfJunctionGroups.clear();
11797 for ( size_t idom = 0; idom < theElems.size(); idom++ )
11799 const TIDSortedElemSet& domain = theElems[idom];
11800 TIDSortedElemSet::const_iterator elemItr = domain.begin();
11801 for ( ; elemItr != domain.end(); ++elemItr )
11803 SMDS_MeshElement* anElem = (SMDS_MeshElement*) *elemItr;
11804 SMDS_MeshFace* aFace = dynamic_cast<SMDS_MeshFace*> (anElem);
11807 // MESSAGE("aFace=" << aFace->GetID());
11808 bool isQuad = aFace->IsQuadratic();
11809 vector<const SMDS_MeshNode*> ln0, ln1, ln2, ln3, ln4;
11811 // --- clone the nodes, create intermediate nodes for non medium nodes of a quad face
11813 SMDS_ElemIteratorPtr nodeIt = aFace->nodesIterator();
11814 while (nodeIt->more())
11816 const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*> (nodeIt->next());
11817 bool isMedium = isQuad && (aFace->IsMediumNode(node));
11819 ln2.push_back(node);
11821 ln0.push_back(node);
11823 const SMDS_MeshNode* clone = 0;
11824 if (!clonedNodes.count(node))
11826 clone = meshDS->AddNode(node->X(), node->Y(), node->Z());
11827 copyPosition( node, clone );
11828 clonedNodes[node] = clone;
11831 clone = clonedNodes[node];
11834 ln3.push_back(clone);
11836 ln1.push_back(clone);
11838 const SMDS_MeshNode* inter = 0;
11839 if (isQuad && (!isMedium))
11841 if (!intermediateNodes.count(node))
11843 inter = meshDS->AddNode(node->X(), node->Y(), node->Z());
11844 copyPosition( node, inter );
11845 intermediateNodes[node] = inter;
11848 inter = intermediateNodes[node];
11849 ln4.push_back(inter);
11853 // --- extrude the face
11855 vector<const SMDS_MeshNode*> ln;
11856 SMDS_MeshVolume* vol = 0;
11857 vtkIdType aType = aFace->GetVtkType();
11861 vol = meshDS->AddVolume(ln0[2], ln0[1], ln0[0], ln1[2], ln1[1], ln1[0]);
11862 // MESSAGE("vol prism " << vol->GetID());
11863 ln.push_back(ln1[0]);
11864 ln.push_back(ln1[1]);
11865 ln.push_back(ln1[2]);
11868 vol = meshDS->AddVolume(ln0[3], ln0[2], ln0[1], ln0[0], ln1[3], ln1[2], ln1[1], ln1[0]);
11869 // MESSAGE("vol hexa " << vol->GetID());
11870 ln.push_back(ln1[0]);
11871 ln.push_back(ln1[1]);
11872 ln.push_back(ln1[2]);
11873 ln.push_back(ln1[3]);
11875 case VTK_QUADRATIC_TRIANGLE:
11876 vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln0[0], ln0[1], ln0[2], ln3[0], ln3[1], ln3[2],
11877 ln2[0], ln2[1], ln2[2], ln4[0], ln4[1], ln4[2]);
11878 // MESSAGE("vol quad prism " << vol->GetID());
11879 ln.push_back(ln1[0]);
11880 ln.push_back(ln1[1]);
11881 ln.push_back(ln1[2]);
11882 ln.push_back(ln3[0]);
11883 ln.push_back(ln3[1]);
11884 ln.push_back(ln3[2]);
11886 case VTK_QUADRATIC_QUAD:
11887 // vol = meshDS->AddVolume(ln0[0], ln0[1], ln0[2], ln0[3], ln1[0], ln1[1], ln1[2], ln1[3],
11888 // ln2[0], ln2[1], ln2[2], ln2[3], ln3[0], ln3[1], ln3[2], ln3[3],
11889 // ln4[0], ln4[1], ln4[2], ln4[3]);
11890 vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln1[3], ln0[0], ln0[1], ln0[2], ln0[3],
11891 ln3[0], ln3[1], ln3[2], ln3[3], ln2[0], ln2[1], ln2[2], ln2[3],
11892 ln4[0], ln4[1], ln4[2], ln4[3]);
11893 // MESSAGE("vol quad hexa " << vol->GetID());
11894 ln.push_back(ln1[0]);
11895 ln.push_back(ln1[1]);
11896 ln.push_back(ln1[2]);
11897 ln.push_back(ln1[3]);
11898 ln.push_back(ln3[0]);
11899 ln.push_back(ln3[1]);
11900 ln.push_back(ln3[2]);
11901 ln.push_back(ln3[3]);
11911 stringstream grpname;
11915 string namegrp = grpname.str();
11916 if (!mapOfJunctionGroups.count(namegrp))
11917 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
11918 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11920 sgrp->Add(vol->GetID());
11923 // --- modify the face
11925 aFace->ChangeNodes(&ln[0], ln.size());
11932 * \brief identify all the elements around a geom shape, get the faces delimiting the hole
11933 * Build groups of volume to remove, groups of faces to replace on the skin of the object,
11934 * groups of faces to remove inside the object, (idem edges).
11935 * Build ordered list of nodes at the border of each group of faces to replace (to be used to build a geom subshape)
11937 void SMESH_MeshEditor::CreateHoleSkin(double radius,
11938 const TopoDS_Shape& theShape,
11939 SMESH_NodeSearcher* theNodeSearcher,
11940 const char* groupName,
11941 std::vector<double>& nodesCoords,
11942 std::vector<std::vector<int> >& listOfListOfNodes)
11944 // MESSAGE("--------------------------------");
11945 // MESSAGE("SMESH_MeshEditor::CreateHoleSkin");
11946 // MESSAGE("--------------------------------");
11948 // --- zone of volumes to remove is given :
11949 // 1 either by a geom shape (one or more vertices) and a radius,
11950 // 2 either by a group of nodes (representative of the shape)to use with the radius,
11951 // 3 either by a group of nodes where all the elements build on one of this nodes are to remove,
11952 // In the case 2, the group of nodes is an external group of nodes from another mesh,
11953 // In the case 3, the group of nodes is an internal group of the mesh (obtained for instance by a filter),
11954 // defined by it's name.
11956 SMESHDS_GroupBase* groupDS = 0;
11957 SMESH_Mesh::GroupIteratorPtr groupIt = this->myMesh->GetGroups();
11958 while ( groupIt->more() )
11961 SMESH_Group * group = groupIt->next();
11962 if ( !group ) continue;
11963 groupDS = group->GetGroupDS();
11964 if ( !groupDS || groupDS->IsEmpty() ) continue;
11965 std::string grpName = group->GetName();
11966 //MESSAGE("grpName=" << grpName);
11967 if (grpName == groupName)
11973 bool isNodeGroup = false;
11974 bool isNodeCoords = false;
11977 if (groupDS->GetType() != SMDSAbs_Node)
11979 isNodeGroup = true; // a group of nodes exists and it is in this mesh
11982 if (nodesCoords.size() > 0)
11983 isNodeCoords = true; // a list o nodes given by their coordinates
11984 //MESSAGE("---" << isNodeGroup << " " << isNodeCoords);
11986 // --- define groups to build
11988 int idg; // --- group of SMDS volumes
11989 string grpvName = groupName;
11990 grpvName += "_vol";
11991 SMESH_Group *grp = this->myMesh->AddGroup(SMDSAbs_Volume, grpvName.c_str(), idg);
11994 MESSAGE("group not created " << grpvName);
11997 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(grp->GetGroupDS());
11999 int idgs; // --- group of SMDS faces on the skin
12000 string grpsName = groupName;
12001 grpsName += "_skin";
12002 SMESH_Group *grps = this->myMesh->AddGroup(SMDSAbs_Face, grpsName.c_str(), idgs);
12005 MESSAGE("group not created " << grpsName);
12008 SMESHDS_Group *sgrps = dynamic_cast<SMESHDS_Group*>(grps->GetGroupDS());
12010 int idgi; // --- group of SMDS faces internal (several shapes)
12011 string grpiName = groupName;
12012 grpiName += "_internalFaces";
12013 SMESH_Group *grpi = this->myMesh->AddGroup(SMDSAbs_Face, grpiName.c_str(), idgi);
12016 MESSAGE("group not created " << grpiName);
12019 SMESHDS_Group *sgrpi = dynamic_cast<SMESHDS_Group*>(grpi->GetGroupDS());
12021 int idgei; // --- group of SMDS faces internal (several shapes)
12022 string grpeiName = groupName;
12023 grpeiName += "_internalEdges";
12024 SMESH_Group *grpei = this->myMesh->AddGroup(SMDSAbs_Edge, grpeiName.c_str(), idgei);
12027 MESSAGE("group not created " << grpeiName);
12030 SMESHDS_Group *sgrpei = dynamic_cast<SMESHDS_Group*>(grpei->GetGroupDS());
12032 // --- build downward connectivity
12034 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
12035 meshDS->BuildDownWardConnectivity(true);
12036 SMDS_UnstructuredGrid* grid = meshDS->getGrid();
12038 // --- set of volumes detected inside
12040 std::set<int> setOfInsideVol;
12041 std::set<int> setOfVolToCheck;
12043 std::vector<gp_Pnt> gpnts;
12046 if (isNodeGroup) // --- a group of nodes is provided : find all the volumes using one or more of this nodes
12048 //MESSAGE("group of nodes provided");
12049 SMDS_ElemIteratorPtr elemIt = groupDS->GetElements();
12050 while ( elemIt->more() )
12052 const SMDS_MeshElement* elem = elemIt->next();
12055 const SMDS_MeshNode* node = dynamic_cast<const SMDS_MeshNode*>(elem);
12058 SMDS_MeshElement* vol = 0;
12059 SMDS_ElemIteratorPtr volItr = node->GetInverseElementIterator(SMDSAbs_Volume);
12060 while (volItr->more())
12062 vol = (SMDS_MeshElement*)volItr->next();
12063 setOfInsideVol.insert(vol->getVtkId());
12064 sgrp->Add(vol->GetID());
12068 else if (isNodeCoords)
12070 //MESSAGE("list of nodes coordinates provided");
12073 while ( i < nodesCoords.size()-2 )
12075 double x = nodesCoords[i++];
12076 double y = nodesCoords[i++];
12077 double z = nodesCoords[i++];
12078 gp_Pnt p = gp_Pnt(x, y ,z);
12079 gpnts.push_back(p);
12080 //MESSAGE("TopoDS_Vertex " << k << " " << p.X() << " " << p.Y() << " " << p.Z());
12084 else // --- no group, no coordinates : use the vertices of the geom shape provided, and radius
12086 //MESSAGE("no group of nodes provided, using vertices from geom shape, and radius");
12087 TopTools_IndexedMapOfShape vertexMap;
12088 TopExp::MapShapes( theShape, TopAbs_VERTEX, vertexMap );
12089 gp_Pnt p = gp_Pnt(0,0,0);
12090 if (vertexMap.Extent() < 1)
12093 for ( int i = 1; i <= vertexMap.Extent(); ++i )
12095 const TopoDS_Vertex& vertex = TopoDS::Vertex( vertexMap( i ));
12096 p = BRep_Tool::Pnt(vertex);
12097 gpnts.push_back(p);
12098 //MESSAGE("TopoDS_Vertex " << i << " " << p.X() << " " << p.Y() << " " << p.Z());
12102 if (gpnts.size() > 0)
12104 const SMDS_MeshNode* startNode = theNodeSearcher->FindClosestTo(gpnts[0]);
12105 //MESSAGE("startNode->nodeId " << nodeId);
12107 double radius2 = radius*radius;
12108 //MESSAGE("radius2 " << radius2);
12110 // --- volumes on start node
12112 setOfVolToCheck.clear();
12113 SMDS_MeshElement* startVol = 0;
12114 SMDS_ElemIteratorPtr volItr = startNode->GetInverseElementIterator(SMDSAbs_Volume);
12115 while (volItr->more())
12117 startVol = (SMDS_MeshElement*)volItr->next();
12118 setOfVolToCheck.insert(startVol->getVtkId());
12120 if (setOfVolToCheck.empty())
12122 MESSAGE("No volumes found");
12126 // --- starting with central volumes then their neighbors, check if they are inside
12127 // or outside the domain, until no more new neighbor volume is inside.
12128 // Fill the group of inside volumes
12130 std::map<int, double> mapOfNodeDistance2;
12131 mapOfNodeDistance2.clear();
12132 std::set<int> setOfOutsideVol;
12133 while (!setOfVolToCheck.empty())
12135 std::set<int>::iterator it = setOfVolToCheck.begin();
12137 //MESSAGE("volume to check, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12138 bool volInside = false;
12139 vtkIdType npts = 0;
12140 vtkIdType* pts = 0;
12141 grid->GetCellPoints(vtkId, npts, pts);
12142 for (int i=0; i<npts; i++)
12144 double distance2 = 0;
12145 if (mapOfNodeDistance2.count(pts[i]))
12147 distance2 = mapOfNodeDistance2[pts[i]];
12148 //MESSAGE("point " << pts[i] << " distance2 " << distance2);
12152 double *coords = grid->GetPoint(pts[i]);
12153 gp_Pnt aPoint = gp_Pnt(coords[0], coords[1], coords[2]);
12155 for ( size_t j = 0; j < gpnts.size(); j++ )
12157 double d2 = aPoint.SquareDistance( gpnts[ j ]);
12158 if (d2 < distance2)
12161 if (distance2 < radius2)
12165 mapOfNodeDistance2[pts[i]] = distance2;
12166 //MESSAGE(" point " << pts[i] << " distance2 " << distance2 << " coords " << coords[0] << " " << coords[1] << " " << coords[2]);
12168 if (distance2 < radius2)
12170 volInside = true; // one or more nodes inside the domain
12171 sgrp->Add(meshDS->fromVtkToSmds(vtkId));
12177 setOfInsideVol.insert(vtkId);
12178 //MESSAGE(" volume inside, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12179 int neighborsVtkIds[NBMAXNEIGHBORS];
12180 int downIds[NBMAXNEIGHBORS];
12181 unsigned char downTypes[NBMAXNEIGHBORS];
12182 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12183 for (int n = 0; n < nbNeighbors; n++)
12184 if (!setOfInsideVol.count(neighborsVtkIds[n]) ||setOfOutsideVol.count(neighborsVtkIds[n]))
12185 setOfVolToCheck.insert(neighborsVtkIds[n]);
12189 setOfOutsideVol.insert(vtkId);
12190 //MESSAGE(" volume outside, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12192 setOfVolToCheck.erase(vtkId);
12196 // --- for outside hexahedrons, check if they have more than one neighbor volume inside
12197 // If yes, add the volume to the inside set
12199 bool addedInside = true;
12200 std::set<int> setOfVolToReCheck;
12201 while (addedInside)
12203 //MESSAGE(" --------------------------- re check");
12204 addedInside = false;
12205 std::set<int>::iterator itv = setOfInsideVol.begin();
12206 for (; itv != setOfInsideVol.end(); ++itv)
12209 int neighborsVtkIds[NBMAXNEIGHBORS];
12210 int downIds[NBMAXNEIGHBORS];
12211 unsigned char downTypes[NBMAXNEIGHBORS];
12212 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12213 for (int n = 0; n < nbNeighbors; n++)
12214 if (!setOfInsideVol.count(neighborsVtkIds[n]))
12215 setOfVolToReCheck.insert(neighborsVtkIds[n]);
12217 setOfVolToCheck = setOfVolToReCheck;
12218 setOfVolToReCheck.clear();
12219 while (!setOfVolToCheck.empty())
12221 std::set<int>::iterator it = setOfVolToCheck.begin();
12223 if (grid->GetCellType(vtkId) == VTK_HEXAHEDRON)
12225 //MESSAGE("volume to recheck, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12226 int countInside = 0;
12227 int neighborsVtkIds[NBMAXNEIGHBORS];
12228 int downIds[NBMAXNEIGHBORS];
12229 unsigned char downTypes[NBMAXNEIGHBORS];
12230 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12231 for (int n = 0; n < nbNeighbors; n++)
12232 if (setOfInsideVol.count(neighborsVtkIds[n]))
12234 //MESSAGE("countInside " << countInside);
12235 if (countInside > 1)
12237 //MESSAGE(" volume inside, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12238 setOfInsideVol.insert(vtkId);
12239 sgrp->Add(meshDS->fromVtkToSmds(vtkId));
12240 addedInside = true;
12243 setOfVolToReCheck.insert(vtkId);
12245 setOfVolToCheck.erase(vtkId);
12249 // --- map of Downward faces at the boundary, inside the global volume
12250 // map of Downward faces on the skin of the global volume (equivalent to SMDS faces on the skin)
12251 // fill group of SMDS faces inside the volume (when several volume shapes)
12252 // fill group of SMDS faces on the skin of the global volume (if skin)
12254 std::map<DownIdType, int, DownIdCompare> boundaryFaces; // boundary faces inside the volume --> corresponding cell
12255 std::map<DownIdType, int, DownIdCompare> skinFaces; // faces on the skin of the global volume --> corresponding cell
12256 std::set<int>::iterator it = setOfInsideVol.begin();
12257 for (; it != setOfInsideVol.end(); ++it)
12260 //MESSAGE(" vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12261 int neighborsVtkIds[NBMAXNEIGHBORS];
12262 int downIds[NBMAXNEIGHBORS];
12263 unsigned char downTypes[NBMAXNEIGHBORS];
12264 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId, true);
12265 for (int n = 0; n < nbNeighbors; n++)
12267 int neighborDim = SMDS_Downward::getCellDimension(grid->GetCellType(neighborsVtkIds[n]));
12268 if (neighborDim == 3)
12270 if (! setOfInsideVol.count(neighborsVtkIds[n])) // neighbor volume is not inside : face is boundary
12272 DownIdType face(downIds[n], downTypes[n]);
12273 boundaryFaces[face] = vtkId;
12275 // if the face between to volumes is in the mesh, get it (internal face between shapes)
12276 int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12277 if (vtkFaceId >= 0)
12279 sgrpi->Add(meshDS->fromVtkToSmds(vtkFaceId));
12280 // find also the smds edges on this face
12281 int nbEdges = grid->getDownArray(downTypes[n])->getNumberOfDownCells(downIds[n]);
12282 const int* dEdges = grid->getDownArray(downTypes[n])->getDownCells(downIds[n]);
12283 const unsigned char* dTypes = grid->getDownArray(downTypes[n])->getDownTypes(downIds[n]);
12284 for (int i = 0; i < nbEdges; i++)
12286 int vtkEdgeId = grid->getDownArray(dTypes[i])->getVtkCellId(dEdges[i]);
12287 if (vtkEdgeId >= 0)
12288 sgrpei->Add(meshDS->fromVtkToSmds(vtkEdgeId));
12292 else if (neighborDim == 2) // skin of the volume
12294 DownIdType face(downIds[n], downTypes[n]);
12295 skinFaces[face] = vtkId;
12296 int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12297 if (vtkFaceId >= 0)
12298 sgrps->Add(meshDS->fromVtkToSmds(vtkFaceId));
12303 // --- identify the edges constituting the wire of each subshape on the skin
12304 // define polylines with the nodes of edges, equivalent to wires
12305 // project polylines on subshapes, and partition, to get geom faces
12307 std::map<int, std::set<int> > shapeIdToVtkIdSet; // shapeId --> set of vtkId on skin
12308 std::set<int> emptySet;
12310 std::set<int> shapeIds;
12312 SMDS_ElemIteratorPtr itelem = sgrps->GetElements();
12313 while (itelem->more())
12315 const SMDS_MeshElement *elem = itelem->next();
12316 int shapeId = elem->getshapeId();
12317 int vtkId = elem->getVtkId();
12318 if (!shapeIdToVtkIdSet.count(shapeId))
12320 shapeIdToVtkIdSet[shapeId] = emptySet;
12321 shapeIds.insert(shapeId);
12323 shapeIdToVtkIdSet[shapeId].insert(vtkId);
12326 std::map<int, std::set<DownIdType, DownIdCompare> > shapeIdToEdges; // shapeId --> set of downward edges
12327 std::set<DownIdType, DownIdCompare> emptyEdges;
12328 emptyEdges.clear();
12330 std::map<int, std::set<int> >::iterator itShape = shapeIdToVtkIdSet.begin();
12331 for (; itShape != shapeIdToVtkIdSet.end(); ++itShape)
12333 int shapeId = itShape->first;
12334 //MESSAGE(" --- Shape ID --- "<< shapeId);
12335 shapeIdToEdges[shapeId] = emptyEdges;
12337 std::vector<int> nodesEdges;
12339 std::set<int>::iterator its = itShape->second.begin();
12340 for (; its != itShape->second.end(); ++its)
12343 //MESSAGE(" " << vtkId);
12344 int neighborsVtkIds[NBMAXNEIGHBORS];
12345 int downIds[NBMAXNEIGHBORS];
12346 unsigned char downTypes[NBMAXNEIGHBORS];
12347 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12348 for (int n = 0; n < nbNeighbors; n++)
12350 if (neighborsVtkIds[n]<0) // only smds faces are considered as neighbors here
12352 int smdsId = meshDS->fromVtkToSmds(neighborsVtkIds[n]);
12353 const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
12354 if ( shapeIds.count(elem->getshapeId()) && !sgrps->Contains(elem)) // edge : neighbor in the set of shape, not in the group
12356 DownIdType edge(downIds[n], downTypes[n]);
12357 if (!shapeIdToEdges[shapeId].count(edge))
12359 shapeIdToEdges[shapeId].insert(edge);
12361 int nbNodes = grid->getDownArray(downTypes[n])->getNodes(downIds[n],vtkNodeId);
12362 nodesEdges.push_back(vtkNodeId[0]);
12363 nodesEdges.push_back(vtkNodeId[nbNodes-1]);
12364 //MESSAGE(" --- nodes " << vtkNodeId[0]+1 << " " << vtkNodeId[nbNodes-1]+1);
12370 std::list<int> order;
12372 if (nodesEdges.size() > 0)
12374 order.push_back(nodesEdges[0]); //MESSAGE(" --- back " << order.back()+1); // SMDS id = VTK id + 1;
12375 nodesEdges[0] = -1;
12376 order.push_back(nodesEdges[1]); //MESSAGE(" --- back " << order.back()+1);
12377 nodesEdges[1] = -1; // do not reuse this edge
12381 int nodeTofind = order.back(); // try first to push back
12383 for ( i = 0; i < (int)nodesEdges.size(); i++ )
12384 if (nodesEdges[i] == nodeTofind)
12386 if ( i == (int) nodesEdges.size() )
12387 found = false; // no follower found on back
12390 if (i%2) // odd ==> use the previous one
12391 if (nodesEdges[i-1] < 0)
12395 order.push_back(nodesEdges[i-1]); //MESSAGE(" --- back " << order.back()+1);
12396 nodesEdges[i-1] = -1;
12398 else // even ==> use the next one
12399 if (nodesEdges[i+1] < 0)
12403 order.push_back(nodesEdges[i+1]); //MESSAGE(" --- back " << order.back()+1);
12404 nodesEdges[i+1] = -1;
12409 // try to push front
12411 nodeTofind = order.front(); // try to push front
12412 for ( i = 0; i < (int)nodesEdges.size(); i++ )
12413 if ( nodesEdges[i] == nodeTofind )
12415 if ( i == (int)nodesEdges.size() )
12417 found = false; // no predecessor found on front
12420 if (i%2) // odd ==> use the previous one
12421 if (nodesEdges[i-1] < 0)
12425 order.push_front(nodesEdges[i-1]); //MESSAGE(" --- front " << order.front()+1);
12426 nodesEdges[i-1] = -1;
12428 else // even ==> use the next one
12429 if (nodesEdges[i+1] < 0)
12433 order.push_front(nodesEdges[i+1]); //MESSAGE(" --- front " << order.front()+1);
12434 nodesEdges[i+1] = -1;
12440 std::vector<int> nodes;
12441 nodes.push_back(shapeId);
12442 std::list<int>::iterator itl = order.begin();
12443 for (; itl != order.end(); itl++)
12445 nodes.push_back((*itl) + 1); // SMDS id = VTK id + 1;
12446 //MESSAGE(" ordered node " << nodes[nodes.size()-1]);
12448 listOfListOfNodes.push_back(nodes);
12451 // partition geom faces with blocFissure
12452 // mesh blocFissure and geom faces of the skin (external wires given, triangle algo to choose)
12453 // mesh volume around blocFissure (skin triangles and quadrangle given, tetra algo to choose)
12459 //================================================================================
12461 * \brief Generates skin mesh (containing 2D cells) from 3D mesh
12462 * The created 2D mesh elements based on nodes of free faces of boundary volumes
12463 * \return TRUE if operation has been completed successfully, FALSE otherwise
12465 //================================================================================
12467 bool SMESH_MeshEditor::Make2DMeshFrom3D()
12469 // iterates on volume elements and detect all free faces on them
12470 SMESHDS_Mesh* aMesh = GetMeshDS();
12474 ElemFeatures faceType( SMDSAbs_Face );
12475 int nbFree = 0, nbExisted = 0, nbCreated = 0;
12476 SMDS_VolumeIteratorPtr vIt = aMesh->volumesIterator();
12479 const SMDS_MeshVolume* volume = vIt->next();
12480 SMDS_VolumeTool vTool( volume, /*ignoreCentralNodes=*/false );
12481 vTool.SetExternalNormal();
12482 const int iQuad = volume->IsQuadratic();
12483 faceType.SetQuad( iQuad );
12484 for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12486 if (!vTool.IsFreeFace(iface))
12489 vector<const SMDS_MeshNode *> nodes;
12490 int nbFaceNodes = vTool.NbFaceNodes(iface);
12491 const SMDS_MeshNode** faceNodes = vTool.GetFaceNodes(iface);
12493 for ( ; inode < nbFaceNodes; inode += iQuad+1)
12494 nodes.push_back(faceNodes[inode]);
12496 if (iQuad) // add medium nodes
12498 for ( inode = 1; inode < nbFaceNodes; inode += 2)
12499 nodes.push_back(faceNodes[inode]);
12500 if ( nbFaceNodes == 9 ) // bi-quadratic quad
12501 nodes.push_back(faceNodes[8]);
12503 // add new face based on volume nodes
12504 if (aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false) )
12506 nbExisted++; // face already exsist
12510 AddElement( nodes, faceType.SetPoly( nbFaceNodes/(iQuad+1) > 4 ));
12515 return ( nbFree == ( nbExisted + nbCreated ));
12520 inline const SMDS_MeshNode* getNodeWithSameID(SMESHDS_Mesh* mesh, const SMDS_MeshNode* node)
12522 if ( const SMDS_MeshNode* n = mesh->FindNode( node->GetID() ))
12524 return mesh->AddNodeWithID( node->X(),node->Y(),node->Z(), node->GetID() );
12527 //================================================================================
12529 * \brief Creates missing boundary elements
12530 * \param elements - elements whose boundary is to be checked
12531 * \param dimension - defines type of boundary elements to create
12532 * \param group - a group to store created boundary elements in
12533 * \param targetMesh - a mesh to store created boundary elements in
12534 * \param toCopyElements - if true, the checked elements will be copied into the targetMesh
12535 * \param toCopyExistingBoundary - if true, not only new but also pre-existing
12536 * boundary elements will be copied into the targetMesh
12537 * \param toAddExistingBondary - if true, not only new but also pre-existing
12538 * boundary elements will be added into the new group
12539 * \param aroundElements - if true, elements will be created on boundary of given
12540 * elements else, on boundary of the whole mesh.
12541 * \return nb of added boundary elements
12543 //================================================================================
12545 int SMESH_MeshEditor::MakeBoundaryMesh(const TIDSortedElemSet& elements,
12546 Bnd_Dimension dimension,
12547 SMESH_Group* group/*=0*/,
12548 SMESH_Mesh* targetMesh/*=0*/,
12549 bool toCopyElements/*=false*/,
12550 bool toCopyExistingBoundary/*=false*/,
12551 bool toAddExistingBondary/*= false*/,
12552 bool aroundElements/*= false*/)
12554 SMDSAbs_ElementType missType = (dimension == BND_2DFROM3D) ? SMDSAbs_Face : SMDSAbs_Edge;
12555 SMDSAbs_ElementType elemType = (dimension == BND_1DFROM2D) ? SMDSAbs_Face : SMDSAbs_Volume;
12556 // hope that all elements are of the same type, do not check them all
12557 if ( !elements.empty() && (*elements.begin())->GetType() != elemType )
12558 throw SALOME_Exception(LOCALIZED("wrong element type"));
12561 toCopyElements = toCopyExistingBoundary = false;
12563 SMESH_MeshEditor tgtEditor( targetMesh ? targetMesh : myMesh );
12564 SMESHDS_Mesh* aMesh = GetMeshDS(), *tgtMeshDS = tgtEditor.GetMeshDS();
12565 int nbAddedBnd = 0;
12567 // editor adding present bnd elements and optionally holding elements to add to the group
12568 SMESH_MeshEditor* presentEditor;
12569 SMESH_MeshEditor tgtEditor2( tgtEditor.GetMesh() );
12570 presentEditor = toAddExistingBondary ? &tgtEditor : &tgtEditor2;
12572 SMESH_MesherHelper helper( *myMesh );
12573 const TopAbs_ShapeEnum missShapeType = ( missType==SMDSAbs_Face ? TopAbs_FACE : TopAbs_EDGE );
12574 SMDS_VolumeTool vTool;
12575 TIDSortedElemSet avoidSet;
12576 const TIDSortedElemSet emptySet, *elemSet = aroundElements ? &elements : &emptySet;
12579 typedef vector<const SMDS_MeshNode*> TConnectivity;
12580 TConnectivity tgtNodes;
12581 ElemFeatures elemKind( missType ), elemToCopy;
12583 vector<const SMDS_MeshElement*> presentBndElems;
12584 vector<TConnectivity> missingBndElems;
12585 vector<int> freeFacets;
12586 TConnectivity nodes, elemNodes;
12588 SMDS_ElemIteratorPtr eIt;
12589 if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
12590 else eIt = elemSetIterator( elements );
12592 while (eIt->more())
12594 const SMDS_MeshElement* elem = eIt->next();
12595 const int iQuad = elem->IsQuadratic();
12596 elemKind.SetQuad( iQuad );
12598 // ------------------------------------------------------------------------------------
12599 // 1. For an elem, get present bnd elements and connectivities of missing bnd elements
12600 // ------------------------------------------------------------------------------------
12601 presentBndElems.clear();
12602 missingBndElems.clear();
12603 freeFacets.clear(); nodes.clear(); elemNodes.clear();
12604 if ( vTool.Set(elem, /*ignoreCentralNodes=*/true) ) // elem is a volume --------------
12606 const SMDS_MeshElement* otherVol = 0;
12607 for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12609 if ( !vTool.IsFreeFace(iface, &otherVol) &&
12610 ( !aroundElements || elements.count( otherVol )))
12612 freeFacets.push_back( iface );
12614 if ( missType == SMDSAbs_Face )
12615 vTool.SetExternalNormal();
12616 for ( size_t i = 0; i < freeFacets.size(); ++i )
12618 int iface = freeFacets[i];
12619 const SMDS_MeshNode** nn = vTool.GetFaceNodes(iface);
12620 const size_t nbFaceNodes = vTool.NbFaceNodes (iface);
12621 if ( missType == SMDSAbs_Edge ) // boundary edges
12623 nodes.resize( 2+iQuad );
12624 for ( size_t i = 0; i < nbFaceNodes; i += 1+iQuad )
12626 for ( size_t j = 0; j < nodes.size(); ++j )
12627 nodes[ j ] = nn[ i+j ];
12628 if ( const SMDS_MeshElement* edge =
12629 aMesh->FindElement( nodes, SMDSAbs_Edge, /*noMedium=*/false ))
12630 presentBndElems.push_back( edge );
12632 missingBndElems.push_back( nodes );
12635 else // boundary face
12638 for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12639 nodes.push_back( nn[inode] ); // add corner nodes
12641 for ( inode = 1; inode < nbFaceNodes; inode += 2)
12642 nodes.push_back( nn[inode] ); // add medium nodes
12643 int iCenter = vTool.GetCenterNodeIndex(iface); // for HEX27
12645 nodes.push_back( vTool.GetNodes()[ iCenter ] );
12647 if (const SMDS_MeshElement * f = aMesh->FindElement( nodes,
12648 SMDSAbs_Face, /*noMedium=*/false ))
12649 presentBndElems.push_back( f );
12651 missingBndElems.push_back( nodes );
12653 if ( targetMesh != myMesh )
12655 // add 1D elements on face boundary to be added to a new mesh
12656 const SMDS_MeshElement* edge;
12657 for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12660 edge = aMesh->FindEdge( nn[inode], nn[inode+1], nn[inode+2]);
12662 edge = aMesh->FindEdge( nn[inode], nn[inode+1]);
12663 if ( edge && avoidSet.insert( edge ).second )
12664 presentBndElems.push_back( edge );
12670 else if ( elem->GetType() == SMDSAbs_Face ) // elem is a face ------------------------
12672 avoidSet.clear(), avoidSet.insert( elem );
12673 elemNodes.assign( SMDS_MeshElement::iterator( elem->interlacedNodesElemIterator() ),
12674 SMDS_MeshElement::iterator() );
12675 elemNodes.push_back( elemNodes[0] );
12676 nodes.resize( 2 + iQuad );
12677 const int nbLinks = elem->NbCornerNodes();
12678 for ( int i = 0, iN = 0; i < nbLinks; i++, iN += 1+iQuad )
12680 nodes[0] = elemNodes[iN];
12681 nodes[1] = elemNodes[iN+1+iQuad];
12682 if ( SMESH_MeshAlgos::FindFaceInSet( nodes[0], nodes[1], *elemSet, avoidSet))
12683 continue; // not free link
12685 if ( iQuad ) nodes[2] = elemNodes[iN+1];
12686 if ( const SMDS_MeshElement* edge =
12687 aMesh->FindElement(nodes,SMDSAbs_Edge,/*noMedium=*/false))
12688 presentBndElems.push_back( edge );
12690 missingBndElems.push_back( nodes );
12694 // ---------------------------------
12695 // 2. Add missing boundary elements
12696 // ---------------------------------
12697 if ( targetMesh != myMesh )
12698 // instead of making a map of nodes in this mesh and targetMesh,
12699 // we create nodes with same IDs.
12700 for ( size_t i = 0; i < missingBndElems.size(); ++i )
12702 TConnectivity& srcNodes = missingBndElems[i];
12703 tgtNodes.resize( srcNodes.size() );
12704 for ( inode = 0; inode < srcNodes.size(); ++inode )
12705 tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, srcNodes[inode] );
12706 if ( aroundElements && tgtEditor.GetMeshDS()->FindElement( tgtNodes,
12708 /*noMedium=*/false))
12710 tgtEditor.AddElement( tgtNodes, elemKind.SetPoly( tgtNodes.size()/(iQuad+1) > 4 ));
12714 for ( size_t i = 0; i < missingBndElems.size(); ++i )
12716 TConnectivity& nodes = missingBndElems[ i ];
12717 if ( aroundElements && tgtEditor.GetMeshDS()->FindElement( nodes,
12719 /*noMedium=*/false))
12721 SMDS_MeshElement* newElem =
12722 tgtEditor.AddElement( nodes, elemKind.SetPoly( nodes.size()/(iQuad+1) > 4 ));
12723 nbAddedBnd += bool( newElem );
12725 // try to set a new element to a shape
12726 if ( myMesh->HasShapeToMesh() )
12729 set< pair<TopAbs_ShapeEnum, int > > mediumShapes;
12730 const size_t nbN = nodes.size() / (iQuad+1 );
12731 for ( inode = 0; inode < nbN && ok; ++inode )
12733 pair<int, TopAbs_ShapeEnum> i_stype =
12734 helper.GetMediumPos( nodes[inode], nodes[(inode+1)%nbN]);
12735 if (( ok = ( i_stype.first > 0 && i_stype.second >= TopAbs_FACE )))
12736 mediumShapes.insert( make_pair ( i_stype.second, i_stype.first ));
12738 if ( ok && mediumShapes.size() > 1 )
12740 set< pair<TopAbs_ShapeEnum, int > >::iterator stype_i = mediumShapes.begin();
12741 pair<TopAbs_ShapeEnum, int> stype_i_0 = *stype_i;
12742 for ( ++stype_i; stype_i != mediumShapes.end() && ok; ++stype_i )
12744 if (( ok = ( stype_i->first != stype_i_0.first )))
12745 ok = helper.IsSubShape( aMesh->IndexToShape( stype_i->second ),
12746 aMesh->IndexToShape( stype_i_0.second ));
12749 if ( ok && mediumShapes.begin()->first == missShapeType )
12750 aMesh->SetMeshElementOnShape( newElem, mediumShapes.begin()->second );
12754 // ----------------------------------
12755 // 3. Copy present boundary elements
12756 // ----------------------------------
12757 if ( toCopyExistingBoundary )
12758 for ( size_t i = 0 ; i < presentBndElems.size(); ++i )
12760 const SMDS_MeshElement* e = presentBndElems[i];
12761 tgtNodes.resize( e->NbNodes() );
12762 for ( inode = 0; inode < tgtNodes.size(); ++inode )
12763 tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, e->GetNode(inode) );
12764 presentEditor->AddElement( tgtNodes, elemToCopy.Init( e ));
12766 else // store present elements to add them to a group
12767 for ( size_t i = 0 ; i < presentBndElems.size(); ++i )
12769 presentEditor->myLastCreatedElems.Append( presentBndElems[ i ]);
12772 } // loop on given elements
12774 // ---------------------------------------------
12775 // 4. Fill group with boundary elements
12776 // ---------------------------------------------
12779 if ( SMESHDS_Group* g = dynamic_cast<SMESHDS_Group*>( group->GetGroupDS() ))
12780 for ( int i = 0; i < tgtEditor.myLastCreatedElems.Size(); ++i )
12781 g->SMDSGroup().Add( tgtEditor.myLastCreatedElems( i+1 ));
12783 tgtEditor.myLastCreatedElems.Clear();
12784 tgtEditor2.myLastCreatedElems.Clear();
12786 // -----------------------
12787 // 5. Copy given elements
12788 // -----------------------
12789 if ( toCopyElements && targetMesh != myMesh )
12791 if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
12792 else eIt = elemSetIterator( elements );
12793 while (eIt->more())
12795 const SMDS_MeshElement* elem = eIt->next();
12796 tgtNodes.resize( elem->NbNodes() );
12797 for ( inode = 0; inode < tgtNodes.size(); ++inode )
12798 tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, elem->GetNode(inode) );
12799 tgtEditor.AddElement( tgtNodes, elemToCopy.Init( elem ));
12801 tgtEditor.myLastCreatedElems.Clear();
12807 //================================================================================
12809 * \brief Copy node position and set \a to node on the same geometry
12811 //================================================================================
12813 void SMESH_MeshEditor::copyPosition( const SMDS_MeshNode* from,
12814 const SMDS_MeshNode* to )
12816 if ( !from || !to ) return;
12818 SMDS_PositionPtr pos = from->GetPosition();
12819 if ( !pos || from->getshapeId() < 1 ) return;
12821 switch ( pos->GetTypeOfPosition() )
12823 case SMDS_TOP_3DSPACE: break;
12825 case SMDS_TOP_FACE:
12827 const SMDS_FacePosition* fPos = static_cast< const SMDS_FacePosition* >( pos );
12828 GetMeshDS()->SetNodeOnFace( to, from->getshapeId(),
12829 fPos->GetUParameter(), fPos->GetVParameter() );
12832 case SMDS_TOP_EDGE:
12834 // WARNING: it is dangerous to set equal nodes on one EDGE!!!!!!!!
12835 const SMDS_EdgePosition* ePos = static_cast< const SMDS_EdgePosition* >( pos );
12836 GetMeshDS()->SetNodeOnEdge( to, from->getshapeId(), ePos->GetUParameter() );
12839 case SMDS_TOP_VERTEX:
12841 GetMeshDS()->SetNodeOnVertex( to, from->getshapeId() );
12844 case SMDS_TOP_UNSPEC: