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 "utilities.h"
52 #include <BRepAdaptor_Surface.hxx>
53 #include <BRepBuilderAPI_MakeEdge.hxx>
54 #include <BRepClass3d_SolidClassifier.hxx>
55 #include <BRep_Tool.hxx>
57 #include <Extrema_GenExtPS.hxx>
58 #include <Extrema_POnCurv.hxx>
59 #include <Extrema_POnSurf.hxx>
60 #include <Geom2d_Curve.hxx>
61 #include <GeomAdaptor_Surface.hxx>
62 #include <Geom_Curve.hxx>
63 #include <Geom_Surface.hxx>
64 #include <Precision.hxx>
65 #include <TColStd_ListOfInteger.hxx>
66 #include <TopAbs_State.hxx>
68 #include <TopExp_Explorer.hxx>
69 #include <TopTools_ListIteratorOfListOfShape.hxx>
70 #include <TopTools_ListOfShape.hxx>
71 #include <TopTools_SequenceOfShape.hxx>
73 #include <TopoDS_Edge.hxx>
74 #include <TopoDS_Face.hxx>
75 #include <TopoDS_Solid.hxx>
81 #include <gp_Trsf.hxx>
95 #include <boost/tuple/tuple.hpp>
96 #include <boost/container/flat_set.hpp>
98 #include <Standard_Failure.hxx>
99 #include <Standard_ErrorHandler.hxx>
101 #define cast2Node(elem) static_cast<const SMDS_MeshNode*>( elem )
104 using namespace SMESH::Controls;
108 template < class ELEM_SET >
109 SMDS_ElemIteratorPtr elemSetIterator( const ELEM_SET& elements )
111 typedef SMDS_SetIterator
112 < SMDS_pElement, typename ELEM_SET::const_iterator> TSetIterator;
113 return SMDS_ElemIteratorPtr( new TSetIterator( elements.begin(), elements.end() ));
117 //=======================================================================
118 //function : SMESH_MeshEditor
120 //=======================================================================
122 SMESH_MeshEditor::SMESH_MeshEditor( SMESH_Mesh* theMesh )
123 :myMesh( theMesh ) // theMesh may be NULL
127 //================================================================================
129 * \brief Return mesh DS
131 //================================================================================
133 SMESHDS_Mesh * SMESH_MeshEditor::GetMeshDS()
135 return myMesh->GetMeshDS();
139 //================================================================================
141 * \brief Clears myLastCreatedNodes and myLastCreatedElems
143 //================================================================================
145 void SMESH_MeshEditor::ClearLastCreated()
147 myLastCreatedNodes.Clear();
148 myLastCreatedElems.Clear();
151 //================================================================================
153 * \brief Initializes members by an existing element
154 * \param [in] elem - the source element
155 * \param [in] basicOnly - if true, does not set additional data of Ball and Polyhedron
157 //================================================================================
159 SMESH_MeshEditor::ElemFeatures&
160 SMESH_MeshEditor::ElemFeatures::Init( const SMDS_MeshElement* elem, bool basicOnly )
164 myType = elem->GetType();
165 if ( myType == SMDSAbs_Face || myType == SMDSAbs_Volume )
167 myIsPoly = elem->IsPoly();
170 myIsQuad = elem->IsQuadratic();
171 if ( myType == SMDSAbs_Volume && !basicOnly )
173 vector<int > quant = static_cast<const SMDS_VtkVolume* >( elem )->GetQuantities();
174 myPolyhedQuantities.swap( quant );
178 else if ( myType == SMDSAbs_Ball && !basicOnly )
180 myBallDiameter = static_cast<const SMDS_BallElement*>(elem)->GetDiameter();
186 //=======================================================================
190 //=======================================================================
193 SMESH_MeshEditor::AddElement(const vector<const SMDS_MeshNode*> & node,
194 const ElemFeatures& features)
196 SMDS_MeshElement* e = 0;
197 int nbnode = node.size();
198 SMESHDS_Mesh* mesh = GetMeshDS();
199 const int ID = features.myID;
201 switch ( features.myType ) {
203 if ( !features.myIsPoly ) {
205 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], ID);
206 else e = mesh->AddFace (node[0], node[1], node[2] );
208 else if (nbnode == 4) {
209 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3], ID);
210 else e = mesh->AddFace (node[0], node[1], node[2], node[3] );
212 else if (nbnode == 6) {
213 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
214 node[4], node[5], ID);
215 else e = mesh->AddFace (node[0], node[1], node[2], node[3],
218 else if (nbnode == 7) {
219 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
220 node[4], node[5], node[6], ID);
221 else e = mesh->AddFace (node[0], node[1], node[2], node[3],
222 node[4], node[5], node[6] );
224 else if (nbnode == 8) {
225 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
226 node[4], node[5], node[6], node[7], ID);
227 else e = mesh->AddFace (node[0], node[1], node[2], node[3],
228 node[4], node[5], node[6], node[7] );
230 else if (nbnode == 9) {
231 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
232 node[4], node[5], node[6], node[7], node[8], ID);
233 else e = mesh->AddFace (node[0], node[1], node[2], node[3],
234 node[4], node[5], node[6], node[7], node[8] );
237 else if ( !features.myIsQuad )
239 if ( ID >= 1 ) e = mesh->AddPolygonalFaceWithID(node, ID);
240 else e = mesh->AddPolygonalFace (node );
242 else if ( nbnode % 2 == 0 ) // just a protection
244 if ( ID >= 1 ) e = mesh->AddQuadPolygonalFaceWithID(node, ID);
245 else e = mesh->AddQuadPolygonalFace (node );
250 if ( !features.myIsPoly ) {
252 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3], ID);
253 else e = mesh->AddVolume (node[0], node[1], node[2], node[3] );
255 else if (nbnode == 5) {
256 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
258 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
261 else if (nbnode == 6) {
262 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
263 node[4], node[5], ID);
264 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
267 else if (nbnode == 8) {
268 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
269 node[4], node[5], node[6], node[7], ID);
270 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
271 node[4], node[5], node[6], node[7] );
273 else if (nbnode == 10) {
274 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
275 node[4], node[5], node[6], node[7],
276 node[8], node[9], ID);
277 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
278 node[4], node[5], node[6], node[7],
281 else if (nbnode == 12) {
282 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
283 node[4], node[5], node[6], node[7],
284 node[8], node[9], node[10], node[11], ID);
285 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
286 node[4], node[5], node[6], node[7],
287 node[8], node[9], node[10], node[11] );
289 else if (nbnode == 13) {
290 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
291 node[4], node[5], node[6], node[7],
292 node[8], node[9], node[10],node[11],
294 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
295 node[4], node[5], node[6], node[7],
296 node[8], node[9], node[10],node[11],
299 else if (nbnode == 15) {
300 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
301 node[4], node[5], node[6], node[7],
302 node[8], node[9], node[10],node[11],
303 node[12],node[13],node[14],ID);
304 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
305 node[4], node[5], node[6], node[7],
306 node[8], node[9], node[10],node[11],
307 node[12],node[13],node[14] );
309 else if (nbnode == 20) {
310 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
311 node[4], node[5], node[6], node[7],
312 node[8], node[9], node[10],node[11],
313 node[12],node[13],node[14],node[15],
314 node[16],node[17],node[18],node[19],ID);
315 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
316 node[4], node[5], node[6], node[7],
317 node[8], node[9], node[10],node[11],
318 node[12],node[13],node[14],node[15],
319 node[16],node[17],node[18],node[19] );
321 else if (nbnode == 27) {
322 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
323 node[4], node[5], node[6], node[7],
324 node[8], node[9], node[10],node[11],
325 node[12],node[13],node[14],node[15],
326 node[16],node[17],node[18],node[19],
327 node[20],node[21],node[22],node[23],
328 node[24],node[25],node[26], ID);
329 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
330 node[4], node[5], node[6], node[7],
331 node[8], node[9], node[10],node[11],
332 node[12],node[13],node[14],node[15],
333 node[16],node[17],node[18],node[19],
334 node[20],node[21],node[22],node[23],
335 node[24],node[25],node[26] );
338 else if ( !features.myIsQuad )
340 if ( ID >= 1 ) e = mesh->AddPolyhedralVolumeWithID(node, features.myPolyhedQuantities, ID);
341 else e = mesh->AddPolyhedralVolume (node, features.myPolyhedQuantities );
345 // if ( ID >= 1 ) e = mesh->AddQuadPolyhedralVolumeWithID(node, features.myPolyhedQuantities,ID);
346 // else e = mesh->AddQuadPolyhedralVolume (node, features.myPolyhedQuantities );
352 if ( ID >= 1 ) e = mesh->AddEdgeWithID(node[0], node[1], ID);
353 else e = mesh->AddEdge (node[0], node[1] );
355 else if ( nbnode == 3 ) {
356 if ( ID >= 1 ) e = mesh->AddEdgeWithID(node[0], node[1], node[2], ID);
357 else e = mesh->AddEdge (node[0], node[1], node[2] );
361 case SMDSAbs_0DElement:
363 if ( ID >= 1 ) e = mesh->Add0DElementWithID(node[0], ID);
364 else e = mesh->Add0DElement (node[0] );
369 if ( ID >= 1 ) e = mesh->AddNodeWithID(node[0]->X(), node[0]->Y(), node[0]->Z(), ID);
370 else e = mesh->AddNode (node[0]->X(), node[0]->Y(), node[0]->Z() );
374 if ( ID >= 1 ) e = mesh->AddBallWithID(node[0], features.myBallDiameter, ID);
375 else e = mesh->AddBall (node[0], features.myBallDiameter );
380 if ( e ) myLastCreatedElems.Append( e );
384 //=======================================================================
388 //=======================================================================
390 SMDS_MeshElement* SMESH_MeshEditor::AddElement(const vector<int> & nodeIDs,
391 const ElemFeatures& features)
393 vector<const SMDS_MeshNode*> nodes;
394 nodes.reserve( nodeIDs.size() );
395 vector<int>::const_iterator id = nodeIDs.begin();
396 while ( id != nodeIDs.end() ) {
397 if ( const SMDS_MeshNode* node = GetMeshDS()->FindNode( *id++ ))
398 nodes.push_back( node );
402 return AddElement( nodes, features );
405 //=======================================================================
407 //purpose : Remove a node or an element.
408 // Modify a compute state of sub-meshes which become empty
409 //=======================================================================
411 int SMESH_MeshEditor::Remove (const list< int >& theIDs,
414 myLastCreatedElems.Clear();
415 myLastCreatedNodes.Clear();
417 SMESHDS_Mesh* aMesh = GetMeshDS();
418 set< SMESH_subMesh *> smmap;
421 list<int>::const_iterator it = theIDs.begin();
422 for ( ; it != theIDs.end(); it++ ) {
423 const SMDS_MeshElement * elem;
425 elem = aMesh->FindNode( *it );
427 elem = aMesh->FindElement( *it );
431 // Notify VERTEX sub-meshes about modification
433 const SMDS_MeshNode* node = cast2Node( elem );
434 if ( node->GetPosition()->GetTypeOfPosition() == SMDS_TOP_VERTEX )
435 if ( int aShapeID = node->getshapeId() )
436 if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( aShapeID ) )
439 // Find sub-meshes to notify about modification
440 // SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
441 // while ( nodeIt->more() ) {
442 // const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
443 // const SMDS_PositionPtr& aPosition = node->GetPosition();
444 // if ( aPosition.get() ) {
445 // if ( int aShapeID = aPosition->GetShapeId() ) {
446 // if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( aShapeID ) )
447 // smmap.insert( sm );
454 aMesh->RemoveNode( static_cast< const SMDS_MeshNode* >( elem ));
456 aMesh->RemoveElement( elem );
460 // Notify sub-meshes about modification
461 if ( !smmap.empty() ) {
462 set< SMESH_subMesh *>::iterator smIt;
463 for ( smIt = smmap.begin(); smIt != smmap.end(); smIt++ )
464 (*smIt)->ComputeStateEngine( SMESH_subMesh::MESH_ENTITY_REMOVED );
467 // // Check if the whole mesh becomes empty
468 // if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( 1 ) )
469 // sm->ComputeStateEngine( SMESH_subMesh::CHECK_COMPUTE_STATE );
474 //================================================================================
476 * \brief Create 0D elements on all nodes of the given object.
477 * \param elements - Elements on whose nodes to create 0D elements; if empty,
478 * the all mesh is treated
479 * \param all0DElems - returns all 0D elements found or created on nodes of \a elements
480 * \param duplicateElements - to add one more 0D element to a node or not
482 //================================================================================
484 void SMESH_MeshEditor::Create0DElementsOnAllNodes( const TIDSortedElemSet& elements,
485 TIDSortedElemSet& all0DElems,
486 const bool duplicateElements )
488 SMDS_ElemIteratorPtr elemIt;
489 if ( elements.empty() )
491 elemIt = GetMeshDS()->elementsIterator( SMDSAbs_Node );
495 elemIt = elemSetIterator( elements );
498 while ( elemIt->more() )
500 const SMDS_MeshElement* e = elemIt->next();
501 SMDS_ElemIteratorPtr nodeIt = e->nodesIterator();
502 while ( nodeIt->more() )
504 const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
505 SMDS_ElemIteratorPtr it0D = n->GetInverseElementIterator( SMDSAbs_0DElement );
506 if ( duplicateElements || !it0D->more() )
508 myLastCreatedElems.Append( GetMeshDS()->Add0DElement( n ));
509 all0DElems.insert( myLastCreatedElems.Last() );
511 while ( it0D->more() )
512 all0DElems.insert( it0D->next() );
517 //=======================================================================
518 //function : FindShape
519 //purpose : Return an index of the shape theElem is on
520 // or zero if a shape not found
521 //=======================================================================
523 int SMESH_MeshEditor::FindShape (const SMDS_MeshElement * theElem)
525 myLastCreatedElems.Clear();
526 myLastCreatedNodes.Clear();
528 SMESHDS_Mesh * aMesh = GetMeshDS();
529 if ( aMesh->ShapeToMesh().IsNull() )
532 int aShapeID = theElem->getshapeId();
536 if ( SMESHDS_SubMesh * sm = aMesh->MeshElements( aShapeID ))
537 if ( sm->Contains( theElem ))
540 if ( theElem->GetType() == SMDSAbs_Node ) {
541 MESSAGE( ":( Error: invalid myShapeId of node " << theElem->GetID() );
544 MESSAGE( ":( Error: invalid myShapeId of element " << theElem->GetID() );
547 TopoDS_Shape aShape; // the shape a node of theElem is on
548 if ( theElem->GetType() != SMDSAbs_Node )
550 SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator();
551 while ( nodeIt->more() ) {
552 const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
553 if ((aShapeID = node->getshapeId()) > 0) {
554 if ( SMESHDS_SubMesh * sm = aMesh->MeshElements( aShapeID ) ) {
555 if ( sm->Contains( theElem ))
557 if ( aShape.IsNull() )
558 aShape = aMesh->IndexToShape( aShapeID );
564 // None of nodes is on a proper shape,
565 // find the shape among ancestors of aShape on which a node is
566 if ( !aShape.IsNull() ) {
567 TopTools_ListIteratorOfListOfShape ancIt( GetMesh()->GetAncestors( aShape ));
568 for ( ; ancIt.More(); ancIt.Next() ) {
569 SMESHDS_SubMesh * sm = aMesh->MeshElements( ancIt.Value() );
570 if ( sm && sm->Contains( theElem ))
571 return aMesh->ShapeToIndex( ancIt.Value() );
576 SMESHDS_SubMeshIteratorPtr smIt = GetMeshDS()->SubMeshes();
577 while ( const SMESHDS_SubMesh* sm = smIt->next() )
578 if ( sm->Contains( theElem ))
585 //=======================================================================
586 //function : IsMedium
588 //=======================================================================
590 bool SMESH_MeshEditor::IsMedium(const SMDS_MeshNode* node,
591 const SMDSAbs_ElementType typeToCheck)
593 bool isMedium = false;
594 SMDS_ElemIteratorPtr it = node->GetInverseElementIterator(typeToCheck);
595 while (it->more() && !isMedium ) {
596 const SMDS_MeshElement* elem = it->next();
597 isMedium = elem->IsMediumNode(node);
602 //=======================================================================
603 //function : shiftNodesQuadTria
604 //purpose : Shift nodes in the array corresponded to quadratic triangle
605 // example: (0,1,2,3,4,5) -> (1,2,0,4,5,3)
606 //=======================================================================
608 static void shiftNodesQuadTria(vector< const SMDS_MeshNode* >& aNodes)
610 const SMDS_MeshNode* nd1 = aNodes[0];
611 aNodes[0] = aNodes[1];
612 aNodes[1] = aNodes[2];
614 const SMDS_MeshNode* nd2 = aNodes[3];
615 aNodes[3] = aNodes[4];
616 aNodes[4] = aNodes[5];
620 //=======================================================================
621 //function : nbEdgeConnectivity
622 //purpose : return number of the edges connected with the theNode.
623 // if theEdges has connections with the other type of the
624 // elements, return -1
625 //=======================================================================
627 static int nbEdgeConnectivity(const SMDS_MeshNode* theNode)
629 // SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator();
631 // while(elemIt->more()) {
636 return theNode->NbInverseElements();
639 //=======================================================================
640 //function : getNodesFromTwoTria
642 //=======================================================================
644 static bool getNodesFromTwoTria(const SMDS_MeshElement * theTria1,
645 const SMDS_MeshElement * theTria2,
646 vector< const SMDS_MeshNode*>& N1,
647 vector< const SMDS_MeshNode*>& N2)
649 N1.assign( theTria1->begin_nodes(), theTria1->end_nodes() );
650 if ( N1.size() < 6 ) return false;
651 N2.assign( theTria2->begin_nodes(), theTria2->end_nodes() );
652 if ( N2.size() < 6 ) return false;
654 int sames[3] = {-1,-1,-1};
666 if(nbsames!=2) return false;
668 shiftNodesQuadTria(N1);
670 shiftNodesQuadTria(N1);
673 i = sames[0] + sames[1] + sames[2];
675 shiftNodesQuadTria(N2);
677 // now we receive following N1 and N2 (using numeration as in the image below)
678 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
679 // i.e. first nodes from both arrays form a new diagonal
683 //=======================================================================
684 //function : InverseDiag
685 //purpose : Replace two neighbour triangles with ones built on the same 4 nodes
686 // but having other common link.
687 // Return False if args are improper
688 //=======================================================================
690 bool SMESH_MeshEditor::InverseDiag (const SMDS_MeshElement * theTria1,
691 const SMDS_MeshElement * theTria2 )
693 myLastCreatedElems.Clear();
694 myLastCreatedNodes.Clear();
696 if (!theTria1 || !theTria2)
699 const SMDS_VtkFace* F1 = dynamic_cast<const SMDS_VtkFace*>( theTria1 );
700 if (!F1) return false;
701 const SMDS_VtkFace* F2 = dynamic_cast<const SMDS_VtkFace*>( theTria2 );
702 if (!F2) return false;
703 if ((theTria1->GetEntityType() == SMDSEntity_Triangle) &&
704 (theTria2->GetEntityType() == SMDSEntity_Triangle)) {
706 // 1 +--+ A theTria1: ( 1 A B ) A->2 ( 1 2 B ) 1 +--+ A
707 // | /| theTria2: ( B A 2 ) B->1 ( 1 A 2 ) |\ |
711 // put nodes in array and find out indices of the same ones
712 const SMDS_MeshNode* aNodes [6];
713 int sameInd [] = { -1, -1, -1, -1, -1, -1 };
715 SMDS_ElemIteratorPtr it = theTria1->nodesIterator();
716 while ( it->more() ) {
717 aNodes[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
719 if ( i > 2 ) // theTria2
720 // find same node of theTria1
721 for ( int j = 0; j < 3; j++ )
722 if ( aNodes[ i ] == aNodes[ j ]) {
731 return false; // theTria1 is not a triangle
732 it = theTria2->nodesIterator();
734 if ( i == 6 && it->more() )
735 return false; // theTria2 is not a triangle
738 // find indices of 1,2 and of A,B in theTria1
739 int iA = -1, iB = 0, i1 = 0, i2 = 0;
740 for ( i = 0; i < 6; i++ ) {
741 if ( sameInd [ i ] == -1 ) {
746 if ( iA >= 0) iB = i;
750 // nodes 1 and 2 should not be the same
751 if ( aNodes[ i1 ] == aNodes[ i2 ] )
755 aNodes[ iA ] = aNodes[ i2 ];
757 aNodes[ sameInd[ iB ]] = aNodes[ i1 ];
759 GetMeshDS()->ChangeElementNodes( theTria1, aNodes, 3 );
760 GetMeshDS()->ChangeElementNodes( theTria2, &aNodes[ 3 ], 3 );
764 } // end if(F1 && F2)
766 // check case of quadratic faces
767 if (theTria1->GetEntityType() != SMDSEntity_Quad_Triangle &&
768 theTria1->GetEntityType() != SMDSEntity_BiQuad_Triangle)
770 if (theTria2->GetEntityType() != SMDSEntity_Quad_Triangle&&
771 theTria2->GetEntityType() != SMDSEntity_BiQuad_Triangle)
775 // 1 +--+--+ 2 theTria1: (1 2 4 5 9 7) or (2 4 1 9 7 5) or (4 1 2 7 5 9)
776 // | /| theTria2: (2 3 4 6 8 9) or (3 4 2 8 9 6) or (4 2 3 9 6 8)
784 vector< const SMDS_MeshNode* > N1;
785 vector< const SMDS_MeshNode* > N2;
786 if(!getNodesFromTwoTria(theTria1,theTria2,N1,N2))
788 // now we receive following N1 and N2 (using numeration as above image)
789 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
790 // i.e. first nodes from both arrays determ new diagonal
792 vector< const SMDS_MeshNode*> N1new( N1.size() );
793 vector< const SMDS_MeshNode*> N2new( N2.size() );
794 N1new.back() = N1.back(); // central node of biquadratic
795 N2new.back() = N2.back();
796 N1new[0] = N1[0]; N2new[0] = N1[0];
797 N1new[1] = N2[0]; N2new[1] = N1[1];
798 N1new[2] = N2[1]; N2new[2] = N2[0];
799 N1new[3] = N1[4]; N2new[3] = N1[3];
800 N1new[4] = N2[3]; N2new[4] = N2[5];
801 N1new[5] = N1[5]; N2new[5] = N1[4];
802 // change nodes in faces
803 GetMeshDS()->ChangeElementNodes( theTria1, &N1new[0], N1new.size() );
804 GetMeshDS()->ChangeElementNodes( theTria2, &N2new[0], N2new.size() );
806 // move the central node of biquadratic triangle
807 SMESH_MesherHelper helper( *GetMesh() );
808 for ( int is2nd = 0; is2nd < 2; ++is2nd )
810 const SMDS_MeshElement* tria = is2nd ? theTria2 : theTria1;
811 vector< const SMDS_MeshNode*>& nodes = is2nd ? N2new : N1new;
812 if ( nodes.size() < 7 )
814 helper.SetSubShape( tria->getshapeId() );
815 const TopoDS_Face& F = TopoDS::Face( helper.GetSubShape() );
819 xyz = ( SMESH_TNodeXYZ( nodes[3] ) +
820 SMESH_TNodeXYZ( nodes[4] ) +
821 SMESH_TNodeXYZ( nodes[5] )) / 3.;
826 gp_XY uv = ( helper.GetNodeUV( F, nodes[3], nodes[2], &checkUV ) +
827 helper.GetNodeUV( F, nodes[4], nodes[0], &checkUV ) +
828 helper.GetNodeUV( F, nodes[5], nodes[1], &checkUV )) / 3.;
830 Handle(Geom_Surface) S = BRep_Tool::Surface(F,loc);
831 xyz = S->Value( uv.X(), uv.Y() );
832 xyz.Transform( loc );
833 if ( nodes[6]->GetPosition()->GetTypeOfPosition() == SMDS_TOP_FACE && // set UV
834 nodes[6]->getshapeId() > 0 )
835 GetMeshDS()->SetNodeOnFace( nodes[6], nodes[6]->getshapeId(), uv.X(), uv.Y() );
837 GetMeshDS()->MoveNode( nodes[6], xyz.X(), xyz.Y(), xyz.Z() );
842 //=======================================================================
843 //function : findTriangles
844 //purpose : find triangles sharing theNode1-theNode2 link
845 //=======================================================================
847 static bool findTriangles(const SMDS_MeshNode * theNode1,
848 const SMDS_MeshNode * theNode2,
849 const SMDS_MeshElement*& theTria1,
850 const SMDS_MeshElement*& theTria2)
852 if ( !theNode1 || !theNode2 ) return false;
854 theTria1 = theTria2 = 0;
856 set< const SMDS_MeshElement* > emap;
857 SMDS_ElemIteratorPtr it = theNode1->GetInverseElementIterator(SMDSAbs_Face);
859 const SMDS_MeshElement* elem = it->next();
860 if ( elem->NbCornerNodes() == 3 )
863 it = theNode2->GetInverseElementIterator(SMDSAbs_Face);
865 const SMDS_MeshElement* elem = it->next();
866 if ( emap.count( elem )) {
874 // theTria1 must be element with minimum ID
875 if ( theTria2->GetID() < theTria1->GetID() )
876 std::swap( theTria2, theTria1 );
884 //=======================================================================
885 //function : InverseDiag
886 //purpose : Replace two neighbour triangles sharing theNode1-theNode2 link
887 // with ones built on the same 4 nodes but having other common link.
888 // Return false if proper faces not found
889 //=======================================================================
891 bool SMESH_MeshEditor::InverseDiag (const SMDS_MeshNode * theNode1,
892 const SMDS_MeshNode * theNode2)
894 myLastCreatedElems.Clear();
895 myLastCreatedNodes.Clear();
897 const SMDS_MeshElement *tr1, *tr2;
898 if ( !findTriangles( theNode1, theNode2, tr1, tr2 ))
901 const SMDS_VtkFace* F1 = dynamic_cast<const SMDS_VtkFace*>( tr1 );
902 if (!F1) return false;
903 const SMDS_VtkFace* F2 = dynamic_cast<const SMDS_VtkFace*>( tr2 );
904 if (!F2) return false;
905 if ((tr1->GetEntityType() == SMDSEntity_Triangle) &&
906 (tr2->GetEntityType() == SMDSEntity_Triangle)) {
908 // 1 +--+ A tr1: ( 1 A B ) A->2 ( 1 2 B ) 1 +--+ A
909 // | /| tr2: ( B A 2 ) B->1 ( 1 A 2 ) |\ |
913 // put nodes in array
914 // and find indices of 1,2 and of A in tr1 and of B in tr2
915 int i, iA1 = 0, i1 = 0;
916 const SMDS_MeshNode* aNodes1 [3];
917 SMDS_ElemIteratorPtr it;
918 for (i = 0, it = tr1->nodesIterator(); it->more(); i++ ) {
919 aNodes1[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
920 if ( aNodes1[ i ] == theNode1 )
921 iA1 = i; // node A in tr1
922 else if ( aNodes1[ i ] != theNode2 )
926 const SMDS_MeshNode* aNodes2 [3];
927 for (i = 0, it = tr2->nodesIterator(); it->more(); i++ ) {
928 aNodes2[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
929 if ( aNodes2[ i ] == theNode2 )
930 iB2 = i; // node B in tr2
931 else if ( aNodes2[ i ] != theNode1 )
935 // nodes 1 and 2 should not be the same
936 if ( aNodes1[ i1 ] == aNodes2[ i2 ] )
940 aNodes1[ iA1 ] = aNodes2[ i2 ];
942 aNodes2[ iB2 ] = aNodes1[ i1 ];
944 GetMeshDS()->ChangeElementNodes( tr1, aNodes1, 3 );
945 GetMeshDS()->ChangeElementNodes( tr2, aNodes2, 3 );
950 // check case of quadratic faces
951 return InverseDiag(tr1,tr2);
954 //=======================================================================
955 //function : getQuadrangleNodes
956 //purpose : fill theQuadNodes - nodes of a quadrangle resulting from
957 // fusion of triangles tr1 and tr2 having shared link on
958 // theNode1 and theNode2
959 //=======================================================================
961 bool getQuadrangleNodes(const SMDS_MeshNode * theQuadNodes [],
962 const SMDS_MeshNode * theNode1,
963 const SMDS_MeshNode * theNode2,
964 const SMDS_MeshElement * tr1,
965 const SMDS_MeshElement * tr2 )
967 if( tr1->NbNodes() != tr2->NbNodes() )
969 // find the 4-th node to insert into tr1
970 const SMDS_MeshNode* n4 = 0;
971 SMDS_ElemIteratorPtr it = tr2->nodesIterator();
973 while ( !n4 && i<3 ) {
974 const SMDS_MeshNode * n = cast2Node( it->next() );
976 bool isDiag = ( n == theNode1 || n == theNode2 );
980 // Make an array of nodes to be in a quadrangle
981 int iNode = 0, iFirstDiag = -1;
982 it = tr1->nodesIterator();
985 const SMDS_MeshNode * n = cast2Node( it->next() );
987 bool isDiag = ( n == theNode1 || n == theNode2 );
989 if ( iFirstDiag < 0 )
991 else if ( iNode - iFirstDiag == 1 )
992 theQuadNodes[ iNode++ ] = n4; // insert the 4-th node between diagonal nodes
994 else if ( n == n4 ) {
995 return false; // tr1 and tr2 should not have all the same nodes
997 theQuadNodes[ iNode++ ] = n;
999 if ( iNode == 3 ) // diagonal nodes have 0 and 2 indices
1000 theQuadNodes[ iNode ] = n4;
1005 //=======================================================================
1006 //function : DeleteDiag
1007 //purpose : Replace two neighbour triangles sharing theNode1-theNode2 link
1008 // with a quadrangle built on the same 4 nodes.
1009 // Return false if proper faces not found
1010 //=======================================================================
1012 bool SMESH_MeshEditor::DeleteDiag (const SMDS_MeshNode * theNode1,
1013 const SMDS_MeshNode * theNode2)
1015 myLastCreatedElems.Clear();
1016 myLastCreatedNodes.Clear();
1018 const SMDS_MeshElement *tr1, *tr2;
1019 if ( !findTriangles( theNode1, theNode2, tr1, tr2 ))
1022 const SMDS_VtkFace* F1 = dynamic_cast<const SMDS_VtkFace*>( tr1 );
1023 if (!F1) return false;
1024 const SMDS_VtkFace* F2 = dynamic_cast<const SMDS_VtkFace*>( tr2 );
1025 if (!F2) return false;
1026 SMESHDS_Mesh * aMesh = GetMeshDS();
1028 if ((tr1->GetEntityType() == SMDSEntity_Triangle) &&
1029 (tr2->GetEntityType() == SMDSEntity_Triangle)) {
1031 const SMDS_MeshNode* aNodes [ 4 ];
1032 if ( ! getQuadrangleNodes( aNodes, theNode1, theNode2, tr1, tr2 ))
1035 const SMDS_MeshElement* newElem = 0;
1036 newElem = aMesh->AddFace( aNodes[0], aNodes[1], aNodes[2], aNodes[3] );
1037 myLastCreatedElems.Append(newElem);
1038 AddToSameGroups( newElem, tr1, aMesh );
1039 int aShapeId = tr1->getshapeId();
1042 aMesh->SetMeshElementOnShape( newElem, aShapeId );
1044 aMesh->RemoveElement( tr1 );
1045 aMesh->RemoveElement( tr2 );
1050 // check case of quadratic faces
1051 if (tr1->GetEntityType() != SMDSEntity_Quad_Triangle)
1053 if (tr2->GetEntityType() != SMDSEntity_Quad_Triangle)
1057 // 1 +--+--+ 2 tr1: (1 2 4 5 9 7) or (2 4 1 9 7 5) or (4 1 2 7 5 9)
1058 // | /| tr2: (2 3 4 6 8 9) or (3 4 2 8 9 6) or (4 2 3 9 6 8)
1066 vector< const SMDS_MeshNode* > N1;
1067 vector< const SMDS_MeshNode* > N2;
1068 if(!getNodesFromTwoTria(tr1,tr2,N1,N2))
1070 // now we receive following N1 and N2 (using numeration as above image)
1071 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
1072 // i.e. first nodes from both arrays determ new diagonal
1074 const SMDS_MeshNode* aNodes[8];
1084 const SMDS_MeshElement* newElem = 0;
1085 newElem = aMesh->AddFace( aNodes[0], aNodes[1], aNodes[2], aNodes[3],
1086 aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
1087 myLastCreatedElems.Append(newElem);
1088 AddToSameGroups( newElem, tr1, aMesh );
1089 int aShapeId = tr1->getshapeId();
1092 aMesh->SetMeshElementOnShape( newElem, aShapeId );
1094 aMesh->RemoveElement( tr1 );
1095 aMesh->RemoveElement( tr2 );
1097 // remove middle node (9)
1098 GetMeshDS()->RemoveNode( N1[4] );
1103 //=======================================================================
1104 //function : Reorient
1105 //purpose : Reverse theElement orientation
1106 //=======================================================================
1108 bool SMESH_MeshEditor::Reorient (const SMDS_MeshElement * theElem)
1110 myLastCreatedElems.Clear();
1111 myLastCreatedNodes.Clear();
1115 SMDS_ElemIteratorPtr it = theElem->nodesIterator();
1116 if ( !it || !it->more() )
1119 const SMDSAbs_ElementType type = theElem->GetType();
1120 if ( type < SMDSAbs_Edge || type > SMDSAbs_Volume )
1123 const SMDSAbs_EntityType geomType = theElem->GetEntityType();
1124 if ( geomType == SMDSEntity_Polyhedra ) // polyhedron
1126 const SMDS_VtkVolume* aPolyedre =
1127 dynamic_cast<const SMDS_VtkVolume*>( theElem );
1129 MESSAGE("Warning: bad volumic element");
1132 const int nbFaces = aPolyedre->NbFaces();
1133 vector<const SMDS_MeshNode *> poly_nodes;
1134 vector<int> quantities (nbFaces);
1136 // reverse each face of the polyedre
1137 for (int iface = 1; iface <= nbFaces; iface++) {
1138 int inode, nbFaceNodes = aPolyedre->NbFaceNodes(iface);
1139 quantities[iface - 1] = nbFaceNodes;
1141 for (inode = nbFaceNodes; inode >= 1; inode--) {
1142 const SMDS_MeshNode* curNode = aPolyedre->GetFaceNode(iface, inode);
1143 poly_nodes.push_back(curNode);
1146 return GetMeshDS()->ChangePolyhedronNodes( theElem, poly_nodes, quantities );
1148 else // other elements
1150 vector<const SMDS_MeshNode*> nodes( theElem->begin_nodes(), theElem->end_nodes() );
1151 const std::vector<int>& interlace = SMDS_MeshCell::reverseSmdsOrder( geomType, nodes.size() );
1152 if ( interlace.empty() )
1154 std::reverse( nodes.begin(), nodes.end() ); // obsolete, just in case
1158 SMDS_MeshCell::applyInterlace( interlace, nodes );
1160 return GetMeshDS()->ChangeElementNodes( theElem, &nodes[0], nodes.size() );
1165 //================================================================================
1167 * \brief Reorient faces.
1168 * \param theFaces - the faces to reorient. If empty the whole mesh is meant
1169 * \param theDirection - desired direction of normal of \a theFace
1170 * \param theFace - one of \a theFaces that should be oriented according to
1171 * \a theDirection and whose orientation defines orientation of other faces
1172 * \return number of reoriented faces.
1174 //================================================================================
1176 int SMESH_MeshEditor::Reorient2D (TIDSortedElemSet & theFaces,
1177 const gp_Dir& theDirection,
1178 const SMDS_MeshElement * theFace)
1181 if ( !theFace || theFace->GetType() != SMDSAbs_Face ) return nbReori;
1183 if ( theFaces.empty() )
1185 SMDS_FaceIteratorPtr fIt = GetMeshDS()->facesIterator(/*idInceasingOrder=*/true);
1186 while ( fIt->more() )
1187 theFaces.insert( theFaces.end(), fIt->next() );
1190 // orient theFace according to theDirection
1192 SMESH_MeshAlgos::FaceNormal( theFace, normal, /*normalized=*/false );
1193 if ( normal * theDirection.XYZ() < 0 )
1194 nbReori += Reorient( theFace );
1196 // Orient other faces
1198 set< const SMDS_MeshElement* > startFaces, visitedFaces;
1199 TIDSortedElemSet avoidSet;
1200 set< SMESH_TLink > checkedLinks;
1201 pair< set< SMESH_TLink >::iterator, bool > linkIt_isNew;
1203 if ( theFaces.size() > 1 )// leave 1 face to prevent finding not selected faces
1204 theFaces.erase( theFace );
1205 startFaces.insert( theFace );
1207 int nodeInd1, nodeInd2;
1208 const SMDS_MeshElement* otherFace;
1209 vector< const SMDS_MeshElement* > facesNearLink;
1210 vector< std::pair< int, int > > nodeIndsOfFace;
1212 set< const SMDS_MeshElement* >::iterator startFace = startFaces.begin();
1213 while ( !startFaces.empty() )
1215 startFace = startFaces.begin();
1216 theFace = *startFace;
1217 startFaces.erase( startFace );
1218 if ( !visitedFaces.insert( theFace ).second )
1222 avoidSet.insert(theFace);
1224 NLink link( theFace->GetNode( 0 ), (SMDS_MeshNode *) 0 );
1226 const int nbNodes = theFace->NbCornerNodes();
1227 for ( int i = 0; i < nbNodes; ++i ) // loop on links of theFace
1229 link.second = theFace->GetNode(( i+1 ) % nbNodes );
1230 linkIt_isNew = checkedLinks.insert( link );
1231 if ( !linkIt_isNew.second )
1233 // link has already been checked and won't be encountered more
1234 // if the group (theFaces) is manifold
1235 //checkedLinks.erase( linkIt_isNew.first );
1239 facesNearLink.clear();
1240 nodeIndsOfFace.clear();
1241 while (( otherFace = SMESH_MeshAlgos::FindFaceInSet( link.first, link.second,
1243 &nodeInd1, &nodeInd2 )))
1244 if ( otherFace != theFace)
1246 facesNearLink.push_back( otherFace );
1247 nodeIndsOfFace.push_back( make_pair( nodeInd1, nodeInd2 ));
1248 avoidSet.insert( otherFace );
1250 if ( facesNearLink.size() > 1 )
1252 // NON-MANIFOLD mesh shell !
1253 // select a face most co-directed with theFace,
1254 // other faces won't be visited this time
1256 SMESH_MeshAlgos::FaceNormal( theFace, NF, /*normalized=*/false );
1257 double proj, maxProj = -1;
1258 for ( size_t i = 0; i < facesNearLink.size(); ++i ) {
1259 SMESH_MeshAlgos::FaceNormal( facesNearLink[i], NOF, /*normalized=*/false );
1260 if (( proj = Abs( NF * NOF )) > maxProj ) {
1262 otherFace = facesNearLink[i];
1263 nodeInd1 = nodeIndsOfFace[i].first;
1264 nodeInd2 = nodeIndsOfFace[i].second;
1267 // not to visit rejected faces
1268 for ( size_t i = 0; i < facesNearLink.size(); ++i )
1269 if ( facesNearLink[i] != otherFace && theFaces.size() > 1 )
1270 visitedFaces.insert( facesNearLink[i] );
1272 else if ( facesNearLink.size() == 1 )
1274 otherFace = facesNearLink[0];
1275 nodeInd1 = nodeIndsOfFace.back().first;
1276 nodeInd2 = nodeIndsOfFace.back().second;
1278 if ( otherFace && otherFace != theFace)
1280 // link must be reverse in otherFace if orientation ot otherFace
1281 // is same as that of theFace
1282 if ( abs(nodeInd2-nodeInd1) == 1 ? nodeInd2 > nodeInd1 : nodeInd1 > nodeInd2 )
1284 nbReori += Reorient( otherFace );
1286 startFaces.insert( otherFace );
1289 std::swap( link.first, link.second ); // reverse the link
1295 //================================================================================
1297 * \brief Reorient faces basing on orientation of adjacent volumes.
1298 * \param theFaces - faces to reorient. If empty, all mesh faces are treated.
1299 * \param theVolumes - reference volumes.
1300 * \param theOutsideNormal - to orient faces to have their normal
1301 * pointing either \a outside or \a inside the adjacent volumes.
1302 * \return number of reoriented faces.
1304 //================================================================================
1306 int SMESH_MeshEditor::Reorient2DBy3D (TIDSortedElemSet & theFaces,
1307 TIDSortedElemSet & theVolumes,
1308 const bool theOutsideNormal)
1312 SMDS_ElemIteratorPtr faceIt;
1313 if ( theFaces.empty() )
1314 faceIt = GetMeshDS()->elementsIterator( SMDSAbs_Face );
1316 faceIt = elemSetIterator( theFaces );
1318 vector< const SMDS_MeshNode* > faceNodes;
1319 TIDSortedElemSet checkedVolumes;
1320 set< const SMDS_MeshNode* > faceNodesSet;
1321 SMDS_VolumeTool volumeTool;
1323 while ( faceIt->more() ) // loop on given faces
1325 const SMDS_MeshElement* face = faceIt->next();
1326 if ( face->GetType() != SMDSAbs_Face )
1329 const size_t nbCornersNodes = face->NbCornerNodes();
1330 faceNodes.assign( face->begin_nodes(), face->end_nodes() );
1332 checkedVolumes.clear();
1333 SMDS_ElemIteratorPtr vIt = faceNodes[ 0 ]->GetInverseElementIterator( SMDSAbs_Volume );
1334 while ( vIt->more() )
1336 const SMDS_MeshElement* volume = vIt->next();
1338 if ( !checkedVolumes.insert( volume ).second )
1340 if ( !theVolumes.empty() && !theVolumes.count( volume ))
1343 // is volume adjacent?
1344 bool allNodesCommon = true;
1345 for ( size_t iN = 1; iN < nbCornersNodes && allNodesCommon; ++iN )
1346 allNodesCommon = ( volume->GetNodeIndex( faceNodes[ iN ]) > -1 );
1347 if ( !allNodesCommon )
1350 // get nodes of a corresponding volume facet
1351 faceNodesSet.clear();
1352 faceNodesSet.insert( faceNodes.begin(), faceNodes.end() );
1353 volumeTool.Set( volume );
1354 int facetID = volumeTool.GetFaceIndex( faceNodesSet );
1355 if ( facetID < 0 ) continue;
1356 volumeTool.SetExternalNormal();
1357 const SMDS_MeshNode** facetNodes = volumeTool.GetFaceNodes( facetID );
1359 // compare order of faceNodes and facetNodes
1360 const int iQ = 1 + ( nbCornersNodes < faceNodes.size() );
1362 for ( int i = 0; i < 2; ++i )
1364 const SMDS_MeshNode* n = facetNodes[ i*iQ ];
1365 for ( size_t iN = 0; iN < nbCornersNodes; ++iN )
1366 if ( faceNodes[ iN ] == n )
1372 bool isOutside = Abs( iNN[0]-iNN[1] ) == 1 ? iNN[0] < iNN[1] : iNN[0] > iNN[1];
1373 if ( isOutside != theOutsideNormal )
1374 nbReori += Reorient( face );
1376 } // loop on given faces
1381 //=======================================================================
1382 //function : getBadRate
1384 //=======================================================================
1386 static double getBadRate (const SMDS_MeshElement* theElem,
1387 SMESH::Controls::NumericalFunctorPtr& theCrit)
1389 SMESH::Controls::TSequenceOfXYZ P;
1390 if ( !theElem || !theCrit->GetPoints( theElem, P ))
1392 return theCrit->GetBadRate( theCrit->GetValue( P ), theElem->NbNodes() );
1393 //return theCrit->GetBadRate( theCrit->GetValue( theElem->GetID() ), theElem->NbNodes() );
1396 //=======================================================================
1397 //function : QuadToTri
1398 //purpose : Cut quadrangles into triangles.
1399 // theCrit is used to select a diagonal to cut
1400 //=======================================================================
1402 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet & theElems,
1403 SMESH::Controls::NumericalFunctorPtr theCrit)
1405 myLastCreatedElems.Clear();
1406 myLastCreatedNodes.Clear();
1408 if ( !theCrit.get() )
1411 SMESHDS_Mesh * aMesh = GetMeshDS();
1413 Handle(Geom_Surface) surface;
1414 SMESH_MesherHelper helper( *GetMesh() );
1416 TIDSortedElemSet::iterator itElem;
1417 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
1419 const SMDS_MeshElement* elem = *itElem;
1420 if ( !elem || elem->GetType() != SMDSAbs_Face )
1422 if ( elem->NbCornerNodes() != 4 )
1425 // retrieve element nodes
1426 vector< const SMDS_MeshNode* > aNodes( elem->begin_nodes(), elem->end_nodes() );
1428 // compare two sets of possible triangles
1429 double aBadRate1, aBadRate2; // to what extent a set is bad
1430 SMDS_FaceOfNodes tr1 ( aNodes[0], aNodes[1], aNodes[2] );
1431 SMDS_FaceOfNodes tr2 ( aNodes[2], aNodes[3], aNodes[0] );
1432 aBadRate1 = getBadRate( &tr1, theCrit ) + getBadRate( &tr2, theCrit );
1434 SMDS_FaceOfNodes tr3 ( aNodes[1], aNodes[2], aNodes[3] );
1435 SMDS_FaceOfNodes tr4 ( aNodes[3], aNodes[0], aNodes[1] );
1436 aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit );
1438 const int aShapeId = FindShape( elem );
1439 const SMDS_MeshElement* newElem1 = 0;
1440 const SMDS_MeshElement* newElem2 = 0;
1442 if ( !elem->IsQuadratic() ) // split liner quadrangle
1444 // for MaxElementLength2D functor we return minimum diagonal for splitting,
1445 // because aBadRate1=2*len(diagonal 1-3); aBadRate2=2*len(diagonal 2-4)
1446 if ( aBadRate1 <= aBadRate2 ) {
1447 // tr1 + tr2 is better
1448 newElem1 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
1449 newElem2 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
1452 // tr3 + tr4 is better
1453 newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
1454 newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
1457 else // split quadratic quadrangle
1459 helper.SetIsQuadratic( true );
1460 helper.SetIsBiQuadratic( aNodes.size() == 9 );
1462 helper.AddTLinks( static_cast< const SMDS_MeshFace* >( elem ));
1463 if ( aNodes.size() == 9 )
1465 helper.SetIsBiQuadratic( true );
1466 if ( aBadRate1 <= aBadRate2 )
1467 helper.AddTLinkNode( aNodes[0], aNodes[2], aNodes[8] );
1469 helper.AddTLinkNode( aNodes[1], aNodes[3], aNodes[8] );
1471 // create a new element
1472 if ( aBadRate1 <= aBadRate2 ) {
1473 newElem1 = helper.AddFace( aNodes[2], aNodes[3], aNodes[0] );
1474 newElem2 = helper.AddFace( aNodes[2], aNodes[0], aNodes[1] );
1477 newElem1 = helper.AddFace( aNodes[3], aNodes[0], aNodes[1] );
1478 newElem2 = helper.AddFace( aNodes[3], aNodes[1], aNodes[2] );
1482 // care of a new element
1484 myLastCreatedElems.Append(newElem1);
1485 myLastCreatedElems.Append(newElem2);
1486 AddToSameGroups( newElem1, elem, aMesh );
1487 AddToSameGroups( newElem2, elem, aMesh );
1489 // put a new triangle on the same shape
1491 aMesh->SetMeshElementOnShape( newElem1, aShapeId );
1492 aMesh->SetMeshElementOnShape( newElem2, aShapeId );
1494 aMesh->RemoveElement( elem );
1499 //=======================================================================
1501 * \brief Split each of given quadrangles into 4 triangles.
1502 * \param theElems - The faces to be splitted. If empty all faces are split.
1504 //=======================================================================
1506 void SMESH_MeshEditor::QuadTo4Tri (TIDSortedElemSet & theElems)
1508 myLastCreatedElems.Clear();
1509 myLastCreatedNodes.Clear();
1511 SMESH_MesherHelper helper( *GetMesh() );
1512 helper.SetElementsOnShape( true );
1514 SMDS_ElemIteratorPtr faceIt;
1515 if ( theElems.empty() ) faceIt = GetMeshDS()->elementsIterator(SMDSAbs_Face);
1516 else faceIt = elemSetIterator( theElems );
1519 gp_XY uv [9]; uv[8] = gp_XY(0,0);
1521 vector< const SMDS_MeshNode* > nodes;
1522 SMESHDS_SubMesh* subMeshDS = 0;
1524 Handle(Geom_Surface) surface;
1525 TopLoc_Location loc;
1527 while ( faceIt->more() )
1529 const SMDS_MeshElement* quad = faceIt->next();
1530 if ( !quad || quad->NbCornerNodes() != 4 )
1533 // get a surface the quad is on
1535 if ( quad->getshapeId() < 1 )
1538 helper.SetSubShape( 0 );
1541 else if ( quad->getshapeId() != helper.GetSubShapeID() )
1543 helper.SetSubShape( quad->getshapeId() );
1544 if ( !helper.GetSubShape().IsNull() &&
1545 helper.GetSubShape().ShapeType() == TopAbs_FACE )
1547 F = TopoDS::Face( helper.GetSubShape() );
1548 surface = BRep_Tool::Surface( F, loc );
1549 subMeshDS = GetMeshDS()->MeshElements( quad->getshapeId() );
1553 helper.SetSubShape( 0 );
1558 // create a central node
1560 const SMDS_MeshNode* nCentral;
1561 nodes.assign( quad->begin_nodes(), quad->end_nodes() );
1563 if ( nodes.size() == 9 )
1565 nCentral = nodes.back();
1572 for ( ; iN < nodes.size(); ++iN )
1573 xyz[ iN ] = SMESH_TNodeXYZ( nodes[ iN ] );
1575 for ( ; iN < 8; ++iN ) // mid-side points of a linear qudrangle
1576 xyz[ iN ] = 0.5 * ( xyz[ iN - 4 ] + xyz[( iN - 3 )%4 ] );
1578 xyz[ 8 ] = helper.calcTFI( 0.5, 0.5,
1579 xyz[0], xyz[1], xyz[2], xyz[3],
1580 xyz[4], xyz[5], xyz[6], xyz[7] );
1584 for ( ; iN < nodes.size(); ++iN )
1585 uv[ iN ] = helper.GetNodeUV( F, nodes[iN], nodes[(iN+2)%4], &checkUV );
1587 for ( ; iN < 8; ++iN ) // UV of mid-side points of a linear qudrangle
1588 uv[ iN ] = helper.GetMiddleUV( surface, uv[ iN - 4 ], uv[( iN - 3 )%4 ] );
1590 uv[ 8 ] = helper.calcTFI( 0.5, 0.5,
1591 uv[0], uv[1], uv[2], uv[3],
1592 uv[4], uv[5], uv[6], uv[7] );
1594 gp_Pnt p = surface->Value( uv[8].X(), uv[8].Y() ).Transformed( loc );
1598 nCentral = helper.AddNode( xyz[8].X(), xyz[8].Y(), xyz[8].Z(), /*id=*/0,
1599 uv[8].X(), uv[8].Y() );
1600 myLastCreatedNodes.Append( nCentral );
1603 // create 4 triangles
1605 helper.SetIsQuadratic ( nodes.size() > 4 );
1606 helper.SetIsBiQuadratic( nodes.size() == 9 );
1607 if ( helper.GetIsQuadratic() )
1608 helper.AddTLinks( static_cast< const SMDS_MeshFace*>( quad ));
1610 GetMeshDS()->RemoveFreeElement( quad, subMeshDS, /*fromGroups=*/false );
1612 for ( int i = 0; i < 4; ++i )
1614 SMDS_MeshElement* tria = helper.AddFace( nodes[ i ],
1617 ReplaceElemInGroups( tria, quad, GetMeshDS() );
1618 myLastCreatedElems.Append( tria );
1623 //=======================================================================
1624 //function : BestSplit
1625 //purpose : Find better diagonal for cutting.
1626 //=======================================================================
1628 int SMESH_MeshEditor::BestSplit (const SMDS_MeshElement* theQuad,
1629 SMESH::Controls::NumericalFunctorPtr theCrit)
1631 myLastCreatedElems.Clear();
1632 myLastCreatedNodes.Clear();
1637 if (!theQuad || theQuad->GetType() != SMDSAbs_Face )
1640 if( theQuad->NbNodes()==4 ||
1641 (theQuad->NbNodes()==8 && theQuad->IsQuadratic()) ) {
1643 // retrieve element nodes
1644 const SMDS_MeshNode* aNodes [4];
1645 SMDS_ElemIteratorPtr itN = theQuad->nodesIterator();
1647 //while (itN->more())
1649 aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
1651 // compare two sets of possible triangles
1652 double aBadRate1, aBadRate2; // to what extent a set is bad
1653 SMDS_FaceOfNodes tr1 ( aNodes[0], aNodes[1], aNodes[2] );
1654 SMDS_FaceOfNodes tr2 ( aNodes[2], aNodes[3], aNodes[0] );
1655 aBadRate1 = getBadRate( &tr1, theCrit ) + getBadRate( &tr2, theCrit );
1657 SMDS_FaceOfNodes tr3 ( aNodes[1], aNodes[2], aNodes[3] );
1658 SMDS_FaceOfNodes tr4 ( aNodes[3], aNodes[0], aNodes[1] );
1659 aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit );
1660 // for MaxElementLength2D functor we return minimum diagonal for splitting,
1661 // because aBadRate1=2*len(diagonal 1-3); aBadRate2=2*len(diagonal 2-4)
1662 if (aBadRate1 <= aBadRate2) // tr1 + tr2 is better
1663 return 1; // diagonal 1-3
1665 return 2; // diagonal 2-4
1672 // Methods of splitting volumes into tetra
1674 const int theHexTo5_1[5*4+1] =
1676 0, 1, 2, 5, 0, 4, 5, 7, 0, 2, 3, 7, 2, 5, 6, 7, 0, 5, 2, 7, -1
1678 const int theHexTo5_2[5*4+1] =
1680 1, 2, 3, 6, 1, 4, 5, 6, 0, 1, 3, 4, 3, 4, 6, 7, 1, 3, 4, 6, -1
1682 const int* theHexTo5[2] = { theHexTo5_1, theHexTo5_2 };
1684 const int theHexTo6_1[6*4+1] =
1686 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
1688 const int theHexTo6_2[6*4+1] =
1690 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
1692 const int theHexTo6_3[6*4+1] =
1694 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
1696 const int theHexTo6_4[6*4+1] =
1698 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
1700 const int* theHexTo6[4] = { theHexTo6_1, theHexTo6_2, theHexTo6_3, theHexTo6_4 };
1702 const int thePyraTo2_1[2*4+1] =
1704 0, 1, 2, 4, 0, 2, 3, 4, -1
1706 const int thePyraTo2_2[2*4+1] =
1708 1, 2, 3, 4, 1, 3, 0, 4, -1
1710 const int* thePyraTo2[2] = { thePyraTo2_1, thePyraTo2_2 };
1712 const int thePentaTo3_1[3*4+1] =
1714 0, 1, 2, 3, 1, 3, 4, 2, 2, 3, 4, 5, -1
1716 const int thePentaTo3_2[3*4+1] =
1718 1, 2, 0, 4, 2, 4, 5, 0, 0, 4, 5, 3, -1
1720 const int thePentaTo3_3[3*4+1] =
1722 2, 0, 1, 5, 0, 5, 3, 1, 1, 5, 3, 4, -1
1724 const int thePentaTo3_4[3*4+1] =
1726 0, 1, 2, 3, 1, 3, 4, 5, 2, 3, 1, 5, -1
1728 const int thePentaTo3_5[3*4+1] =
1730 1, 2, 0, 4, 2, 4, 5, 3, 0, 4, 2, 3, -1
1732 const int thePentaTo3_6[3*4+1] =
1734 2, 0, 1, 5, 0, 5, 3, 4, 1, 5, 0, 4, -1
1736 const int* thePentaTo3[6] = { thePentaTo3_1, thePentaTo3_2, thePentaTo3_3,
1737 thePentaTo3_4, thePentaTo3_5, thePentaTo3_6 };
1739 // Methods of splitting hexahedron into prisms
1741 const int theHexTo4Prisms_BT[6*4+1] = // bottom-top
1743 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
1745 const int theHexTo4Prisms_LR[6*4+1] = // left-right
1747 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
1749 const int theHexTo4Prisms_FB[6*4+1] = // front-back
1751 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
1754 const int theHexTo2Prisms_BT_1[6*2+1] =
1756 0, 1, 3, 4, 5, 7, 1, 2, 3, 5, 6, 7, -1
1758 const int theHexTo2Prisms_BT_2[6*2+1] =
1760 0, 1, 2, 4, 5, 6, 0, 2, 3, 4, 6, 7, -1
1762 const int* theHexTo2Prisms_BT[2] = { theHexTo2Prisms_BT_1, theHexTo2Prisms_BT_2 };
1764 const int theHexTo2Prisms_LR_1[6*2+1] =
1766 1, 0, 4, 2, 3, 7, 1, 4, 5, 2, 7, 6, -1
1768 const int theHexTo2Prisms_LR_2[6*2+1] =
1770 1, 0, 4, 2, 3, 7, 1, 4, 5, 2, 7, 6, -1
1772 const int* theHexTo2Prisms_LR[2] = { theHexTo2Prisms_LR_1, theHexTo2Prisms_LR_2 };
1774 const int theHexTo2Prisms_FB_1[6*2+1] =
1776 0, 3, 4, 1, 2, 5, 3, 7, 4, 2, 6, 5, -1
1778 const int theHexTo2Prisms_FB_2[6*2+1] =
1780 0, 3, 7, 1, 2, 7, 0, 7, 4, 1, 6, 5, -1
1782 const int* theHexTo2Prisms_FB[2] = { theHexTo2Prisms_FB_1, theHexTo2Prisms_FB_2 };
1785 struct TTriangleFacet //!< stores indices of three nodes of tetra facet
1788 TTriangleFacet(int n1, int n2, int n3): _n1(n1), _n2(n2), _n3(n3) {}
1789 bool contains(int n) const { return ( n == _n1 || n == _n2 || n == _n3 ); }
1790 bool hasAdjacentVol( const SMDS_MeshElement* elem,
1791 const SMDSAbs_GeometryType geom = SMDSGeom_TETRA) const;
1797 const int* _connectivity; //!< foursomes of tetra connectivy finished by -1
1798 bool _baryNode; //!< additional node is to be created at cell barycenter
1799 bool _ownConn; //!< to delete _connectivity in destructor
1800 map<int, const SMDS_MeshNode*> _faceBaryNode; //!< map face index to node at BC of face
1802 TSplitMethod( int nbTet=0, const int* conn=0, bool addNode=false)
1803 : _nbSplits(nbTet), _nbCorners(4), _connectivity(conn), _baryNode(addNode), _ownConn(false) {}
1804 ~TSplitMethod() { if ( _ownConn ) delete [] _connectivity; _connectivity = 0; }
1805 bool hasFacet( const TTriangleFacet& facet ) const
1807 if ( _nbCorners == 4 )
1809 const int* tetConn = _connectivity;
1810 for ( ; tetConn[0] >= 0; tetConn += 4 )
1811 if (( facet.contains( tetConn[0] ) +
1812 facet.contains( tetConn[1] ) +
1813 facet.contains( tetConn[2] ) +
1814 facet.contains( tetConn[3] )) == 3 )
1817 else // prism, _nbCorners == 6
1819 const int* prismConn = _connectivity;
1820 for ( ; prismConn[0] >= 0; prismConn += 6 )
1822 if (( facet.contains( prismConn[0] ) &&
1823 facet.contains( prismConn[1] ) &&
1824 facet.contains( prismConn[2] ))
1826 ( facet.contains( prismConn[3] ) &&
1827 facet.contains( prismConn[4] ) &&
1828 facet.contains( prismConn[5] )))
1836 //=======================================================================
1838 * \brief return TSplitMethod for the given element to split into tetrahedra
1840 //=======================================================================
1842 TSplitMethod getTetraSplitMethod( SMDS_VolumeTool& vol, const int theMethodFlags)
1844 const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
1846 // at HEXA_TO_24 method, each face of volume is split into triangles each based on
1847 // an edge and a face barycenter; tertaherdons are based on triangles and
1848 // a volume barycenter
1849 const bool is24TetMode = ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_24 );
1851 // Find out how adjacent volumes are split
1853 vector < list< TTriangleFacet > > triaSplitsByFace( vol.NbFaces() ); // splits of each side
1854 int hasAdjacentSplits = 0, maxTetConnSize = 0;
1855 for ( int iF = 0; iF < vol.NbFaces(); ++iF )
1857 int nbNodes = vol.NbFaceNodes( iF ) / iQ;
1858 maxTetConnSize += 4 * ( nbNodes - (is24TetMode ? 0 : 2));
1859 if ( nbNodes < 4 ) continue;
1861 list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
1862 const int* nInd = vol.GetFaceNodesIndices( iF );
1865 TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
1866 TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
1867 if ( t012.hasAdjacentVol( vol.Element() )) triaSplits.push_back( t012 );
1868 else if ( t123.hasAdjacentVol( vol.Element() )) triaSplits.push_back( t123 );
1872 int iCom = 0; // common node of triangle faces to split into
1873 for ( int iVar = 0; iVar < nbNodes; ++iVar, ++iCom )
1875 TTriangleFacet t012( nInd[ iQ * ( iCom )],
1876 nInd[ iQ * ( (iCom+1)%nbNodes )],
1877 nInd[ iQ * ( (iCom+2)%nbNodes )]);
1878 TTriangleFacet t023( nInd[ iQ * ( iCom )],
1879 nInd[ iQ * ( (iCom+2)%nbNodes )],
1880 nInd[ iQ * ( (iCom+3)%nbNodes )]);
1881 if ( t012.hasAdjacentVol( vol.Element() ) && t023.hasAdjacentVol( vol.Element() ))
1883 triaSplits.push_back( t012 );
1884 triaSplits.push_back( t023 );
1889 if ( !triaSplits.empty() )
1890 hasAdjacentSplits = true;
1893 // Among variants of split method select one compliant with adjacent volumes
1895 TSplitMethod method;
1896 if ( !vol.Element()->IsPoly() && !is24TetMode )
1898 int nbVariants = 2, nbTet = 0;
1899 const int** connVariants = 0;
1900 switch ( vol.Element()->GetEntityType() )
1902 case SMDSEntity_Hexa:
1903 case SMDSEntity_Quad_Hexa:
1904 case SMDSEntity_TriQuad_Hexa:
1905 if ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_5 )
1906 connVariants = theHexTo5, nbTet = 5;
1908 connVariants = theHexTo6, nbTet = 6, nbVariants = 4;
1910 case SMDSEntity_Pyramid:
1911 case SMDSEntity_Quad_Pyramid:
1912 connVariants = thePyraTo2; nbTet = 2;
1914 case SMDSEntity_Penta:
1915 case SMDSEntity_Quad_Penta:
1916 case SMDSEntity_BiQuad_Penta:
1917 connVariants = thePentaTo3; nbTet = 3; nbVariants = 6;
1922 for ( int variant = 0; variant < nbVariants && method._nbSplits == 0; ++variant )
1924 // check method compliancy with adjacent tetras,
1925 // all found splits must be among facets of tetras described by this method
1926 method = TSplitMethod( nbTet, connVariants[variant] );
1927 if ( hasAdjacentSplits && method._nbSplits > 0 )
1929 bool facetCreated = true;
1930 for ( size_t iF = 0; facetCreated && iF < triaSplitsByFace.size(); ++iF )
1932 list< TTriangleFacet >::const_iterator facet = triaSplitsByFace[iF].begin();
1933 for ( ; facetCreated && facet != triaSplitsByFace[iF].end(); ++facet )
1934 facetCreated = method.hasFacet( *facet );
1936 if ( !facetCreated )
1937 method = TSplitMethod(0); // incompatible method
1941 if ( method._nbSplits < 1 )
1943 // No standard method is applicable, use a generic solution:
1944 // each facet of a volume is split into triangles and
1945 // each of triangles and a volume barycenter form a tetrahedron.
1947 const bool isHex27 = ( vol.Element()->GetEntityType() == SMDSEntity_TriQuad_Hexa );
1949 int* connectivity = new int[ maxTetConnSize + 1 ];
1950 method._connectivity = connectivity;
1951 method._ownConn = true;
1952 method._baryNode = !isHex27; // to create central node or not
1955 int baryCenInd = vol.NbNodes() - int( isHex27 );
1956 for ( int iF = 0; iF < vol.NbFaces(); ++iF )
1958 const int nbNodes = vol.NbFaceNodes( iF ) / iQ;
1959 const int* nInd = vol.GetFaceNodesIndices( iF );
1960 // find common node of triangle facets of tetra to create
1961 int iCommon = 0; // index in linear numeration
1962 const list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
1963 if ( !triaSplits.empty() )
1966 const TTriangleFacet* facet = &triaSplits.front();
1967 for ( ; iCommon < nbNodes-1 ; ++iCommon )
1968 if ( facet->contains( nInd[ iQ * iCommon ]) &&
1969 facet->contains( nInd[ iQ * ((iCommon+2)%nbNodes) ]))
1972 else if ( nbNodes > 3 && !is24TetMode )
1974 // find the best method of splitting into triangles by aspect ratio
1975 SMESH::Controls::NumericalFunctorPtr aspectRatio( new SMESH::Controls::AspectRatio);
1976 map< double, int > badness2iCommon;
1977 const SMDS_MeshNode** nodes = vol.GetFaceNodes( iF );
1978 int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
1979 for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCommon )
1982 for ( int iLast = iCommon+2; iLast < iCommon+nbNodes; ++iLast )
1984 SMDS_FaceOfNodes tria ( nodes[ iQ*( iCommon )],
1985 nodes[ iQ*((iLast-1)%nbNodes)],
1986 nodes[ iQ*((iLast )%nbNodes)]);
1987 badness += getBadRate( &tria, aspectRatio );
1989 badness2iCommon.insert( make_pair( badness, iCommon ));
1991 // use iCommon with lowest badness
1992 iCommon = badness2iCommon.begin()->second;
1994 if ( iCommon >= nbNodes )
1995 iCommon = 0; // something wrong
1997 // fill connectivity of tetrahedra based on a current face
1998 int nbTet = nbNodes - 2;
1999 if ( is24TetMode && nbNodes > 3 && triaSplits.empty())
2004 faceBaryCenInd = vol.GetCenterNodeIndex( iF );
2005 method._faceBaryNode[ iF ] = vol.GetNodes()[ faceBaryCenInd ];
2009 method._faceBaryNode[ iF ] = 0;
2010 faceBaryCenInd = baryCenInd + method._faceBaryNode.size();
2013 for ( int i = 0; i < nbTet; ++i )
2015 int i1 = i, i2 = (i+1) % nbNodes;
2016 if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
2017 connectivity[ connSize++ ] = nInd[ iQ * i1 ];
2018 connectivity[ connSize++ ] = nInd[ iQ * i2 ];
2019 connectivity[ connSize++ ] = faceBaryCenInd;
2020 connectivity[ connSize++ ] = baryCenInd;
2025 for ( int i = 0; i < nbTet; ++i )
2027 int i1 = (iCommon+1+i) % nbNodes, i2 = (iCommon+2+i) % nbNodes;
2028 if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
2029 connectivity[ connSize++ ] = nInd[ iQ * iCommon ];
2030 connectivity[ connSize++ ] = nInd[ iQ * i1 ];
2031 connectivity[ connSize++ ] = nInd[ iQ * i2 ];
2032 connectivity[ connSize++ ] = baryCenInd;
2035 method._nbSplits += nbTet;
2037 } // loop on volume faces
2039 connectivity[ connSize++ ] = -1;
2041 } // end of generic solution
2045 //=======================================================================
2047 * \brief return TSplitMethod to split haxhedron into prisms
2049 //=======================================================================
2051 TSplitMethod getPrismSplitMethod( SMDS_VolumeTool& vol,
2052 const int methodFlags,
2053 const int facetToSplit)
2055 // order of facets in HEX according to SMDS_VolumeTool::Hexa_F :
2057 const int iF = ( facetToSplit < 2 ) ? 0 : 1 + ( facetToSplit-2 ) % 2; // [0,1,2]
2059 if ( methodFlags == SMESH_MeshEditor::HEXA_TO_4_PRISMS )
2061 static TSplitMethod to4methods[4]; // order BT, LR, FB
2062 if ( to4methods[iF]._nbSplits == 0 )
2066 to4methods[iF]._connectivity = theHexTo4Prisms_BT;
2067 to4methods[iF]._faceBaryNode[ 0 ] = 0;
2068 to4methods[iF]._faceBaryNode[ 1 ] = 0;
2071 to4methods[iF]._connectivity = theHexTo4Prisms_LR;
2072 to4methods[iF]._faceBaryNode[ 2 ] = 0;
2073 to4methods[iF]._faceBaryNode[ 4 ] = 0;
2076 to4methods[iF]._connectivity = theHexTo4Prisms_FB;
2077 to4methods[iF]._faceBaryNode[ 3 ] = 0;
2078 to4methods[iF]._faceBaryNode[ 5 ] = 0;
2080 default: return to4methods[3];
2082 to4methods[iF]._nbSplits = 4;
2083 to4methods[iF]._nbCorners = 6;
2085 return to4methods[iF];
2087 // else if ( methodFlags == HEXA_TO_2_PRISMS )
2089 TSplitMethod method;
2091 const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
2093 const int nbVariants = 2, nbSplits = 2;
2094 const int** connVariants = 0;
2096 case 0: connVariants = theHexTo2Prisms_BT; break;
2097 case 1: connVariants = theHexTo2Prisms_LR; break;
2098 case 2: connVariants = theHexTo2Prisms_FB; break;
2099 default: return method;
2102 // look for prisms adjacent via facetToSplit and an opposite one
2103 for ( int is2nd = 0; is2nd < 2; ++is2nd )
2105 int iFacet = is2nd ? vol.GetOppFaceIndexOfHex( facetToSplit ) : facetToSplit;
2106 int nbNodes = vol.NbFaceNodes( iFacet ) / iQ;
2107 if ( nbNodes != 4 ) return method;
2109 const int* nInd = vol.GetFaceNodesIndices( iFacet );
2110 TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
2111 TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
2113 if ( t012.hasAdjacentVol( vol.Element(), SMDSGeom_PENTA ))
2115 else if ( t123.hasAdjacentVol( vol.Element(), SMDSGeom_PENTA ))
2120 // there are adjacent prism
2121 for ( int variant = 0; variant < nbVariants; ++variant )
2123 // check method compliancy with adjacent prisms,
2124 // the found prism facets must be among facets of prisms described by current method
2125 method._nbSplits = nbSplits;
2126 method._nbCorners = 6;
2127 method._connectivity = connVariants[ variant ];
2128 if ( method.hasFacet( *t ))
2133 // No adjacent prisms. Select a variant with a best aspect ratio.
2135 double badness[2] = { 0., 0. };
2136 static SMESH::Controls::NumericalFunctorPtr aspectRatio( new SMESH::Controls::AspectRatio);
2137 const SMDS_MeshNode** nodes = vol.GetNodes();
2138 for ( int variant = 0; variant < nbVariants; ++variant )
2139 for ( int is2nd = 0; is2nd < 2; ++is2nd )
2141 int iFacet = is2nd ? vol.GetOppFaceIndexOfHex( facetToSplit ) : facetToSplit;
2142 const int* nInd = vol.GetFaceNodesIndices( iFacet );
2144 method._connectivity = connVariants[ variant ];
2145 TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
2146 TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
2147 TTriangleFacet* t = ( method.hasFacet( t012 )) ? & t012 : & t123;
2149 SMDS_FaceOfNodes tria ( nodes[ t->_n1 ],
2152 badness[ variant ] += getBadRate( &tria, aspectRatio );
2154 const int iBetter = ( badness[1] < badness[0] && badness[0]-badness[1] > 0.1 * badness[0] );
2156 method._nbSplits = nbSplits;
2157 method._nbCorners = 6;
2158 method._connectivity = connVariants[ iBetter ];
2163 //================================================================================
2165 * \brief Check if there is a tetraherdon adjacent to the given element via this facet
2167 //================================================================================
2169 bool TTriangleFacet::hasAdjacentVol( const SMDS_MeshElement* elem,
2170 const SMDSAbs_GeometryType geom ) const
2172 // find the tetrahedron including the three nodes of facet
2173 const SMDS_MeshNode* n1 = elem->GetNode(_n1);
2174 const SMDS_MeshNode* n2 = elem->GetNode(_n2);
2175 const SMDS_MeshNode* n3 = elem->GetNode(_n3);
2176 SMDS_ElemIteratorPtr volIt1 = n1->GetInverseElementIterator(SMDSAbs_Volume);
2177 while ( volIt1->more() )
2179 const SMDS_MeshElement* v = volIt1->next();
2180 if ( v->GetGeomType() != geom )
2182 const int lastCornerInd = v->NbCornerNodes() - 1;
2183 if ( v->IsQuadratic() && v->GetNodeIndex( n1 ) > lastCornerInd )
2184 continue; // medium node not allowed
2185 const int ind2 = v->GetNodeIndex( n2 );
2186 if ( ind2 < 0 || lastCornerInd < ind2 )
2188 const int ind3 = v->GetNodeIndex( n3 );
2189 if ( ind3 < 0 || lastCornerInd < ind3 )
2196 //=======================================================================
2198 * \brief A key of a face of volume
2200 //=======================================================================
2202 struct TVolumeFaceKey: pair< pair< int, int>, pair< int, int> >
2204 TVolumeFaceKey( SMDS_VolumeTool& vol, int iF )
2206 TIDSortedNodeSet sortedNodes;
2207 const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
2208 int nbNodes = vol.NbFaceNodes( iF );
2209 const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iF );
2210 for ( int i = 0; i < nbNodes; i += iQ )
2211 sortedNodes.insert( fNodes[i] );
2212 TIDSortedNodeSet::iterator n = sortedNodes.begin();
2213 first.first = (*(n++))->GetID();
2214 first.second = (*(n++))->GetID();
2215 second.first = (*(n++))->GetID();
2216 second.second = ( sortedNodes.size() > 3 ) ? (*(n++))->GetID() : 0;
2221 //=======================================================================
2222 //function : SplitVolumes
2223 //purpose : Split volume elements into tetrahedra or prisms.
2224 // If facet ID < 0, element is split into tetrahedra,
2225 // else a hexahedron is split into prisms so that the given facet is
2226 // split into triangles
2227 //=======================================================================
2229 void SMESH_MeshEditor::SplitVolumes (const TFacetOfElem & theElems,
2230 const int theMethodFlags)
2232 SMDS_VolumeTool volTool;
2233 SMESH_MesherHelper helper( *GetMesh()), fHelper(*GetMesh());
2234 fHelper.ToFixNodeParameters( true );
2236 SMESHDS_SubMesh* subMesh = 0;//GetMeshDS()->MeshElements(1);
2237 SMESHDS_SubMesh* fSubMesh = 0;//subMesh;
2239 SMESH_SequenceOfElemPtr newNodes, newElems;
2241 // map face of volume to it's baricenrtic node
2242 map< TVolumeFaceKey, const SMDS_MeshNode* > volFace2BaryNode;
2244 vector<const SMDS_MeshElement* > splitVols;
2246 TFacetOfElem::const_iterator elem2facet = theElems.begin();
2247 for ( ; elem2facet != theElems.end(); ++elem2facet )
2249 const SMDS_MeshElement* elem = elem2facet->first;
2250 const int facetToSplit = elem2facet->second;
2251 if ( elem->GetType() != SMDSAbs_Volume )
2253 const SMDSAbs_EntityType geomType = elem->GetEntityType();
2254 if ( geomType == SMDSEntity_Tetra || geomType == SMDSEntity_Quad_Tetra )
2257 if ( !volTool.Set( elem, /*ignoreCentralNodes=*/false )) continue; // strange...
2259 TSplitMethod splitMethod = ( facetToSplit < 0 ?
2260 getTetraSplitMethod( volTool, theMethodFlags ) :
2261 getPrismSplitMethod( volTool, theMethodFlags, facetToSplit ));
2262 if ( splitMethod._nbSplits < 1 ) continue;
2264 // find submesh to add new tetras to
2265 if ( !subMesh || !subMesh->Contains( elem ))
2267 int shapeID = FindShape( elem );
2268 helper.SetSubShape( shapeID ); // helper will add tetras to the found submesh
2269 subMesh = GetMeshDS()->MeshElements( shapeID );
2272 if ( elem->IsQuadratic() )
2275 // add quadratic links to the helper
2276 for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
2278 const SMDS_MeshNode** fNodes = volTool.GetFaceNodes( iF );
2279 int nbN = volTool.NbFaceNodes( iF ) - bool( volTool.GetCenterNodeIndex(iF) > 0 );
2280 for ( int iN = 0; iN < nbN; iN += iQ )
2281 helper.AddTLinkNode( fNodes[iN], fNodes[iN+2], fNodes[iN+1] );
2283 helper.SetIsQuadratic( true );
2288 helper.SetIsQuadratic( false );
2290 vector<const SMDS_MeshNode*> nodes( volTool.GetNodes(),
2291 volTool.GetNodes() + elem->NbNodes() );
2292 helper.SetElementsOnShape( true );
2293 if ( splitMethod._baryNode )
2295 // make a node at barycenter
2296 volTool.GetBaryCenter( bc[0], bc[1], bc[2] );
2297 SMDS_MeshNode* gcNode = helper.AddNode( bc[0], bc[1], bc[2] );
2298 nodes.push_back( gcNode );
2299 newNodes.Append( gcNode );
2301 if ( !splitMethod._faceBaryNode.empty() )
2303 // make or find baricentric nodes of faces
2304 map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.begin();
2305 for ( ; iF_n != splitMethod._faceBaryNode.end(); ++iF_n )
2307 map< TVolumeFaceKey, const SMDS_MeshNode* >::iterator f_n =
2308 volFace2BaryNode.insert
2309 ( make_pair( TVolumeFaceKey( volTool,iF_n->first ), iF_n->second )).first;
2312 volTool.GetFaceBaryCenter( iF_n->first, bc[0], bc[1], bc[2] );
2313 newNodes.Append( f_n->second = helper.AddNode( bc[0], bc[1], bc[2] ));
2315 nodes.push_back( iF_n->second = f_n->second );
2320 splitVols.resize( splitMethod._nbSplits ); // splits of a volume
2321 const int* volConn = splitMethod._connectivity;
2322 if ( splitMethod._nbCorners == 4 ) // tetra
2323 for ( int i = 0; i < splitMethod._nbSplits; ++i, volConn += splitMethod._nbCorners )
2324 newElems.Append( splitVols[ i ] = helper.AddVolume( nodes[ volConn[0] ],
2325 nodes[ volConn[1] ],
2326 nodes[ volConn[2] ],
2327 nodes[ volConn[3] ]));
2329 for ( int i = 0; i < splitMethod._nbSplits; ++i, volConn += splitMethod._nbCorners )
2330 newElems.Append( splitVols[ i ] = helper.AddVolume( nodes[ volConn[0] ],
2331 nodes[ volConn[1] ],
2332 nodes[ volConn[2] ],
2333 nodes[ volConn[3] ],
2334 nodes[ volConn[4] ],
2335 nodes[ volConn[5] ]));
2337 ReplaceElemInGroups( elem, splitVols, GetMeshDS() );
2339 // Split faces on sides of the split volume
2341 const SMDS_MeshNode** volNodes = volTool.GetNodes();
2342 for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
2344 const int nbNodes = volTool.NbFaceNodes( iF ) / iQ;
2345 if ( nbNodes < 4 ) continue;
2347 // find an existing face
2348 vector<const SMDS_MeshNode*> fNodes( volTool.GetFaceNodes( iF ),
2349 volTool.GetFaceNodes( iF ) + volTool.NbFaceNodes( iF ));
2350 while ( const SMDS_MeshElement* face = GetMeshDS()->FindElement( fNodes, SMDSAbs_Face,
2351 /*noMedium=*/false))
2354 helper.SetElementsOnShape( false );
2355 vector< const SMDS_MeshElement* > triangles;
2357 // find submesh to add new triangles in
2358 if ( !fSubMesh || !fSubMesh->Contains( face ))
2360 int shapeID = FindShape( face );
2361 fSubMesh = GetMeshDS()->MeshElements( shapeID );
2363 map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.find(iF);
2364 if ( iF_n != splitMethod._faceBaryNode.end() )
2366 const SMDS_MeshNode *baryNode = iF_n->second;
2367 for ( int iN = 0; iN < nbNodes*iQ; iN += iQ )
2369 const SMDS_MeshNode* n1 = fNodes[iN];
2370 const SMDS_MeshNode *n2 = fNodes[(iN+iQ)%(nbNodes*iQ)];
2371 const SMDS_MeshNode *n3 = baryNode;
2372 if ( !volTool.IsFaceExternal( iF ))
2374 triangles.push_back( helper.AddFace( n1,n2,n3 ));
2376 if ( fSubMesh ) // update position of the bary node on geometry
2379 subMesh->RemoveNode( baryNode, false );
2380 GetMeshDS()->SetNodeOnFace( baryNode, fSubMesh->GetID() );
2381 const TopoDS_Shape& s = GetMeshDS()->IndexToShape( fSubMesh->GetID() );
2382 if ( !s.IsNull() && s.ShapeType() == TopAbs_FACE )
2384 fHelper.SetSubShape( s );
2385 gp_XY uv( 1e100, 1e100 );
2387 if ( !fHelper.CheckNodeUV( TopoDS::Face( s ), baryNode,
2388 uv, /*tol=*/1e-7, /*force=*/true, distXYZ ) &&
2391 // node is too far from the surface
2392 GetMeshDS()->MoveNode( baryNode, distXYZ[1], distXYZ[2], distXYZ[3] );
2393 const_cast<SMDS_MeshNode*>( baryNode )->SetPosition
2394 ( SMDS_PositionPtr( new SMDS_FacePosition( uv.X(), uv.Y() )));
2401 // among possible triangles create ones described by split method
2402 const int* nInd = volTool.GetFaceNodesIndices( iF );
2403 int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
2404 int iCom = 0; // common node of triangle faces to split into
2405 list< TTriangleFacet > facets;
2406 for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCom )
2408 TTriangleFacet t012( nInd[ iQ * ( iCom )],
2409 nInd[ iQ * ( (iCom+1)%nbNodes )],
2410 nInd[ iQ * ( (iCom+2)%nbNodes )]);
2411 TTriangleFacet t023( nInd[ iQ * ( iCom )],
2412 nInd[ iQ * ( (iCom+2)%nbNodes )],
2413 nInd[ iQ * ( (iCom+3)%nbNodes )]);
2414 if ( splitMethod.hasFacet( t012 ) && splitMethod.hasFacet( t023 ))
2416 facets.push_back( t012 );
2417 facets.push_back( t023 );
2418 for ( int iLast = iCom+4; iLast < iCom+nbNodes; ++iLast )
2419 facets.push_back( TTriangleFacet( nInd[ iQ * ( iCom )],
2420 nInd[ iQ * ((iLast-1)%nbNodes )],
2421 nInd[ iQ * ((iLast )%nbNodes )]));
2425 list< TTriangleFacet >::iterator facet = facets.begin();
2426 if ( facet == facets.end() )
2428 for ( ; facet != facets.end(); ++facet )
2430 if ( !volTool.IsFaceExternal( iF ))
2431 swap( facet->_n2, facet->_n3 );
2432 triangles.push_back( helper.AddFace( volNodes[ facet->_n1 ],
2433 volNodes[ facet->_n2 ],
2434 volNodes[ facet->_n3 ]));
2437 for ( size_t i = 0; i < triangles.size(); ++i )
2439 if ( !triangles[ i ]) continue;
2441 fSubMesh->AddElement( triangles[ i ]);
2442 newElems.Append( triangles[ i ]);
2444 ReplaceElemInGroups( face, triangles, GetMeshDS() );
2445 GetMeshDS()->RemoveFreeElement( face, fSubMesh, /*fromGroups=*/false );
2447 } // while a face based on facet nodes exists
2448 } // loop on volume faces to split them into triangles
2450 GetMeshDS()->RemoveFreeElement( elem, subMesh, /*fromGroups=*/false );
2452 if ( geomType == SMDSEntity_TriQuad_Hexa )
2454 // remove medium nodes that could become free
2455 for ( int i = 20; i < volTool.NbNodes(); ++i )
2456 if ( volNodes[i]->NbInverseElements() == 0 )
2457 GetMeshDS()->RemoveNode( volNodes[i] );
2459 } // loop on volumes to split
2461 myLastCreatedNodes = newNodes;
2462 myLastCreatedElems = newElems;
2465 //=======================================================================
2466 //function : GetHexaFacetsToSplit
2467 //purpose : For hexahedra that will be split into prisms, finds facets to
2468 // split into triangles. Only hexahedra adjacent to the one closest
2469 // to theFacetNormal.Location() are returned.
2470 //param [in,out] theHexas - the hexahedra
2471 //param [in] theFacetNormal - facet normal
2472 //param [out] theFacets - the hexahedra and found facet IDs
2473 //=======================================================================
2475 void SMESH_MeshEditor::GetHexaFacetsToSplit( TIDSortedElemSet& theHexas,
2476 const gp_Ax1& theFacetNormal,
2477 TFacetOfElem & theFacets)
2479 #define THIS_METHOD "SMESH_MeshEditor::GetHexaFacetsToSplit(): "
2481 // Find a hexa closest to the location of theFacetNormal
2483 const SMDS_MeshElement* startHex;
2485 // get SMDS_ElemIteratorPtr on theHexas
2486 typedef const SMDS_MeshElement* TValue;
2487 typedef TIDSortedElemSet::iterator TSetIterator;
2488 typedef SMDS::SimpleAccessor<TValue,TSetIterator> TAccesor;
2489 typedef SMDS_MeshElement::GeomFilter TFilter;
2490 typedef SMDS_SetIterator < TValue, TSetIterator, TAccesor, TFilter > TElemSetIter;
2491 SMDS_ElemIteratorPtr elemIt = SMDS_ElemIteratorPtr
2492 ( new TElemSetIter( theHexas.begin(),
2494 SMDS_MeshElement::GeomFilter( SMDSGeom_HEXA )));
2496 SMESH_ElementSearcher* searcher =
2497 SMESH_MeshAlgos::GetElementSearcher( *myMesh->GetMeshDS(), elemIt );
2499 startHex = searcher->FindClosestTo( theFacetNormal.Location(), SMDSAbs_Volume );
2504 throw SALOME_Exception( THIS_METHOD "startHex not found");
2507 // Select a facet of startHex by theFacetNormal
2509 SMDS_VolumeTool vTool( startHex );
2510 double norm[3], dot, maxDot = 0;
2512 for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2513 if ( vTool.GetFaceNormal( iF, norm[0], norm[1], norm[2] ))
2515 dot = Abs( theFacetNormal.Direction().Dot( gp_Dir( norm[0], norm[1], norm[2] )));
2523 throw SALOME_Exception( THIS_METHOD "facet of startHex not found");
2525 // Fill theFacets starting from facetID of startHex
2527 // facets used for searching of volumes adjacent to already treated ones
2528 typedef pair< TFacetOfElem::iterator, int > TElemFacets;
2529 typedef map< TVolumeFaceKey, TElemFacets > TFacetMap;
2530 TFacetMap facetsToCheck;
2532 set<const SMDS_MeshNode*> facetNodes;
2533 const SMDS_MeshElement* curHex;
2535 const bool allHex = ((int) theHexas.size() == myMesh->NbHexas() );
2539 // move in two directions from startHex via facetID
2540 for ( int is2nd = 0; is2nd < 2; ++is2nd )
2543 int curFacet = facetID;
2544 if ( is2nd ) // do not treat startHex twice
2546 vTool.Set( curHex );
2547 if ( vTool.IsFreeFace( curFacet, &curHex ))
2553 vTool.GetFaceNodes( curFacet, facetNodes );
2554 vTool.Set( curHex );
2555 curFacet = vTool.GetFaceIndex( facetNodes );
2560 // store a facet to split
2561 if ( curHex->GetGeomType() != SMDSGeom_HEXA )
2563 theFacets.insert( make_pair( curHex, -1 ));
2566 if ( !allHex && !theHexas.count( curHex ))
2569 pair< TFacetOfElem::iterator, bool > facetIt2isNew =
2570 theFacets.insert( make_pair( curHex, curFacet ));
2571 if ( !facetIt2isNew.second )
2574 // remember not-to-split facets in facetsToCheck
2575 int oppFacet = vTool.GetOppFaceIndexOfHex( curFacet );
2576 for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2578 if ( iF == curFacet && iF == oppFacet )
2580 TVolumeFaceKey facetKey ( vTool, iF );
2581 TElemFacets elemFacet( facetIt2isNew.first, iF );
2582 pair< TFacetMap::iterator, bool > it2isnew =
2583 facetsToCheck.insert( make_pair( facetKey, elemFacet ));
2584 if ( !it2isnew.second )
2585 facetsToCheck.erase( it2isnew.first ); // adjacent hex already checked
2587 // pass to a volume adjacent via oppFacet
2588 if ( vTool.IsFreeFace( oppFacet, &curHex ))
2594 // get a new curFacet
2595 vTool.GetFaceNodes( oppFacet, facetNodes );
2596 vTool.Set( curHex );
2597 curFacet = vTool.GetFaceIndex( facetNodes, /*hint=*/curFacet );
2600 } // move in two directions from startHex via facetID
2602 // Find a new startHex by facetsToCheck
2606 TFacetMap::iterator fIt = facetsToCheck.begin();
2607 while ( !startHex && fIt != facetsToCheck.end() )
2609 const TElemFacets& elemFacets = fIt->second;
2610 const SMDS_MeshElement* hex = elemFacets.first->first;
2611 int splitFacet = elemFacets.first->second;
2612 int lateralFacet = elemFacets.second;
2613 facetsToCheck.erase( fIt );
2614 fIt = facetsToCheck.begin();
2617 if ( vTool.IsFreeFace( lateralFacet, &curHex ) ||
2618 curHex->GetGeomType() != SMDSGeom_HEXA )
2620 if ( !allHex && !theHexas.count( curHex ))
2625 // find a facet of startHex to split
2627 set<const SMDS_MeshNode*> lateralNodes;
2628 vTool.GetFaceNodes( lateralFacet, lateralNodes );
2629 vTool.GetFaceNodes( splitFacet, facetNodes );
2630 int oppLateralFacet = vTool.GetOppFaceIndexOfHex( lateralFacet );
2631 vTool.Set( startHex );
2632 lateralFacet = vTool.GetFaceIndex( lateralNodes, oppLateralFacet );
2634 // look for a facet of startHex having common nodes with facetNodes
2635 // but not lateralFacet
2636 for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2638 if ( iF == lateralFacet )
2640 int nbCommonNodes = 0;
2641 const SMDS_MeshNode** nn = vTool.GetFaceNodes( iF );
2642 for ( int iN = 0, nbN = vTool.NbFaceNodes( iF ); iN < nbN; ++iN )
2643 nbCommonNodes += facetNodes.count( nn[ iN ]);
2645 if ( nbCommonNodes >= 2 )
2652 throw SALOME_Exception( THIS_METHOD "facet of a new startHex not found");
2654 } // while ( startHex )
2661 //================================================================================
2663 * \brief Selects nodes of several elements according to a given interlace
2664 * \param [in] srcNodes - nodes to select from
2665 * \param [out] tgtNodesVec - array of nodes of several elements to fill in
2666 * \param [in] interlace - indices of nodes for all elements
2667 * \param [in] nbElems - nb of elements
2668 * \param [in] nbNodes - nb of nodes in each element
2669 * \param [in] mesh - the mesh
2670 * \param [out] elemQueue - a list to push elements found by the selected nodes
2671 * \param [in] type - type of elements to look for
2673 //================================================================================
2675 void selectNodes( const vector< const SMDS_MeshNode* >& srcNodes,
2676 vector< const SMDS_MeshNode* >* tgtNodesVec,
2677 const int* interlace,
2680 SMESHDS_Mesh* mesh = 0,
2681 list< const SMDS_MeshElement* >* elemQueue=0,
2682 SMDSAbs_ElementType type=SMDSAbs_All)
2684 for ( int iE = 0; iE < nbElems; ++iE )
2686 vector< const SMDS_MeshNode* >& elemNodes = tgtNodesVec[iE];
2687 const int* select = & interlace[iE*nbNodes];
2688 elemNodes.resize( nbNodes );
2689 for ( int iN = 0; iN < nbNodes; ++iN )
2690 elemNodes[iN] = srcNodes[ select[ iN ]];
2692 const SMDS_MeshElement* e;
2694 for ( int iE = 0; iE < nbElems; ++iE )
2695 if (( e = mesh->FindElement( tgtNodesVec[iE], type, /*noMedium=*/false)))
2696 elemQueue->push_back( e );
2700 //=======================================================================
2702 * Split bi-quadratic elements into linear ones without creation of additional nodes
2703 * - bi-quadratic triangle will be split into 3 linear quadrangles;
2704 * - bi-quadratic quadrangle will be split into 4 linear quadrangles;
2705 * - tri-quadratic hexahedron will be split into 8 linear hexahedra;
2706 * Quadratic elements of lower dimension adjacent to the split bi-quadratic element
2707 * will be split in order to keep the mesh conformal.
2708 * \param elems - elements to split
2710 //=======================================================================
2712 void SMESH_MeshEditor::SplitBiQuadraticIntoLinear(TIDSortedElemSet& theElems)
2714 vector< const SMDS_MeshNode* > elemNodes(27), subNodes[12], splitNodes[8];
2715 vector<const SMDS_MeshElement* > splitElems;
2716 list< const SMDS_MeshElement* > elemQueue;
2717 list< const SMDS_MeshElement* >::iterator elemIt;
2719 SMESHDS_Mesh * mesh = GetMeshDS();
2720 ElemFeatures *elemType, hexaType(SMDSAbs_Volume), quadType(SMDSAbs_Face), segType(SMDSAbs_Edge);
2721 int nbElems, nbNodes;
2723 TIDSortedElemSet::iterator elemSetIt = theElems.begin();
2724 for ( ; elemSetIt != theElems.end(); ++elemSetIt )
2727 elemQueue.push_back( *elemSetIt );
2728 for ( elemIt = elemQueue.begin(); elemIt != elemQueue.end(); ++elemIt )
2730 const SMDS_MeshElement* elem = *elemIt;
2731 switch( elem->GetEntityType() )
2733 case SMDSEntity_TriQuad_Hexa: // HEX27
2735 elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2736 nbElems = nbNodes = 8;
2737 elemType = & hexaType;
2739 // get nodes for new elements
2740 static int vInd[8][8] = {{ 0,8,20,11, 16,21,26,24 },
2741 { 1,9,20,8, 17,22,26,21 },
2742 { 2,10,20,9, 18,23,26,22 },
2743 { 3,11,20,10, 19,24,26,23 },
2744 { 16,21,26,24, 4,12,25,15 },
2745 { 17,22,26,21, 5,13,25,12 },
2746 { 18,23,26,22, 6,14,25,13 },
2747 { 19,24,26,23, 7,15,25,14 }};
2748 selectNodes( elemNodes, & splitNodes[0], &vInd[0][0], nbElems, nbNodes );
2750 // add boundary faces to elemQueue
2751 static int fInd[6][9] = {{ 0,1,2,3, 8,9,10,11, 20 },
2752 { 4,5,6,7, 12,13,14,15, 25 },
2753 { 0,1,5,4, 8,17,12,16, 21 },
2754 { 1,2,6,5, 9,18,13,17, 22 },
2755 { 2,3,7,6, 10,19,14,18, 23 },
2756 { 3,0,4,7, 11,16,15,19, 24 }};
2757 selectNodes( elemNodes, & subNodes[0], &fInd[0][0], 6,9, mesh, &elemQueue, SMDSAbs_Face );
2759 // add boundary segments to elemQueue
2760 static int eInd[12][3] = {{ 0,1,8 }, { 1,2,9 }, { 2,3,10 }, { 3,0,11 },
2761 { 4,5,12}, { 5,6,13}, { 6,7,14 }, { 7,4,15 },
2762 { 0,4,16}, { 1,5,17}, { 2,6,18 }, { 3,7,19 }};
2763 selectNodes( elemNodes, & subNodes[0], &eInd[0][0], 12,3, mesh, &elemQueue, SMDSAbs_Edge );
2766 case SMDSEntity_BiQuad_Triangle: // TRIA7
2768 elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2771 elemType = & quadType;
2773 // get nodes for new elements
2774 static int fInd[3][4] = {{ 0,3,6,5 }, { 1,4,6,3 }, { 2,5,6,4 }};
2775 selectNodes( elemNodes, & splitNodes[0], &fInd[0][0], nbElems, nbNodes );
2777 // add boundary segments to elemQueue
2778 static int eInd[3][3] = {{ 0,1,3 }, { 1,2,4 }, { 2,0,5 }};
2779 selectNodes( elemNodes, & subNodes[0], &eInd[0][0], 3,3, mesh, &elemQueue, SMDSAbs_Edge );
2782 case SMDSEntity_BiQuad_Quadrangle: // QUAD9
2784 elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2787 elemType = & quadType;
2789 // get nodes for new elements
2790 static int fInd[4][4] = {{ 0,4,8,7 }, { 1,5,8,4 }, { 2,6,8,5 }, { 3,7,8,6 }};
2791 selectNodes( elemNodes, & splitNodes[0], &fInd[0][0], nbElems, nbNodes );
2793 // add boundary segments to elemQueue
2794 static int eInd[4][3] = {{ 0,1,4 }, { 1,2,5 }, { 2,3,6 }, { 3,0,7 }};
2795 selectNodes( elemNodes, & subNodes[0], &eInd[0][0], 4,3, mesh, &elemQueue, SMDSAbs_Edge );
2798 case SMDSEntity_Quad_Edge:
2800 if ( elemIt == elemQueue.begin() )
2801 continue; // an elem is in theElems
2802 elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2805 elemType = & segType;
2807 // get nodes for new elements
2808 static int eInd[2][2] = {{ 0,2 }, { 2,1 }};
2809 selectNodes( elemNodes, & splitNodes[0], &eInd[0][0], nbElems, nbNodes );
2813 } // switch( elem->GetEntityType() )
2815 // Create new elements
2817 SMESHDS_SubMesh* subMesh = mesh->MeshElements( elem->getshapeId() );
2821 //elemType->SetID( elem->GetID() ); // create an elem with the same ID as a removed one
2822 mesh->RemoveFreeElement( elem, subMesh, /*fromGroups=*/false );
2823 //splitElems.push_back( AddElement( splitNodes[ 0 ], *elemType ));
2824 //elemType->SetID( -1 );
2826 for ( int iE = 0; iE < nbElems; ++iE )
2827 splitElems.push_back( AddElement( splitNodes[ iE ], *elemType ));
2830 ReplaceElemInGroups( elem, splitElems, mesh );
2833 for ( size_t i = 0; i < splitElems.size(); ++i )
2834 subMesh->AddElement( splitElems[i] );
2839 //=======================================================================
2840 //function : AddToSameGroups
2841 //purpose : add elemToAdd to the groups the elemInGroups belongs to
2842 //=======================================================================
2844 void SMESH_MeshEditor::AddToSameGroups (const SMDS_MeshElement* elemToAdd,
2845 const SMDS_MeshElement* elemInGroups,
2846 SMESHDS_Mesh * aMesh)
2848 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2849 if (!groups.empty()) {
2850 set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2851 for ( ; grIt != groups.end(); grIt++ ) {
2852 SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2853 if ( group && group->Contains( elemInGroups ))
2854 group->SMDSGroup().Add( elemToAdd );
2860 //=======================================================================
2861 //function : RemoveElemFromGroups
2862 //purpose : Remove removeelem to the groups the elemInGroups belongs to
2863 //=======================================================================
2864 void SMESH_MeshEditor::RemoveElemFromGroups (const SMDS_MeshElement* removeelem,
2865 SMESHDS_Mesh * aMesh)
2867 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2868 if (!groups.empty())
2870 set<SMESHDS_GroupBase*>::const_iterator GrIt = groups.begin();
2871 for (; GrIt != groups.end(); GrIt++)
2873 SMESHDS_Group* grp = dynamic_cast<SMESHDS_Group*>(*GrIt);
2874 if (!grp || grp->IsEmpty()) continue;
2875 grp->SMDSGroup().Remove(removeelem);
2880 //================================================================================
2882 * \brief Replace elemToRm by elemToAdd in the all groups
2884 //================================================================================
2886 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement* elemToRm,
2887 const SMDS_MeshElement* elemToAdd,
2888 SMESHDS_Mesh * aMesh)
2890 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2891 if (!groups.empty()) {
2892 set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2893 for ( ; grIt != groups.end(); grIt++ ) {
2894 SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2895 if ( group && group->SMDSGroup().Remove( elemToRm ) && elemToAdd )
2896 group->SMDSGroup().Add( elemToAdd );
2901 //================================================================================
2903 * \brief Replace elemToRm by elemToAdd in the all groups
2905 //================================================================================
2907 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement* elemToRm,
2908 const vector<const SMDS_MeshElement*>& elemToAdd,
2909 SMESHDS_Mesh * aMesh)
2911 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2912 if (!groups.empty())
2914 set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2915 for ( ; grIt != groups.end(); grIt++ ) {
2916 SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2917 if ( group && group->SMDSGroup().Remove( elemToRm ) )
2918 for ( size_t i = 0; i < elemToAdd.size(); ++i )
2919 group->SMDSGroup().Add( elemToAdd[ i ] );
2924 //=======================================================================
2925 //function : QuadToTri
2926 //purpose : Cut quadrangles into triangles.
2927 // theCrit is used to select a diagonal to cut
2928 //=======================================================================
2930 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet & theElems,
2931 const bool the13Diag)
2933 myLastCreatedElems.Clear();
2934 myLastCreatedNodes.Clear();
2936 SMESHDS_Mesh * aMesh = GetMeshDS();
2938 Handle(Geom_Surface) surface;
2939 SMESH_MesherHelper helper( *GetMesh() );
2941 TIDSortedElemSet::iterator itElem;
2942 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
2944 const SMDS_MeshElement* elem = *itElem;
2945 if ( !elem || elem->GetGeomType() != SMDSGeom_QUADRANGLE )
2948 if ( elem->NbNodes() == 4 ) {
2949 // retrieve element nodes
2950 const SMDS_MeshNode* aNodes [4];
2951 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
2953 while ( itN->more() )
2954 aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
2956 int aShapeId = FindShape( elem );
2957 const SMDS_MeshElement* newElem1 = 0;
2958 const SMDS_MeshElement* newElem2 = 0;
2960 newElem1 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
2961 newElem2 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
2964 newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
2965 newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
2967 myLastCreatedElems.Append(newElem1);
2968 myLastCreatedElems.Append(newElem2);
2969 // put a new triangle on the same shape and add to the same groups
2972 aMesh->SetMeshElementOnShape( newElem1, aShapeId );
2973 aMesh->SetMeshElementOnShape( newElem2, aShapeId );
2975 AddToSameGroups( newElem1, elem, aMesh );
2976 AddToSameGroups( newElem2, elem, aMesh );
2977 aMesh->RemoveElement( elem );
2980 // Quadratic quadrangle
2982 else if ( elem->NbNodes() >= 8 )
2984 // get surface elem is on
2985 int aShapeId = FindShape( elem );
2986 if ( aShapeId != helper.GetSubShapeID() ) {
2990 shape = aMesh->IndexToShape( aShapeId );
2991 if ( !shape.IsNull() && shape.ShapeType() == TopAbs_FACE ) {
2992 TopoDS_Face face = TopoDS::Face( shape );
2993 surface = BRep_Tool::Surface( face );
2994 if ( !surface.IsNull() )
2995 helper.SetSubShape( shape );
2999 const SMDS_MeshNode* aNodes [9]; aNodes[8] = 0;
3000 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3001 for ( int i = 0; itN->more(); ++i )
3002 aNodes[ i ] = static_cast<const SMDS_MeshNode*>( itN->next() );
3004 const SMDS_MeshNode* centrNode = aNodes[8];
3005 if ( centrNode == 0 )
3007 centrNode = helper.GetCentralNode( aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3008 aNodes[4], aNodes[5], aNodes[6], aNodes[7],
3010 myLastCreatedNodes.Append(centrNode);
3013 // create a new element
3014 const SMDS_MeshElement* newElem1 = 0;
3015 const SMDS_MeshElement* newElem2 = 0;
3017 newElem1 = aMesh->AddFace(aNodes[2], aNodes[3], aNodes[0],
3018 aNodes[6], aNodes[7], centrNode );
3019 newElem2 = aMesh->AddFace(aNodes[2], aNodes[0], aNodes[1],
3020 centrNode, aNodes[4], aNodes[5] );
3023 newElem1 = aMesh->AddFace(aNodes[3], aNodes[0], aNodes[1],
3024 aNodes[7], aNodes[4], centrNode );
3025 newElem2 = aMesh->AddFace(aNodes[3], aNodes[1], aNodes[2],
3026 centrNode, aNodes[5], aNodes[6] );
3028 myLastCreatedElems.Append(newElem1);
3029 myLastCreatedElems.Append(newElem2);
3030 // put a new triangle on the same shape and add to the same groups
3033 aMesh->SetMeshElementOnShape( newElem1, aShapeId );
3034 aMesh->SetMeshElementOnShape( newElem2, aShapeId );
3036 AddToSameGroups( newElem1, elem, aMesh );
3037 AddToSameGroups( newElem2, elem, aMesh );
3038 aMesh->RemoveElement( elem );
3045 //=======================================================================
3046 //function : getAngle
3048 //=======================================================================
3050 double getAngle(const SMDS_MeshElement * tr1,
3051 const SMDS_MeshElement * tr2,
3052 const SMDS_MeshNode * n1,
3053 const SMDS_MeshNode * n2)
3055 double angle = 2. * M_PI; // bad angle
3058 SMESH::Controls::TSequenceOfXYZ P1, P2;
3059 if ( !SMESH::Controls::NumericalFunctor::GetPoints( tr1, P1 ) ||
3060 !SMESH::Controls::NumericalFunctor::GetPoints( tr2, P2 ))
3063 if(!tr1->IsQuadratic())
3064 N1 = gp_Vec( P1(2) - P1(1) ) ^ gp_Vec( P1(3) - P1(1) );
3066 N1 = gp_Vec( P1(3) - P1(1) ) ^ gp_Vec( P1(5) - P1(1) );
3067 if ( N1.SquareMagnitude() <= gp::Resolution() )
3069 if(!tr2->IsQuadratic())
3070 N2 = gp_Vec( P2(2) - P2(1) ) ^ gp_Vec( P2(3) - P2(1) );
3072 N2 = gp_Vec( P2(3) - P2(1) ) ^ gp_Vec( P2(5) - P2(1) );
3073 if ( N2.SquareMagnitude() <= gp::Resolution() )
3076 // find the first diagonal node n1 in the triangles:
3077 // take in account a diagonal link orientation
3078 const SMDS_MeshElement *nFirst[2], *tr[] = { tr1, tr2 };
3079 for ( int t = 0; t < 2; t++ ) {
3080 SMDS_ElemIteratorPtr it = tr[ t ]->nodesIterator();
3081 int i = 0, iDiag = -1;
3082 while ( it->more()) {
3083 const SMDS_MeshElement *n = it->next();
3084 if ( n == n1 || n == n2 ) {
3088 if ( i - iDiag == 1 )
3089 nFirst[ t ] = ( n == n1 ? n2 : n1 );
3098 if ( nFirst[ 0 ] == nFirst[ 1 ] )
3101 angle = N1.Angle( N2 );
3106 // =================================================
3107 // class generating a unique ID for a pair of nodes
3108 // and able to return nodes by that ID
3109 // =================================================
3113 LinkID_Gen( const SMESHDS_Mesh* theMesh )
3114 :myMesh( theMesh ), myMaxID( theMesh->MaxNodeID() + 1)
3117 long GetLinkID (const SMDS_MeshNode * n1,
3118 const SMDS_MeshNode * n2) const
3120 return ( Min(n1->GetID(),n2->GetID()) * myMaxID + Max(n1->GetID(),n2->GetID()));
3123 bool GetNodes (const long theLinkID,
3124 const SMDS_MeshNode* & theNode1,
3125 const SMDS_MeshNode* & theNode2) const
3127 theNode1 = myMesh->FindNode( theLinkID / myMaxID );
3128 if ( !theNode1 ) return false;
3129 theNode2 = myMesh->FindNode( theLinkID % myMaxID );
3130 if ( !theNode2 ) return false;
3136 const SMESHDS_Mesh* myMesh;
3141 //=======================================================================
3142 //function : TriToQuad
3143 //purpose : Fuse neighbour triangles into quadrangles.
3144 // theCrit is used to select a neighbour to fuse with.
3145 // theMaxAngle is a max angle between element normals at which
3146 // fusion is still performed.
3147 //=======================================================================
3149 bool SMESH_MeshEditor::TriToQuad (TIDSortedElemSet & theElems,
3150 SMESH::Controls::NumericalFunctorPtr theCrit,
3151 const double theMaxAngle)
3153 myLastCreatedElems.Clear();
3154 myLastCreatedNodes.Clear();
3156 if ( !theCrit.get() )
3159 SMESHDS_Mesh * aMesh = GetMeshDS();
3161 // Prepare data for algo: build
3162 // 1. map of elements with their linkIDs
3163 // 2. map of linkIDs with their elements
3165 map< SMESH_TLink, list< const SMDS_MeshElement* > > mapLi_listEl;
3166 map< SMESH_TLink, list< const SMDS_MeshElement* > >::iterator itLE;
3167 map< const SMDS_MeshElement*, set< SMESH_TLink > > mapEl_setLi;
3168 map< const SMDS_MeshElement*, set< SMESH_TLink > >::iterator itEL;
3170 TIDSortedElemSet::iterator itElem;
3171 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
3173 const SMDS_MeshElement* elem = *itElem;
3174 if(!elem || elem->GetType() != SMDSAbs_Face ) continue;
3175 bool IsTria = ( elem->NbCornerNodes()==3 );
3176 if (!IsTria) continue;
3178 // retrieve element nodes
3179 const SMDS_MeshNode* aNodes [4];
3180 SMDS_NodeIteratorPtr itN = elem->nodeIterator();
3183 aNodes[ i++ ] = itN->next();
3184 aNodes[ 3 ] = aNodes[ 0 ];
3187 for ( i = 0; i < 3; i++ ) {
3188 SMESH_TLink link( aNodes[i], aNodes[i+1] );
3189 // check if elements sharing a link can be fused
3190 itLE = mapLi_listEl.find( link );
3191 if ( itLE != mapLi_listEl.end() ) {
3192 if ((*itLE).second.size() > 1 ) // consider only 2 elems adjacent by a link
3194 const SMDS_MeshElement* elem2 = (*itLE).second.front();
3195 //if ( FindShape( elem ) != FindShape( elem2 ))
3196 // continue; // do not fuse triangles laying on different shapes
3197 if ( getAngle( elem, elem2, aNodes[i], aNodes[i+1] ) > theMaxAngle )
3198 continue; // avoid making badly shaped quads
3199 (*itLE).second.push_back( elem );
3202 mapLi_listEl[ link ].push_back( elem );
3204 mapEl_setLi [ elem ].insert( link );
3207 // Clean the maps from the links shared by a sole element, ie
3208 // links to which only one element is bound in mapLi_listEl
3210 for ( itLE = mapLi_listEl.begin(); itLE != mapLi_listEl.end(); itLE++ ) {
3211 int nbElems = (*itLE).second.size();
3212 if ( nbElems < 2 ) {
3213 const SMDS_MeshElement* elem = (*itLE).second.front();
3214 SMESH_TLink link = (*itLE).first;
3215 mapEl_setLi[ elem ].erase( link );
3216 if ( mapEl_setLi[ elem ].empty() )
3217 mapEl_setLi.erase( elem );
3221 // Algo: fuse triangles into quadrangles
3223 while ( ! mapEl_setLi.empty() ) {
3224 // Look for the start element:
3225 // the element having the least nb of shared links
3226 const SMDS_MeshElement* startElem = 0;
3228 for ( itEL = mapEl_setLi.begin(); itEL != mapEl_setLi.end(); itEL++ ) {
3229 int nbLinks = (*itEL).second.size();
3230 if ( nbLinks < minNbLinks ) {
3231 startElem = (*itEL).first;
3232 minNbLinks = nbLinks;
3233 if ( minNbLinks == 1 )
3238 // search elements to fuse starting from startElem or links of elements
3239 // fused earlyer - startLinks
3240 list< SMESH_TLink > startLinks;
3241 while ( startElem || !startLinks.empty() ) {
3242 while ( !startElem && !startLinks.empty() ) {
3243 // Get an element to start, by a link
3244 SMESH_TLink linkId = startLinks.front();
3245 startLinks.pop_front();
3246 itLE = mapLi_listEl.find( linkId );
3247 if ( itLE != mapLi_listEl.end() ) {
3248 list< const SMDS_MeshElement* > & listElem = (*itLE).second;
3249 list< const SMDS_MeshElement* >::iterator itE = listElem.begin();
3250 for ( ; itE != listElem.end() ; itE++ )
3251 if ( mapEl_setLi.find( (*itE) ) != mapEl_setLi.end() )
3253 mapLi_listEl.erase( itLE );
3258 // Get candidates to be fused
3259 const SMDS_MeshElement *tr1 = startElem, *tr2 = 0, *tr3 = 0;
3260 const SMESH_TLink *link12 = 0, *link13 = 0;
3262 ASSERT( mapEl_setLi.find( tr1 ) != mapEl_setLi.end() );
3263 set< SMESH_TLink >& setLi = mapEl_setLi[ tr1 ];
3264 ASSERT( !setLi.empty() );
3265 set< SMESH_TLink >::iterator itLi;
3266 for ( itLi = setLi.begin(); itLi != setLi.end(); itLi++ )
3268 const SMESH_TLink & link = (*itLi);
3269 itLE = mapLi_listEl.find( link );
3270 if ( itLE == mapLi_listEl.end() )
3273 const SMDS_MeshElement* elem = (*itLE).second.front();
3275 elem = (*itLE).second.back();
3276 mapLi_listEl.erase( itLE );
3277 if ( mapEl_setLi.find( elem ) == mapEl_setLi.end())
3288 // add other links of elem to list of links to re-start from
3289 set< SMESH_TLink >& links = mapEl_setLi[ elem ];
3290 set< SMESH_TLink >::iterator it;
3291 for ( it = links.begin(); it != links.end(); it++ ) {
3292 const SMESH_TLink& link2 = (*it);
3293 if ( link2 != link )
3294 startLinks.push_back( link2 );
3298 // Get nodes of possible quadrangles
3299 const SMDS_MeshNode *n12 [4], *n13 [4];
3300 bool Ok12 = false, Ok13 = false;
3301 const SMDS_MeshNode *linkNode1, *linkNode2;
3303 linkNode1 = link12->first;
3304 linkNode2 = link12->second;
3305 if ( tr2 && getQuadrangleNodes( n12, linkNode1, linkNode2, tr1, tr2 ))
3309 linkNode1 = link13->first;
3310 linkNode2 = link13->second;
3311 if ( tr3 && getQuadrangleNodes( n13, linkNode1, linkNode2, tr1, tr3 ))
3315 // Choose a pair to fuse
3316 if ( Ok12 && Ok13 ) {
3317 SMDS_FaceOfNodes quad12 ( n12[ 0 ], n12[ 1 ], n12[ 2 ], n12[ 3 ] );
3318 SMDS_FaceOfNodes quad13 ( n13[ 0 ], n13[ 1 ], n13[ 2 ], n13[ 3 ] );
3319 double aBadRate12 = getBadRate( &quad12, theCrit );
3320 double aBadRate13 = getBadRate( &quad13, theCrit );
3321 if ( aBadRate13 < aBadRate12 )
3328 // and remove fused elems and remove links from the maps
3329 mapEl_setLi.erase( tr1 );
3332 mapEl_setLi.erase( tr2 );
3333 mapLi_listEl.erase( *link12 );
3334 if ( tr1->NbNodes() == 3 )
3336 const SMDS_MeshElement* newElem = 0;
3337 newElem = aMesh->AddFace(n12[0], n12[1], n12[2], n12[3] );
3338 myLastCreatedElems.Append(newElem);
3339 AddToSameGroups( newElem, tr1, aMesh );
3340 int aShapeId = tr1->getshapeId();
3342 aMesh->SetMeshElementOnShape( newElem, aShapeId );
3343 aMesh->RemoveElement( tr1 );
3344 aMesh->RemoveElement( tr2 );
3347 vector< const SMDS_MeshNode* > N1;
3348 vector< const SMDS_MeshNode* > N2;
3349 getNodesFromTwoTria(tr1,tr2,N1,N2);
3350 // now we receive following N1 and N2 (using numeration as in image in InverseDiag())
3351 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
3352 // i.e. first nodes from both arrays form a new diagonal
3353 const SMDS_MeshNode* aNodes[8];
3362 const SMDS_MeshElement* newElem = 0;
3363 if ( N1.size() == 7 || N2.size() == 7 ) // biquadratic
3364 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3365 aNodes[4], aNodes[5], aNodes[6], aNodes[7], N1[4]);
3367 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3368 aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
3369 myLastCreatedElems.Append(newElem);
3370 AddToSameGroups( newElem, tr1, aMesh );
3371 int aShapeId = tr1->getshapeId();
3373 aMesh->SetMeshElementOnShape( newElem, aShapeId );
3374 aMesh->RemoveElement( tr1 );
3375 aMesh->RemoveElement( tr2 );
3376 // remove middle node (9)
3377 if ( N1[4]->NbInverseElements() == 0 )
3378 aMesh->RemoveNode( N1[4] );
3379 if ( N1.size() == 7 && N1[6]->NbInverseElements() == 0 )
3380 aMesh->RemoveNode( N1[6] );
3381 if ( N2.size() == 7 && N2[6]->NbInverseElements() == 0 )
3382 aMesh->RemoveNode( N2[6] );
3387 mapEl_setLi.erase( tr3 );
3388 mapLi_listEl.erase( *link13 );
3389 if ( tr1->NbNodes() == 3 ) {
3390 const SMDS_MeshElement* newElem = 0;
3391 newElem = aMesh->AddFace(n13[0], n13[1], n13[2], n13[3] );
3392 myLastCreatedElems.Append(newElem);
3393 AddToSameGroups( newElem, tr1, aMesh );
3394 int aShapeId = tr1->getshapeId();
3396 aMesh->SetMeshElementOnShape( newElem, aShapeId );
3397 aMesh->RemoveElement( tr1 );
3398 aMesh->RemoveElement( tr3 );
3401 vector< const SMDS_MeshNode* > N1;
3402 vector< const SMDS_MeshNode* > N2;
3403 getNodesFromTwoTria(tr1,tr3,N1,N2);
3404 // now we receive following N1 and N2 (using numeration as above image)
3405 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
3406 // i.e. first nodes from both arrays form a new diagonal
3407 const SMDS_MeshNode* aNodes[8];
3416 const SMDS_MeshElement* newElem = 0;
3417 if ( N1.size() == 7 || N2.size() == 7 ) // biquadratic
3418 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3419 aNodes[4], aNodes[5], aNodes[6], aNodes[7], N1[4]);
3421 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3422 aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
3423 myLastCreatedElems.Append(newElem);
3424 AddToSameGroups( newElem, tr1, aMesh );
3425 int aShapeId = tr1->getshapeId();
3427 aMesh->SetMeshElementOnShape( newElem, aShapeId );
3428 aMesh->RemoveElement( tr1 );
3429 aMesh->RemoveElement( tr3 );
3430 // remove middle node (9)
3431 if ( N1[4]->NbInverseElements() == 0 )
3432 aMesh->RemoveNode( N1[4] );
3433 if ( N1.size() == 7 && N1[6]->NbInverseElements() == 0 )
3434 aMesh->RemoveNode( N1[6] );
3435 if ( N2.size() == 7 && N2[6]->NbInverseElements() == 0 )
3436 aMesh->RemoveNode( N2[6] );
3440 // Next element to fuse: the rejected one
3442 startElem = Ok12 ? tr3 : tr2;
3444 } // if ( startElem )
3445 } // while ( startElem || !startLinks.empty() )
3446 } // while ( ! mapEl_setLi.empty() )
3452 /*#define DUMPSO(txt) \
3453 // cout << txt << endl;
3454 //=============================================================================
3458 //=============================================================================
3459 static void swap( int i1, int i2, int idNodes[], gp_Pnt P[] )
3463 int tmp = idNodes[ i1 ];
3464 idNodes[ i1 ] = idNodes[ i2 ];
3465 idNodes[ i2 ] = tmp;
3466 gp_Pnt Ptmp = P[ i1 ];
3469 DUMPSO( i1 << "(" << idNodes[ i2 ] << ") <-> " << i2 << "(" << idNodes[ i1 ] << ")");
3472 //=======================================================================
3473 //function : SortQuadNodes
3474 //purpose : Set 4 nodes of a quadrangle face in a good order.
3475 // Swap 1<->2 or 2<->3 nodes and correspondingly return
3477 //=======================================================================
3479 int SMESH_MeshEditor::SortQuadNodes (const SMDS_Mesh * theMesh,
3484 for ( i = 0; i < 4; i++ ) {
3485 const SMDS_MeshNode *n = theMesh->FindNode( idNodes[i] );
3487 P[ i ].SetCoord( n->X(), n->Y(), n->Z() );
3490 gp_Vec V1(P[0], P[1]);
3491 gp_Vec V2(P[0], P[2]);
3492 gp_Vec V3(P[0], P[3]);
3494 gp_Vec Cross1 = V1 ^ V2;
3495 gp_Vec Cross2 = V2 ^ V3;
3498 if (Cross1.Dot(Cross2) < 0)
3503 if (Cross1.Dot(Cross2) < 0)
3507 swap ( i, i + 1, idNodes, P );
3509 // for ( int ii = 0; ii < 4; ii++ ) {
3510 // const SMDS_MeshNode *n = theMesh->FindNode( idNodes[ii] );
3511 // DUMPSO( ii << "(" << idNodes[ii] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
3517 //=======================================================================
3518 //function : SortHexaNodes
3519 //purpose : Set 8 nodes of a hexahedron in a good order.
3520 // Return success status
3521 //=======================================================================
3523 bool SMESH_MeshEditor::SortHexaNodes (const SMDS_Mesh * theMesh,
3528 DUMPSO( "INPUT: ========================================");
3529 for ( i = 0; i < 8; i++ ) {
3530 const SMDS_MeshNode *n = theMesh->FindNode( idNodes[i] );
3531 if ( !n ) return false;
3532 P[ i ].SetCoord( n->X(), n->Y(), n->Z() );
3533 DUMPSO( i << "(" << idNodes[i] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
3535 DUMPSO( "========================================");
3538 set<int> faceNodes; // ids of bottom face nodes, to be found
3539 set<int> checkedId1; // ids of tried 2-nd nodes
3540 Standard_Real leastDist = DBL_MAX; // dist of the 4-th node from 123 plane
3541 const Standard_Real tol = 1.e-6; // tolerance to find nodes in plane
3542 int iMin, iLoop1 = 0;
3544 // Loop to try the 2-nd nodes
3546 while ( leastDist > DBL_MIN && ++iLoop1 < 8 )
3548 // Find not checked 2-nd node
3549 for ( i = 1; i < 8; i++ )
3550 if ( checkedId1.find( idNodes[i] ) == checkedId1.end() ) {
3551 int id1 = idNodes[i];
3552 swap ( 1, i, idNodes, P );
3553 checkedId1.insert ( id1 );
3557 // Find the 3-d node so that 1-2-3 triangle to be on a hexa face,
3558 // ie that all but meybe one (id3 which is on the same face) nodes
3559 // lay on the same side from the triangle plane.
3561 bool manyInPlane = false; // more than 4 nodes lay in plane
3563 while ( ++iLoop2 < 6 ) {
3565 // get 1-2-3 plane coeffs
3566 Standard_Real A, B, C, D;
3567 gp_Vec N = gp_Vec (P[0], P[1]).Crossed( gp_Vec (P[0], P[2]) );
3568 if ( N.SquareMagnitude() > gp::Resolution() )
3570 gp_Pln pln ( P[0], N );
3571 pln.Coefficients( A, B, C, D );
3573 // find the node (iMin) closest to pln
3574 Standard_Real dist[ 8 ], minDist = DBL_MAX;
3576 for ( i = 3; i < 8; i++ ) {
3577 dist[i] = A * P[i].X() + B * P[i].Y() + C * P[i].Z() + D;
3578 if ( fabs( dist[i] ) < minDist ) {
3579 minDist = fabs( dist[i] );
3582 if ( fabs( dist[i] ) <= tol )
3583 idInPln.insert( idNodes[i] );
3586 // there should not be more than 4 nodes in bottom plane
3587 if ( idInPln.size() > 1 )
3589 DUMPSO( "### idInPln.size() = " << idInPln.size());
3590 // idInPlane does not contain the first 3 nodes
3591 if ( manyInPlane || idInPln.size() == 5)
3592 return false; // all nodes in one plane
3595 // set the 1-st node to be not in plane
3596 for ( i = 3; i < 8; i++ ) {
3597 if ( idInPln.find( idNodes[ i ] ) == idInPln.end() ) {
3598 DUMPSO( "### Reset 0-th node");
3599 swap( 0, i, idNodes, P );
3604 // reset to re-check second nodes
3605 leastDist = DBL_MAX;
3609 break; // from iLoop2;
3612 // check that the other 4 nodes are on the same side
3613 bool sameSide = true;
3614 bool isNeg = dist[ iMin == 3 ? 4 : 3 ] <= 0.;
3615 for ( i = 3; sameSide && i < 8; i++ ) {
3617 sameSide = ( isNeg == dist[i] <= 0.);
3620 // keep best solution
3621 if ( sameSide && minDist < leastDist ) {
3622 leastDist = minDist;
3624 faceNodes.insert( idNodes[ 1 ] );
3625 faceNodes.insert( idNodes[ 2 ] );
3626 faceNodes.insert( idNodes[ iMin ] );
3627 DUMPSO( "loop " << iLoop2 << " id2 " << idNodes[ 1 ] << " id3 " << idNodes[ 2 ]
3628 << " leastDist = " << leastDist);
3629 if ( leastDist <= DBL_MIN )
3634 // set next 3-d node to check
3635 int iNext = 2 + iLoop2;
3637 DUMPSO( "Try 2-nd");
3638 swap ( 2, iNext, idNodes, P );
3640 } // while ( iLoop2 < 6 )
3643 if ( faceNodes.empty() ) return false;
3645 // Put the faceNodes in proper places
3646 for ( i = 4; i < 8; i++ ) {
3647 if ( faceNodes.find( idNodes[ i ] ) != faceNodes.end() ) {
3648 // find a place to put
3650 while ( faceNodes.find( idNodes[ iTo ] ) != faceNodes.end() )
3652 DUMPSO( "Set faceNodes");
3653 swap ( iTo, i, idNodes, P );
3658 // Set nodes of the found bottom face in good order
3659 DUMPSO( " Found bottom face: ");
3660 i = SortQuadNodes( theMesh, idNodes );
3662 gp_Pnt Ptmp = P[ i ];
3667 // for ( int ii = 0; ii < 4; ii++ ) {
3668 // const SMDS_MeshNode *n = theMesh->FindNode( idNodes[ii] );
3669 // DUMPSO( ii << "(" << idNodes[ii] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
3672 // Gravity center of the top and bottom faces
3673 gp_Pnt aGCb = ( P[0].XYZ() + P[1].XYZ() + P[2].XYZ() + P[3].XYZ() ) / 4.;
3674 gp_Pnt aGCt = ( P[4].XYZ() + P[5].XYZ() + P[6].XYZ() + P[7].XYZ() ) / 4.;
3676 // Get direction from the bottom to the top face
3677 gp_Vec upDir ( aGCb, aGCt );
3678 Standard_Real upDirSize = upDir.Magnitude();
3679 if ( upDirSize <= gp::Resolution() ) return false;
3682 // Assure that the bottom face normal points up
3683 gp_Vec Nb = gp_Vec (P[0], P[1]).Crossed( gp_Vec (P[0], P[2]) );
3684 Nb += gp_Vec (P[0], P[2]).Crossed( gp_Vec (P[0], P[3]) );
3685 if ( Nb.Dot( upDir ) < 0 ) {
3686 DUMPSO( "Reverse bottom face");
3687 swap( 1, 3, idNodes, P );
3690 // Find 5-th node - the one closest to the 1-st among the last 4 nodes.
3691 Standard_Real minDist = DBL_MAX;
3692 for ( i = 4; i < 8; i++ ) {
3693 // projection of P[i] to the plane defined by P[0] and upDir
3694 gp_Pnt Pp = P[i].Translated( upDir * ( upDir.Dot( gp_Vec( P[i], P[0] ))));
3695 Standard_Real sqDist = P[0].SquareDistance( Pp );
3696 if ( sqDist < minDist ) {
3701 DUMPSO( "Set 4-th");
3702 swap ( 4, iMin, idNodes, P );
3704 // Set nodes of the top face in good order
3705 DUMPSO( "Sort top face");
3706 i = SortQuadNodes( theMesh, &idNodes[4] );
3709 gp_Pnt Ptmp = P[ i ];
3714 // Assure that direction of the top face normal is from the bottom face
3715 gp_Vec Nt = gp_Vec (P[4], P[5]).Crossed( gp_Vec (P[4], P[6]) );
3716 Nt += gp_Vec (P[4], P[6]).Crossed( gp_Vec (P[4], P[7]) );
3717 if ( Nt.Dot( upDir ) < 0 ) {
3718 DUMPSO( "Reverse top face");
3719 swap( 5, 7, idNodes, P );
3722 // DUMPSO( "OUTPUT: ========================================");
3723 // for ( i = 0; i < 8; i++ ) {
3724 // float *p = ugrid->GetPoint(idNodes[i]);
3725 // DUMPSO( i << "(" << idNodes[i] << ") : " << p[0] << " " << p[1] << " " << p[2]);
3731 //================================================================================
3733 * \brief Return nodes linked to the given one
3734 * \param theNode - the node
3735 * \param linkedNodes - the found nodes
3736 * \param type - the type of elements to check
3738 * Medium nodes are ignored
3740 //================================================================================
3742 void SMESH_MeshEditor::GetLinkedNodes( const SMDS_MeshNode* theNode,
3743 TIDSortedElemSet & linkedNodes,
3744 SMDSAbs_ElementType type )
3746 SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(type);
3747 while ( elemIt->more() )
3749 const SMDS_MeshElement* elem = elemIt->next();
3750 if(elem->GetType() == SMDSAbs_0DElement)
3753 SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
3754 if ( elem->GetType() == SMDSAbs_Volume )
3756 SMDS_VolumeTool vol( elem );
3757 while ( nodeIt->more() ) {
3758 const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
3759 if ( theNode != n && vol.IsLinked( theNode, n ))
3760 linkedNodes.insert( n );
3765 for ( int i = 0; nodeIt->more(); ++i ) {
3766 const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
3767 if ( n == theNode ) {
3768 int iBefore = i - 1;
3770 if ( elem->IsQuadratic() ) {
3771 int nb = elem->NbNodes() / 2;
3772 iAfter = SMESH_MesherHelper::WrapIndex( iAfter, nb );
3773 iBefore = SMESH_MesherHelper::WrapIndex( iBefore, nb );
3775 linkedNodes.insert( elem->GetNodeWrap( iAfter ));
3776 linkedNodes.insert( elem->GetNodeWrap( iBefore ));
3783 //=======================================================================
3784 //function : laplacianSmooth
3785 //purpose : pulls theNode toward the center of surrounding nodes directly
3786 // connected to that node along an element edge
3787 //=======================================================================
3789 void laplacianSmooth(const SMDS_MeshNode* theNode,
3790 const Handle(Geom_Surface)& theSurface,
3791 map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
3793 // find surrounding nodes
3795 TIDSortedElemSet nodeSet;
3796 SMESH_MeshEditor::GetLinkedNodes( theNode, nodeSet, SMDSAbs_Face );
3798 // compute new coodrs
3800 double coord[] = { 0., 0., 0. };
3801 TIDSortedElemSet::iterator nodeSetIt = nodeSet.begin();
3802 for ( ; nodeSetIt != nodeSet.end(); nodeSetIt++ ) {
3803 const SMDS_MeshNode* node = cast2Node(*nodeSetIt);
3804 if ( theSurface.IsNull() ) { // smooth in 3D
3805 coord[0] += node->X();
3806 coord[1] += node->Y();
3807 coord[2] += node->Z();
3809 else { // smooth in 2D
3810 ASSERT( theUVMap.find( node ) != theUVMap.end() );
3811 gp_XY* uv = theUVMap[ node ];
3812 coord[0] += uv->X();
3813 coord[1] += uv->Y();
3816 int nbNodes = nodeSet.size();
3819 coord[0] /= nbNodes;
3820 coord[1] /= nbNodes;
3822 if ( !theSurface.IsNull() ) {
3823 ASSERT( theUVMap.find( theNode ) != theUVMap.end() );
3824 theUVMap[ theNode ]->SetCoord( coord[0], coord[1] );
3825 gp_Pnt p3d = theSurface->Value( coord[0], coord[1] );
3831 coord[2] /= nbNodes;
3835 const_cast< SMDS_MeshNode* >( theNode )->setXYZ(coord[0],coord[1],coord[2]);
3838 //=======================================================================
3839 //function : centroidalSmooth
3840 //purpose : pulls theNode toward the element-area-weighted centroid of the
3841 // surrounding elements
3842 //=======================================================================
3844 void centroidalSmooth(const SMDS_MeshNode* theNode,
3845 const Handle(Geom_Surface)& theSurface,
3846 map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
3848 gp_XYZ aNewXYZ(0.,0.,0.);
3849 SMESH::Controls::Area anAreaFunc;
3850 double totalArea = 0.;
3855 SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(SMDSAbs_Face);
3856 while ( elemIt->more() )
3858 const SMDS_MeshElement* elem = elemIt->next();
3861 gp_XYZ elemCenter(0.,0.,0.);
3862 SMESH::Controls::TSequenceOfXYZ aNodePoints;
3863 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3864 int nn = elem->NbNodes();
3865 if(elem->IsQuadratic()) nn = nn/2;
3867 //while ( itN->more() ) {
3869 const SMDS_MeshNode* aNode = static_cast<const SMDS_MeshNode*>( itN->next() );
3871 gp_XYZ aP( aNode->X(), aNode->Y(), aNode->Z() );
3872 aNodePoints.push_back( aP );
3873 if ( !theSurface.IsNull() ) { // smooth in 2D
3874 ASSERT( theUVMap.find( aNode ) != theUVMap.end() );
3875 gp_XY* uv = theUVMap[ aNode ];
3876 aP.SetCoord( uv->X(), uv->Y(), 0. );
3880 double elemArea = anAreaFunc.GetValue( aNodePoints );
3881 totalArea += elemArea;
3883 aNewXYZ += elemCenter * elemArea;
3885 aNewXYZ /= totalArea;
3886 if ( !theSurface.IsNull() ) {
3887 theUVMap[ theNode ]->SetCoord( aNewXYZ.X(), aNewXYZ.Y() );
3888 aNewXYZ = theSurface->Value( aNewXYZ.X(), aNewXYZ.Y() ).XYZ();
3893 const_cast< SMDS_MeshNode* >( theNode )->setXYZ(aNewXYZ.X(),aNewXYZ.Y(),aNewXYZ.Z());
3896 //=======================================================================
3897 //function : getClosestUV
3898 //purpose : return UV of closest projection
3899 //=======================================================================
3901 static bool getClosestUV (Extrema_GenExtPS& projector,
3902 const gp_Pnt& point,
3905 projector.Perform( point );
3906 if ( projector.IsDone() ) {
3907 double u, v, minVal = DBL_MAX;
3908 for ( int i = projector.NbExt(); i > 0; i-- )
3909 if ( projector.SquareDistance( i ) < minVal ) {
3910 minVal = projector.SquareDistance( i );
3911 projector.Point( i ).Parameter( u, v );
3913 result.SetCoord( u, v );
3919 //=======================================================================
3921 //purpose : Smooth theElements during theNbIterations or until a worst
3922 // element has aspect ratio <= theTgtAspectRatio.
3923 // Aspect Ratio varies in range [1.0, inf].
3924 // If theElements is empty, the whole mesh is smoothed.
3925 // theFixedNodes contains additionally fixed nodes. Nodes built
3926 // on edges and boundary nodes are always fixed.
3927 //=======================================================================
3929 void SMESH_MeshEditor::Smooth (TIDSortedElemSet & theElems,
3930 set<const SMDS_MeshNode*> & theFixedNodes,
3931 const SmoothMethod theSmoothMethod,
3932 const int theNbIterations,
3933 double theTgtAspectRatio,
3936 myLastCreatedElems.Clear();
3937 myLastCreatedNodes.Clear();
3939 if ( theTgtAspectRatio < 1.0 )
3940 theTgtAspectRatio = 1.0;
3942 const double disttol = 1.e-16;
3944 SMESH::Controls::AspectRatio aQualityFunc;
3946 SMESHDS_Mesh* aMesh = GetMeshDS();
3948 if ( theElems.empty() ) {
3949 // add all faces to theElems
3950 SMDS_FaceIteratorPtr fIt = aMesh->facesIterator();
3951 while ( fIt->more() ) {
3952 const SMDS_MeshElement* face = fIt->next();
3953 theElems.insert( theElems.end(), face );
3956 // get all face ids theElems are on
3957 set< int > faceIdSet;
3958 TIDSortedElemSet::iterator itElem;
3960 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
3961 int fId = FindShape( *itElem );
3962 // check that corresponding submesh exists and a shape is face
3964 faceIdSet.find( fId ) == faceIdSet.end() &&
3965 aMesh->MeshElements( fId )) {
3966 TopoDS_Shape F = aMesh->IndexToShape( fId );
3967 if ( !F.IsNull() && F.ShapeType() == TopAbs_FACE )
3968 faceIdSet.insert( fId );
3971 faceIdSet.insert( 0 ); // to smooth elements that are not on any TopoDS_Face
3973 // ===============================================
3974 // smooth elements on each TopoDS_Face separately
3975 // ===============================================
3977 SMESH_MesherHelper helper( *GetMesh() );
3979 set< int >::reverse_iterator fId = faceIdSet.rbegin(); // treat 0 fId at the end
3980 for ( ; fId != faceIdSet.rend(); ++fId )
3982 // get face surface and submesh
3983 Handle(Geom_Surface) surface;
3984 SMESHDS_SubMesh* faceSubMesh = 0;
3987 double u1 = 0, u2 = 0, v1 = 0, v2 = 0;
3988 bool isUPeriodic = false, isVPeriodic = false;
3991 face = TopoDS::Face( aMesh->IndexToShape( *fId ));
3992 surface = BRep_Tool::Surface( face );
3993 faceSubMesh = aMesh->MeshElements( *fId );
3994 fToler2 = BRep_Tool::Tolerance( face );
3995 fToler2 *= fToler2 * 10.;
3996 isUPeriodic = surface->IsUPeriodic();
3997 // if ( isUPeriodic )
3998 // surface->UPeriod();
3999 isVPeriodic = surface->IsVPeriodic();
4000 // if ( isVPeriodic )
4001 // surface->VPeriod();
4002 surface->Bounds( u1, u2, v1, v2 );
4003 helper.SetSubShape( face );
4005 // ---------------------------------------------------------
4006 // for elements on a face, find movable and fixed nodes and
4007 // compute UV for them
4008 // ---------------------------------------------------------
4009 bool checkBoundaryNodes = false;
4010 bool isQuadratic = false;
4011 set<const SMDS_MeshNode*> setMovableNodes;
4012 map< const SMDS_MeshNode*, gp_XY* > uvMap, uvMap2;
4013 list< gp_XY > listUV; // uvs the 2 uvMaps refer to
4014 list< const SMDS_MeshElement* > elemsOnFace;
4016 Extrema_GenExtPS projector;
4017 GeomAdaptor_Surface surfAdaptor;
4018 if ( !surface.IsNull() ) {
4019 surfAdaptor.Load( surface );
4020 projector.Initialize( surfAdaptor, 20,20, 1e-5,1e-5 );
4022 int nbElemOnFace = 0;
4023 itElem = theElems.begin();
4024 // loop on not yet smoothed elements: look for elems on a face
4025 while ( itElem != theElems.end() )
4027 if ( faceSubMesh && nbElemOnFace == faceSubMesh->NbElements() )
4028 break; // all elements found
4030 const SMDS_MeshElement* elem = *itElem;
4031 if ( !elem || elem->GetType() != SMDSAbs_Face || elem->NbNodes() < 3 ||
4032 ( faceSubMesh && !faceSubMesh->Contains( elem ))) {
4036 elemsOnFace.push_back( elem );
4037 theElems.erase( itElem++ );
4041 isQuadratic = elem->IsQuadratic();
4043 // get movable nodes of elem
4044 const SMDS_MeshNode* node;
4045 SMDS_TypeOfPosition posType;
4046 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
4047 int nn = 0, nbn = elem->NbNodes();
4048 if(elem->IsQuadratic())
4050 while ( nn++ < nbn ) {
4051 node = static_cast<const SMDS_MeshNode*>( itN->next() );
4052 const SMDS_PositionPtr& pos = node->GetPosition();
4053 posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
4054 if (posType != SMDS_TOP_EDGE &&
4055 posType != SMDS_TOP_VERTEX &&
4056 theFixedNodes.find( node ) == theFixedNodes.end())
4058 // check if all faces around the node are on faceSubMesh
4059 // because a node on edge may be bound to face
4061 if ( faceSubMesh ) {
4062 SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
4063 while ( eIt->more() && all ) {
4064 const SMDS_MeshElement* e = eIt->next();
4065 all = faceSubMesh->Contains( e );
4069 setMovableNodes.insert( node );
4071 checkBoundaryNodes = true;
4073 if ( posType == SMDS_TOP_3DSPACE )
4074 checkBoundaryNodes = true;
4077 if ( surface.IsNull() )
4080 // get nodes to check UV
4081 list< const SMDS_MeshNode* > uvCheckNodes;
4082 const SMDS_MeshNode* nodeInFace = 0;
4083 itN = elem->nodesIterator();
4084 nn = 0; nbn = elem->NbNodes();
4085 if(elem->IsQuadratic())
4087 while ( nn++ < nbn ) {
4088 node = static_cast<const SMDS_MeshNode*>( itN->next() );
4089 if ( node->GetPosition()->GetDim() == 2 )
4091 if ( uvMap.find( node ) == uvMap.end() )
4092 uvCheckNodes.push_back( node );
4093 // add nodes of elems sharing node
4094 // SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
4095 // while ( eIt->more() ) {
4096 // const SMDS_MeshElement* e = eIt->next();
4097 // if ( e != elem ) {
4098 // SMDS_ElemIteratorPtr nIt = e->nodesIterator();
4099 // while ( nIt->more() ) {
4100 // const SMDS_MeshNode* n =
4101 // static_cast<const SMDS_MeshNode*>( nIt->next() );
4102 // if ( uvMap.find( n ) == uvMap.end() )
4103 // uvCheckNodes.push_back( n );
4109 list< const SMDS_MeshNode* >::iterator n = uvCheckNodes.begin();
4110 for ( ; n != uvCheckNodes.end(); ++n ) {
4113 const SMDS_PositionPtr& pos = node->GetPosition();
4114 posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
4118 bool toCheck = true;
4119 uv = helper.GetNodeUV( face, node, nodeInFace, &toCheck );
4121 // compute not existing UV
4122 bool project = ( posType == SMDS_TOP_3DSPACE );
4123 // double dist1 = DBL_MAX, dist2 = 0;
4124 // if ( posType != SMDS_TOP_3DSPACE ) {
4125 // dist1 = pNode.SquareDistance( surface->Value( uv.X(), uv.Y() ));
4126 // project = dist1 > fToler2;
4128 if ( project ) { // compute new UV
4130 gp_Pnt pNode = SMESH_TNodeXYZ( node );
4131 if ( !getClosestUV( projector, pNode, newUV )) {
4132 MESSAGE("Node Projection Failed " << node);
4136 newUV.SetX( ElCLib::InPeriod( newUV.X(), u1, u2 ));
4138 newUV.SetY( ElCLib::InPeriod( newUV.Y(), v1, v2 ));
4140 // if ( posType != SMDS_TOP_3DSPACE )
4141 // dist2 = pNode.SquareDistance( surface->Value( newUV.X(), newUV.Y() ));
4142 // if ( dist2 < dist1 )
4146 // store UV in the map
4147 listUV.push_back( uv );
4148 uvMap.insert( make_pair( node, &listUV.back() ));
4150 } // loop on not yet smoothed elements
4152 if ( !faceSubMesh || nbElemOnFace != faceSubMesh->NbElements() )
4153 checkBoundaryNodes = true;
4155 // fix nodes on mesh boundary
4157 if ( checkBoundaryNodes ) {
4158 map< SMESH_TLink, int > linkNbMap; // how many times a link encounters in elemsOnFace
4159 map< SMESH_TLink, int >::iterator link_nb;
4160 // put all elements links to linkNbMap
4161 list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4162 for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
4163 const SMDS_MeshElement* elem = (*elemIt);
4164 int nbn = elem->NbCornerNodes();
4165 // loop on elem links: insert them in linkNbMap
4166 for ( int iN = 0; iN < nbn; ++iN ) {
4167 const SMDS_MeshNode* n1 = elem->GetNode( iN );
4168 const SMDS_MeshNode* n2 = elem->GetNode(( iN+1 ) % nbn);
4169 SMESH_TLink link( n1, n2 );
4170 link_nb = linkNbMap.insert( make_pair( link, 0 )).first;
4174 // remove nodes that are in links encountered only once from setMovableNodes
4175 for ( link_nb = linkNbMap.begin(); link_nb != linkNbMap.end(); ++link_nb ) {
4176 if ( link_nb->second == 1 ) {
4177 setMovableNodes.erase( link_nb->first.node1() );
4178 setMovableNodes.erase( link_nb->first.node2() );
4183 // -----------------------------------------------------
4184 // for nodes on seam edge, compute one more UV ( uvMap2 );
4185 // find movable nodes linked to nodes on seam and which
4186 // are to be smoothed using the second UV ( uvMap2 )
4187 // -----------------------------------------------------
4189 set<const SMDS_MeshNode*> nodesNearSeam; // to smooth using uvMap2
4190 if ( !surface.IsNull() ) {
4191 TopExp_Explorer eExp( face, TopAbs_EDGE );
4192 for ( ; eExp.More(); eExp.Next() ) {
4193 TopoDS_Edge edge = TopoDS::Edge( eExp.Current() );
4194 if ( !BRep_Tool::IsClosed( edge, face ))
4196 SMESHDS_SubMesh* sm = aMesh->MeshElements( edge );
4197 if ( !sm ) continue;
4198 // find out which parameter varies for a node on seam
4201 Handle(Geom2d_Curve) pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
4202 if ( pcurve.IsNull() ) continue;
4203 uv1 = pcurve->Value( f );
4205 pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
4206 if ( pcurve.IsNull() ) continue;
4207 uv2 = pcurve->Value( f );
4208 int iPar = Abs( uv1.X() - uv2.X() ) > Abs( uv1.Y() - uv2.Y() ) ? 1 : 2;
4210 if ( uv1.Coord( iPar ) > uv2.Coord( iPar ))
4211 std::swap( uv1, uv2 );
4212 // get nodes on seam and its vertices
4213 list< const SMDS_MeshNode* > seamNodes;
4214 SMDS_NodeIteratorPtr nSeamIt = sm->GetNodes();
4215 while ( nSeamIt->more() ) {
4216 const SMDS_MeshNode* node = nSeamIt->next();
4217 if ( !isQuadratic || !IsMedium( node ))
4218 seamNodes.push_back( node );
4220 TopExp_Explorer vExp( edge, TopAbs_VERTEX );
4221 for ( ; vExp.More(); vExp.Next() ) {
4222 sm = aMesh->MeshElements( vExp.Current() );
4224 nSeamIt = sm->GetNodes();
4225 while ( nSeamIt->more() )
4226 seamNodes.push_back( nSeamIt->next() );
4229 // loop on nodes on seam
4230 list< const SMDS_MeshNode* >::iterator noSeIt = seamNodes.begin();
4231 for ( ; noSeIt != seamNodes.end(); ++noSeIt ) {
4232 const SMDS_MeshNode* nSeam = *noSeIt;
4233 map< const SMDS_MeshNode*, gp_XY* >::iterator n_uv = uvMap.find( nSeam );
4234 if ( n_uv == uvMap.end() )
4237 n_uv->second->SetCoord( iPar, uv1.Coord( iPar ));
4238 // set the second UV
4239 listUV.push_back( *n_uv->second );
4240 listUV.back().SetCoord( iPar, uv2.Coord( iPar ));
4241 if ( uvMap2.empty() )
4242 uvMap2 = uvMap; // copy the uvMap contents
4243 uvMap2[ nSeam ] = &listUV.back();
4245 // collect movable nodes linked to ones on seam in nodesNearSeam
4246 SMDS_ElemIteratorPtr eIt = nSeam->GetInverseElementIterator(SMDSAbs_Face);
4247 while ( eIt->more() ) {
4248 const SMDS_MeshElement* e = eIt->next();
4249 int nbUseMap1 = 0, nbUseMap2 = 0;
4250 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
4251 int nn = 0, nbn = e->NbNodes();
4252 if(e->IsQuadratic()) nbn = nbn/2;
4253 while ( nn++ < nbn )
4255 const SMDS_MeshNode* n =
4256 static_cast<const SMDS_MeshNode*>( nIt->next() );
4258 setMovableNodes.find( n ) == setMovableNodes.end() )
4260 // add only nodes being closer to uv2 than to uv1
4261 // gp_Pnt pMid (0.5 * ( n->X() + nSeam->X() ),
4262 // 0.5 * ( n->Y() + nSeam->Y() ),
4263 // 0.5 * ( n->Z() + nSeam->Z() ));
4265 // getClosestUV( projector, pMid, uv );
4266 double x = uvMap[ n ]->Coord( iPar );
4267 if ( Abs( uv1.Coord( iPar ) - x ) >
4268 Abs( uv2.Coord( iPar ) - x )) {
4269 nodesNearSeam.insert( n );
4275 // for centroidalSmooth all element nodes must
4276 // be on one side of a seam
4277 if ( theSmoothMethod == CENTROIDAL && nbUseMap1 && nbUseMap2 ) {
4278 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
4280 while ( nn++ < nbn ) {
4281 const SMDS_MeshNode* n =
4282 static_cast<const SMDS_MeshNode*>( nIt->next() );
4283 setMovableNodes.erase( n );
4287 } // loop on nodes on seam
4288 } // loop on edge of a face
4289 } // if ( !face.IsNull() )
4291 if ( setMovableNodes.empty() ) {
4292 MESSAGE( "Face id : " << *fId << " - NO SMOOTHING: no nodes to move!!!");
4293 continue; // goto next face
4301 double maxRatio = -1., maxDisplacement = -1.;
4302 set<const SMDS_MeshNode*>::iterator nodeToMove;
4303 for ( it = 0; it < theNbIterations; it++ ) {
4304 maxDisplacement = 0.;
4305 nodeToMove = setMovableNodes.begin();
4306 for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
4307 const SMDS_MeshNode* node = (*nodeToMove);
4308 gp_XYZ aPrevPos ( node->X(), node->Y(), node->Z() );
4311 bool map2 = ( nodesNearSeam.find( node ) != nodesNearSeam.end() );
4312 if ( theSmoothMethod == LAPLACIAN )
4313 laplacianSmooth( node, surface, map2 ? uvMap2 : uvMap );
4315 centroidalSmooth( node, surface, map2 ? uvMap2 : uvMap );
4317 // node displacement
4318 gp_XYZ aNewPos ( node->X(), node->Y(), node->Z() );
4319 Standard_Real aDispl = (aPrevPos - aNewPos).SquareModulus();
4320 if ( aDispl > maxDisplacement )
4321 maxDisplacement = aDispl;
4323 // no node movement => exit
4324 //if ( maxDisplacement < 1.e-16 ) {
4325 if ( maxDisplacement < disttol ) {
4326 MESSAGE("-- no node movement --");
4330 // check elements quality
4332 list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4333 for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
4334 const SMDS_MeshElement* elem = (*elemIt);
4335 if ( !elem || elem->GetType() != SMDSAbs_Face )
4337 SMESH::Controls::TSequenceOfXYZ aPoints;
4338 if ( aQualityFunc.GetPoints( elem, aPoints )) {
4339 double aValue = aQualityFunc.GetValue( aPoints );
4340 if ( aValue > maxRatio )
4344 if ( maxRatio <= theTgtAspectRatio ) {
4345 //MESSAGE("-- quality achieved --");
4348 if (it+1 == theNbIterations) {
4349 //MESSAGE("-- Iteration limit exceeded --");
4351 } // smoothing iterations
4353 // MESSAGE(" Face id: " << *fId <<
4354 // " Nb iterstions: " << it <<
4355 // " Displacement: " << maxDisplacement <<
4356 // " Aspect Ratio " << maxRatio);
4358 // ---------------------------------------
4359 // new nodes positions are computed,
4360 // record movement in DS and set new UV
4361 // ---------------------------------------
4362 nodeToMove = setMovableNodes.begin();
4363 for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
4364 SMDS_MeshNode* node = const_cast< SMDS_MeshNode* > (*nodeToMove);
4365 aMesh->MoveNode( node, node->X(), node->Y(), node->Z() );
4366 map< const SMDS_MeshNode*, gp_XY* >::iterator node_uv = uvMap.find( node );
4367 if ( node_uv != uvMap.end() ) {
4368 gp_XY* uv = node_uv->second;
4370 ( SMDS_PositionPtr( new SMDS_FacePosition( uv->X(), uv->Y() )));
4374 // move medium nodes of quadratic elements
4377 vector<const SMDS_MeshNode*> nodes;
4379 list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4380 for ( ; elemIt != elemsOnFace.end(); ++elemIt )
4382 const SMDS_MeshElement* QF = *elemIt;
4383 if ( QF->IsQuadratic() )
4385 nodes.assign( SMDS_MeshElement::iterator( QF->interlacedNodesElemIterator() ),
4386 SMDS_MeshElement::iterator() );
4387 nodes.push_back( nodes[0] );
4389 for (size_t i = 1; i < nodes.size(); i += 2 ) // i points to a medium node
4391 if ( !surface.IsNull() )
4393 gp_XY uv1 = helper.GetNodeUV( face, nodes[i-1], nodes[i+1], &checkUV );
4394 gp_XY uv2 = helper.GetNodeUV( face, nodes[i+1], nodes[i-1], &checkUV );
4395 gp_XY uv = helper.GetMiddleUV( surface, uv1, uv2 );
4396 xyz = surface->Value( uv.X(), uv.Y() );
4399 xyz = 0.5 * ( SMESH_TNodeXYZ( nodes[i-1] ) + SMESH_TNodeXYZ( nodes[i+1] ));
4401 if (( SMESH_TNodeXYZ( nodes[i] ) - xyz.XYZ() ).Modulus() > disttol )
4402 // we have to move a medium node
4403 aMesh->MoveNode( nodes[i], xyz.X(), xyz.Y(), xyz.Z() );
4409 } // loop on face ids
4415 //=======================================================================
4416 //function : isReverse
4417 //purpose : Return true if normal of prevNodes is not co-directied with
4418 // gp_Vec(prevNodes[iNotSame],nextNodes[iNotSame]).
4419 // iNotSame is where prevNodes and nextNodes are different.
4420 // If result is true then future volume orientation is OK
4421 //=======================================================================
4423 bool isReverse(const SMDS_MeshElement* face,
4424 const vector<const SMDS_MeshNode*>& prevNodes,
4425 const vector<const SMDS_MeshNode*>& nextNodes,
4429 SMESH_TNodeXYZ pP = prevNodes[ iNotSame ];
4430 SMESH_TNodeXYZ pN = nextNodes[ iNotSame ];
4431 gp_XYZ extrDir( pN - pP ), faceNorm;
4432 SMESH_MeshAlgos::FaceNormal( face, faceNorm, /*normalized=*/false );
4434 return faceNorm * extrDir < 0.0;
4437 //================================================================================
4439 * \brief Assure that theElemSets[0] holds elements, not nodes
4441 //================================================================================
4443 void setElemsFirst( TIDSortedElemSet theElemSets[2] )
4445 if ( !theElemSets[0].empty() &&
4446 (*theElemSets[0].begin())->GetType() == SMDSAbs_Node )
4448 std::swap( theElemSets[0], theElemSets[1] );
4450 else if ( !theElemSets[1].empty() &&
4451 (*theElemSets[1].begin())->GetType() != SMDSAbs_Node )
4453 std::swap( theElemSets[0], theElemSets[1] );
4458 //=======================================================================
4460 * \brief Create elements by sweeping an element
4461 * \param elem - element to sweep
4462 * \param newNodesItVec - nodes generated from each node of the element
4463 * \param newElems - generated elements
4464 * \param nbSteps - number of sweeping steps
4465 * \param srcElements - to append elem for each generated element
4467 //=======================================================================
4469 void SMESH_MeshEditor::sweepElement(const SMDS_MeshElement* elem,
4470 const vector<TNodeOfNodeListMapItr> & newNodesItVec,
4471 list<const SMDS_MeshElement*>& newElems,
4472 const size_t nbSteps,
4473 SMESH_SequenceOfElemPtr& srcElements)
4475 SMESHDS_Mesh* aMesh = GetMeshDS();
4477 const int nbNodes = elem->NbNodes();
4478 const int nbCorners = elem->NbCornerNodes();
4479 SMDSAbs_EntityType baseType = elem->GetEntityType(); /* it can change in case of
4480 polyhedron creation !!! */
4481 // Loop on elem nodes:
4482 // find new nodes and detect same nodes indices
4483 vector < list< const SMDS_MeshNode* >::const_iterator > itNN( nbNodes );
4484 vector<const SMDS_MeshNode*> prevNod( nbNodes );
4485 vector<const SMDS_MeshNode*> nextNod( nbNodes );
4486 vector<const SMDS_MeshNode*> midlNod( nbNodes );
4488 int iNode, nbSame = 0, nbDouble = 0, iNotSameNode = 0;
4489 vector<int> sames(nbNodes);
4490 vector<bool> isSingleNode(nbNodes);
4492 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4493 TNodeOfNodeListMapItr nnIt = newNodesItVec[ iNode ];
4494 const SMDS_MeshNode* node = nnIt->first;
4495 const list< const SMDS_MeshNode* > & listNewNodes = nnIt->second;
4496 if ( listNewNodes.empty() )
4499 itNN [ iNode ] = listNewNodes.begin();
4500 prevNod[ iNode ] = node;
4501 nextNod[ iNode ] = listNewNodes.front();
4503 isSingleNode[iNode] = (listNewNodes.size()==nbSteps); /* medium node of quadratic or
4504 corner node of linear */
4505 if ( prevNod[ iNode ] != nextNod [ iNode ])
4506 nbDouble += !isSingleNode[iNode];
4508 if( iNode < nbCorners ) { // check corners only
4509 if ( prevNod[ iNode ] == nextNod [ iNode ])
4510 sames[nbSame++] = iNode;
4512 iNotSameNode = iNode;
4516 if ( nbSame == nbNodes || nbSame > 2) {
4517 MESSAGE( " Too many same nodes of element " << elem->GetID() );
4521 if ( elem->GetType() == SMDSAbs_Face && !isReverse( elem, prevNod, nextNod, iNotSameNode ))
4523 // fix nodes order to have bottom normal external
4524 if ( baseType == SMDSEntity_Polygon )
4526 std::reverse( itNN.begin(), itNN.end() );
4527 std::reverse( prevNod.begin(), prevNod.end() );
4528 std::reverse( midlNod.begin(), midlNod.end() );
4529 std::reverse( nextNod.begin(), nextNod.end() );
4530 std::reverse( isSingleNode.begin(), isSingleNode.end() );
4534 const vector<int>& ind = SMDS_MeshCell::reverseSmdsOrder( baseType, nbNodes );
4535 SMDS_MeshCell::applyInterlace( ind, itNN );
4536 SMDS_MeshCell::applyInterlace( ind, prevNod );
4537 SMDS_MeshCell::applyInterlace( ind, nextNod );
4538 SMDS_MeshCell::applyInterlace( ind, midlNod );
4539 SMDS_MeshCell::applyInterlace( ind, isSingleNode );
4542 sames[nbSame] = iNotSameNode;
4543 for ( int j = 0; j <= nbSame; ++j )
4544 for ( size_t i = 0; i < ind.size(); ++i )
4545 if ( ind[i] == sames[j] )
4550 iNotSameNode = sames[nbSame];
4554 else if ( elem->GetType() == SMDSAbs_Edge )
4556 // orient a new face same as adjacent one
4558 const SMDS_MeshElement* e;
4559 TIDSortedElemSet dummy;
4560 if (( e = SMESH_MeshAlgos::FindFaceInSet( nextNod[0], prevNod[0], dummy,dummy, &i1, &i2 )) ||
4561 ( e = SMESH_MeshAlgos::FindFaceInSet( prevNod[1], nextNod[1], dummy,dummy, &i1, &i2 )) ||
4562 ( e = SMESH_MeshAlgos::FindFaceInSet( prevNod[0], prevNod[1], dummy,dummy, &i1, &i2 )))
4564 // there is an adjacent face, check order of nodes in it
4565 bool sameOrder = ( Abs( i2 - i1 ) == 1 ) ? ( i2 > i1 ) : ( i2 < i1 );
4568 std::swap( itNN[0], itNN[1] );
4569 std::swap( prevNod[0], prevNod[1] );
4570 std::swap( nextNod[0], nextNod[1] );
4571 std::swap( isSingleNode[0], isSingleNode[1] );
4573 sames[0] = 1 - sames[0];
4574 iNotSameNode = 1 - iNotSameNode;
4579 int iSameNode = 0, iBeforeSame = 0, iAfterSame = 0, iOpposSame = 0;
4581 iSameNode = sames[ nbSame-1 ];
4582 iBeforeSame = ( iSameNode + nbCorners - 1 ) % nbCorners;
4583 iAfterSame = ( iSameNode + 1 ) % nbCorners;
4584 iOpposSame = ( iSameNode - 2 < 0 ? iSameNode + 2 : iSameNode - 2 );
4587 if ( baseType == SMDSEntity_Polygon )
4589 if ( nbNodes == 3 ) baseType = SMDSEntity_Triangle;
4590 else if ( nbNodes == 4 ) baseType = SMDSEntity_Quadrangle;
4592 else if ( baseType == SMDSEntity_Quad_Polygon )
4594 if ( nbNodes == 6 ) baseType = SMDSEntity_Quad_Triangle;
4595 else if ( nbNodes == 8 ) baseType = SMDSEntity_Quad_Quadrangle;
4598 // make new elements
4599 for ( size_t iStep = 0; iStep < nbSteps; iStep++ )
4602 for ( iNode = 0; iNode < nbNodes; iNode++ )
4604 midlNod[ iNode ] = isSingleNode[iNode] ? 0 : *itNN[ iNode ]++;
4605 nextNod[ iNode ] = *itNN[ iNode ]++;
4608 SMDS_MeshElement* aNewElem = 0;
4609 /*if(!elem->IsPoly())*/ {
4610 switch ( baseType ) {
4612 case SMDSEntity_Node: { // sweep NODE
4613 if ( nbSame == 0 ) {
4614 if ( isSingleNode[0] )
4615 aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ] );
4617 aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ], midlNod[ 0 ] );
4623 case SMDSEntity_Edge: { // sweep EDGE
4624 if ( nbDouble == 0 )
4626 if ( nbSame == 0 ) // ---> quadrangle
4627 aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
4628 nextNod[ 1 ], nextNod[ 0 ] );
4629 else // ---> triangle
4630 aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
4631 nextNod[ iNotSameNode ] );
4633 else // ---> polygon
4635 vector<const SMDS_MeshNode*> poly_nodes;
4636 poly_nodes.push_back( prevNod[0] );
4637 poly_nodes.push_back( prevNod[1] );
4638 if ( prevNod[1] != nextNod[1] )
4640 if ( midlNod[1]) poly_nodes.push_back( midlNod[1]);
4641 poly_nodes.push_back( nextNod[1] );
4643 if ( prevNod[0] != nextNod[0] )
4645 poly_nodes.push_back( nextNod[0] );
4646 if ( midlNod[0]) poly_nodes.push_back( midlNod[0]);
4648 switch ( poly_nodes.size() ) {
4650 aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ], poly_nodes[ 2 ]);
4653 aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ],
4654 poly_nodes[ 2 ], poly_nodes[ 3 ]);
4657 aNewElem = aMesh->AddPolygonalFace (poly_nodes);
4662 case SMDSEntity_Triangle: // TRIANGLE --->
4664 if ( nbDouble > 0 ) break;
4665 if ( nbSame == 0 ) // ---> pentahedron
4666 aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
4667 nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ] );
4669 else if ( nbSame == 1 ) // ---> pyramid
4670 aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
4671 nextNod[ iAfterSame ], nextNod[ iBeforeSame ],
4672 nextNod[ iSameNode ]);
4674 else // 2 same nodes: ---> tetrahedron
4675 aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
4676 nextNod[ iNotSameNode ]);
4679 case SMDSEntity_Quad_Edge: // sweep quadratic EDGE --->
4683 if ( nbDouble+nbSame == 2 )
4685 if(nbSame==0) { // ---> quadratic quadrangle
4686 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
4687 prevNod[2], midlNod[1], nextNod[2], midlNod[0]);
4689 else { //(nbSame==1) // ---> quadratic triangle
4691 return; // medium node on axis
4693 else if(sames[0]==0)
4694 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1],
4695 prevNod[2], midlNod[1], nextNod[2] );
4697 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[0],
4698 prevNod[2], nextNod[2], midlNod[0]);
4701 else if ( nbDouble == 3 )
4703 if ( nbSame == 0 ) { // ---> bi-quadratic quadrangle
4704 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
4705 prevNod[2], midlNod[1], nextNod[2], midlNod[0], midlNod[2]);
4712 case SMDSEntity_Quadrangle: { // sweep QUADRANGLE --->
4713 if ( nbDouble > 0 ) break;
4715 if ( nbSame == 0 ) // ---> hexahedron
4716 aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ], prevNod[ 3 ],
4717 nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ], nextNod[ 3 ]);
4719 else if ( nbSame == 1 ) { // ---> pyramid + pentahedron
4720 aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
4721 nextNod[ iAfterSame ], nextNod[ iBeforeSame ],
4722 nextNod[ iSameNode ]);
4723 newElems.push_back( aNewElem );
4724 aNewElem = aMesh->AddVolume (prevNod[ iAfterSame ], prevNod[ iOpposSame ],
4725 prevNod[ iBeforeSame ], nextNod[ iAfterSame ],
4726 nextNod[ iOpposSame ], nextNod[ iBeforeSame ] );
4728 else if ( nbSame == 2 ) { // ---> pentahedron
4729 if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] )
4730 // iBeforeSame is same too
4731 aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iOpposSame ],
4732 nextNod[ iOpposSame ], prevNod[ iSameNode ],
4733 prevNod[ iAfterSame ], nextNod[ iAfterSame ]);
4735 // iAfterSame is same too
4736 aNewElem = aMesh->AddVolume (prevNod[ iSameNode ], prevNod[ iBeforeSame ],
4737 nextNod[ iBeforeSame ], prevNod[ iAfterSame ],
4738 prevNod[ iOpposSame ], nextNod[ iOpposSame ]);
4742 case SMDSEntity_Quad_Triangle: // sweep (Bi)Quadratic TRIANGLE --->
4743 case SMDSEntity_BiQuad_Triangle: /* ??? */ {
4744 if ( nbDouble+nbSame != 3 ) break;
4746 // ---> pentahedron with 15 nodes
4747 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
4748 nextNod[0], nextNod[1], nextNod[2],
4749 prevNod[3], prevNod[4], prevNod[5],
4750 nextNod[3], nextNod[4], nextNod[5],
4751 midlNod[0], midlNod[1], midlNod[2]);
4753 else if(nbSame==1) {
4754 // ---> 2d order pyramid of 13 nodes
4755 int apex = iSameNode;
4756 int i0 = ( apex + 1 ) % nbCorners;
4757 int i1 = ( apex - 1 + nbCorners ) % nbCorners;
4761 aNewElem = aMesh->AddVolume(prevNod[i1], prevNod[i0],
4762 nextNod[i0], nextNod[i1], prevNod[apex],
4763 prevNod[i01], midlNod[i0],
4764 nextNod[i01], midlNod[i1],
4765 prevNod[i1a], prevNod[i0a],
4766 nextNod[i0a], nextNod[i1a]);
4768 else if(nbSame==2) {
4769 // ---> 2d order tetrahedron of 10 nodes
4770 int n1 = iNotSameNode;
4771 int n2 = ( n1 + 1 ) % nbCorners;
4772 int n3 = ( n1 + nbCorners - 1 ) % nbCorners;
4776 aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], prevNod[n3], nextNod[n1],
4777 prevNod[n12], prevNod[n23], prevNod[n31],
4778 midlNod[n1], nextNod[n12], nextNod[n31]);
4782 case SMDSEntity_Quad_Quadrangle: { // sweep Quadratic QUADRANGLE --->
4784 if ( nbDouble != 4 ) break;
4785 // ---> hexahedron with 20 nodes
4786 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
4787 nextNod[0], nextNod[1], nextNod[2], nextNod[3],
4788 prevNod[4], prevNod[5], prevNod[6], prevNod[7],
4789 nextNod[4], nextNod[5], nextNod[6], nextNod[7],
4790 midlNod[0], midlNod[1], midlNod[2], midlNod[3]);
4792 else if(nbSame==1) {
4793 // ---> pyramid + pentahedron - can not be created since it is needed
4794 // additional middle node at the center of face
4795 //INFOS( " Sweep for face " << elem->GetID() << " can not be created" );
4798 else if( nbSame == 2 ) {
4799 if ( nbDouble != 2 ) break;
4800 // ---> 2d order Pentahedron with 15 nodes
4802 if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] ) {
4803 // iBeforeSame is same too
4810 // iAfterSame is same too
4820 aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], nextNod[n2],
4821 prevNod[n4], prevNod[n5], nextNod[n5],
4822 prevNod[n12], midlNod[n2], nextNod[n12],
4823 prevNod[n45], midlNod[n5], nextNod[n45],
4824 prevNod[n14], prevNod[n25], nextNod[n25]);
4828 case SMDSEntity_BiQuad_Quadrangle: { // sweep BiQuadratic QUADRANGLE --->
4830 if( nbSame == 0 && nbDouble == 9 ) {
4831 // ---> tri-quadratic hexahedron with 27 nodes
4832 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
4833 nextNod[0], nextNod[1], nextNod[2], nextNod[3],
4834 prevNod[4], prevNod[5], prevNod[6], prevNod[7],
4835 nextNod[4], nextNod[5], nextNod[6], nextNod[7],
4836 midlNod[0], midlNod[1], midlNod[2], midlNod[3],
4837 prevNod[8], // bottom center
4838 midlNod[4], midlNod[5], midlNod[6], midlNod[7],
4839 nextNod[8], // top center
4840 midlNod[8]);// elem center
4848 case SMDSEntity_Polygon: { // sweep POLYGON
4850 if ( nbNodes == 6 && nbSame == 0 && nbDouble == 0 ) {
4851 // ---> hexagonal prism
4852 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
4853 prevNod[3], prevNod[4], prevNod[5],
4854 nextNod[0], nextNod[1], nextNod[2],
4855 nextNod[3], nextNod[4], nextNod[5]);
4859 case SMDSEntity_Ball:
4864 } // switch ( baseType )
4867 if ( !aNewElem && elem->GetType() == SMDSAbs_Face ) // try to create a polyherdal prism
4869 if ( baseType != SMDSEntity_Polygon )
4871 const std::vector<int>& ind = SMDS_MeshCell::interlacedSmdsOrder(baseType,nbNodes);
4872 SMDS_MeshCell::applyInterlace( ind, prevNod );
4873 SMDS_MeshCell::applyInterlace( ind, nextNod );
4874 SMDS_MeshCell::applyInterlace( ind, midlNod );
4875 SMDS_MeshCell::applyInterlace( ind, itNN );
4876 SMDS_MeshCell::applyInterlace( ind, isSingleNode );
4877 baseType = SMDSEntity_Polygon; // WARNING: change baseType !!!!
4879 vector<const SMDS_MeshNode*> polyedre_nodes (nbNodes*2 + 4*nbNodes);
4880 vector<int> quantities (nbNodes + 2);
4881 polyedre_nodes.clear();
4885 for (int inode = 0; inode < nbNodes; inode++)
4886 polyedre_nodes.push_back( prevNod[inode] );
4887 quantities.push_back( nbNodes );
4890 polyedre_nodes.push_back( nextNod[0] );
4891 for (int inode = nbNodes; inode-1; --inode )
4892 polyedre_nodes.push_back( nextNod[inode-1] );
4893 quantities.push_back( nbNodes );
4901 const int iQuad = elem->IsQuadratic();
4902 for (int iface = 0; iface < nbNodes; iface += 1+iQuad )
4904 const int prevNbNodes = polyedre_nodes.size(); // to detect degenerated face
4905 int inextface = (iface+1+iQuad) % nbNodes;
4906 int imid = (iface+1) % nbNodes;
4907 polyedre_nodes.push_back( prevNod[inextface] ); // 0
4908 if ( iQuad ) polyedre_nodes.push_back( prevNod[imid] ); // 4
4909 polyedre_nodes.push_back( prevNod[iface] ); // 1
4910 if ( prevNod[iface] != nextNod[iface] ) // 1 != 2
4912 if ( midlNod[ iface ]) polyedre_nodes.push_back( midlNod[ iface ]); // 5
4913 polyedre_nodes.push_back( nextNod[iface] ); // 2
4915 if ( iQuad ) polyedre_nodes.push_back( nextNod[imid] ); // 6
4916 if ( prevNod[inextface] != nextNod[inextface] ) // 0 != 3
4918 polyedre_nodes.push_back( nextNod[inextface] ); // 3
4919 if ( midlNod[ inextface ]) polyedre_nodes.push_back( midlNod[ inextface ]);// 7
4921 const int nbFaceNodes = polyedre_nodes.size() - prevNbNodes;
4922 if ( nbFaceNodes > 2 )
4923 quantities.push_back( nbFaceNodes );
4924 else // degenerated face
4925 polyedre_nodes.resize( prevNbNodes );
4927 aNewElem = aMesh->AddPolyhedralVolume (polyedre_nodes, quantities);
4929 } // try to create a polyherdal prism
4932 newElems.push_back( aNewElem );
4933 myLastCreatedElems.Append(aNewElem);
4934 srcElements.Append( elem );
4937 // set new prev nodes
4938 for ( iNode = 0; iNode < nbNodes; iNode++ )
4939 prevNod[ iNode ] = nextNod[ iNode ];
4944 //=======================================================================
4946 * \brief Create 1D and 2D elements around swept elements
4947 * \param mapNewNodes - source nodes and ones generated from them
4948 * \param newElemsMap - source elements and ones generated from them
4949 * \param elemNewNodesMap - nodes generated from each node of each element
4950 * \param elemSet - all swept elements
4951 * \param nbSteps - number of sweeping steps
4952 * \param srcElements - to append elem for each generated element
4954 //=======================================================================
4956 void SMESH_MeshEditor::makeWalls (TNodeOfNodeListMap & mapNewNodes,
4957 TTElemOfElemListMap & newElemsMap,
4958 TElemOfVecOfNnlmiMap & elemNewNodesMap,
4959 TIDSortedElemSet& elemSet,
4961 SMESH_SequenceOfElemPtr& srcElements)
4963 ASSERT( newElemsMap.size() == elemNewNodesMap.size() );
4964 SMESHDS_Mesh* aMesh = GetMeshDS();
4966 // Find nodes belonging to only one initial element - sweep them into edges.
4968 TNodeOfNodeListMapItr nList = mapNewNodes.begin();
4969 for ( ; nList != mapNewNodes.end(); nList++ )
4971 const SMDS_MeshNode* node =
4972 static_cast<const SMDS_MeshNode*>( nList->first );
4973 if ( newElemsMap.count( node ))
4974 continue; // node was extruded into edge
4975 SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator();
4976 int nbInitElems = 0;
4977 const SMDS_MeshElement* el = 0;
4978 SMDSAbs_ElementType highType = SMDSAbs_Edge; // count most complex elements only
4979 while ( eIt->more() && nbInitElems < 2 ) {
4980 const SMDS_MeshElement* e = eIt->next();
4981 SMDSAbs_ElementType type = e->GetType();
4982 if ( type == SMDSAbs_Volume ||
4986 if ( type > highType ) {
4993 if ( nbInitElems == 1 ) {
4994 bool NotCreateEdge = el && el->IsMediumNode(node);
4995 if(!NotCreateEdge) {
4996 vector<TNodeOfNodeListMapItr> newNodesItVec( 1, nList );
4997 list<const SMDS_MeshElement*> newEdges;
4998 sweepElement( node, newNodesItVec, newEdges, nbSteps, srcElements );
5003 // Make a ceiling for each element ie an equal element of last new nodes.
5004 // Find free links of faces - make edges and sweep them into faces.
5006 ElemFeatures polyFace( SMDSAbs_Face, /*isPoly=*/true ), anyFace;
5008 TTElemOfElemListMap::iterator itElem = newElemsMap.begin();
5009 TElemOfVecOfNnlmiMap::iterator itElemNodes = elemNewNodesMap.begin();
5010 for ( ; itElem != newElemsMap.end(); itElem++, itElemNodes++ )
5012 const SMDS_MeshElement* elem = itElem->first;
5013 vector<TNodeOfNodeListMapItr>& vecNewNodes = itElemNodes->second;
5015 if(itElem->second.size()==0) continue;
5017 const bool isQuadratic = elem->IsQuadratic();
5019 if ( elem->GetType() == SMDSAbs_Edge ) {
5020 // create a ceiling edge
5021 if ( !isQuadratic ) {
5022 if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
5023 vecNewNodes[ 1 ]->second.back())) {
5024 myLastCreatedElems.Append(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
5025 vecNewNodes[ 1 ]->second.back()));
5026 srcElements.Append( elem );
5030 if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
5031 vecNewNodes[ 1 ]->second.back(),
5032 vecNewNodes[ 2 ]->second.back())) {
5033 myLastCreatedElems.Append(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
5034 vecNewNodes[ 1 ]->second.back(),
5035 vecNewNodes[ 2 ]->second.back()));
5036 srcElements.Append( elem );
5040 if ( elem->GetType() != SMDSAbs_Face )
5043 bool hasFreeLinks = false;
5045 TIDSortedElemSet avoidSet;
5046 avoidSet.insert( elem );
5048 set<const SMDS_MeshNode*> aFaceLastNodes;
5049 int iNode, nbNodes = vecNewNodes.size();
5050 if ( !isQuadratic ) {
5051 // loop on the face nodes
5052 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
5053 aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
5054 // look for free links of the face
5055 int iNext = ( iNode + 1 == nbNodes ) ? 0 : iNode + 1;
5056 const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
5057 const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
5058 // check if a link n1-n2 is free
5059 if ( ! SMESH_MeshAlgos::FindFaceInSet ( n1, n2, elemSet, avoidSet )) {
5060 hasFreeLinks = true;
5061 // make a new edge and a ceiling for a new edge
5062 const SMDS_MeshElement* edge;
5063 if ( ! ( edge = aMesh->FindEdge( n1, n2 ))) {
5064 myLastCreatedElems.Append( edge = aMesh->AddEdge( n1, n2 )); // free link edge
5065 srcElements.Append( myLastCreatedElems.Last() );
5067 n1 = vecNewNodes[ iNode ]->second.back();
5068 n2 = vecNewNodes[ iNext ]->second.back();
5069 if ( !aMesh->FindEdge( n1, n2 )) {
5070 myLastCreatedElems.Append(aMesh->AddEdge( n1, n2 )); // new edge ceiling
5071 srcElements.Append( edge );
5076 else { // elem is quadratic face
5077 int nbn = nbNodes/2;
5078 for ( iNode = 0; iNode < nbn; iNode++ ) {
5079 aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
5080 int iNext = ( iNode + 1 == nbn ) ? 0 : iNode + 1;
5081 const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
5082 const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
5083 const SMDS_MeshNode* n3 = vecNewNodes[ iNode+nbn ]->first;
5084 // check if a link is free
5085 if ( ! SMESH_MeshAlgos::FindFaceInSet ( n1, n2, elemSet, avoidSet ) &&
5086 ! SMESH_MeshAlgos::FindFaceInSet ( n1, n3, elemSet, avoidSet ) &&
5087 ! SMESH_MeshAlgos::FindFaceInSet ( n3, n2, elemSet, avoidSet ) ) {
5088 hasFreeLinks = true;
5089 // make an edge and a ceiling for a new edge
5091 if ( !aMesh->FindEdge( n1, n2, n3 )) {
5092 myLastCreatedElems.Append(aMesh->AddEdge( n1, n2, n3 )); // free link edge
5093 srcElements.Append( elem );
5095 n1 = vecNewNodes[ iNode ]->second.back();
5096 n2 = vecNewNodes[ iNext ]->second.back();
5097 n3 = vecNewNodes[ iNode+nbn ]->second.back();
5098 if ( !aMesh->FindEdge( n1, n2, n3 )) {
5099 myLastCreatedElems.Append(aMesh->AddEdge( n1, n2, n3 )); // ceiling edge
5100 srcElements.Append( elem );
5104 for ( iNode = nbn; iNode < nbNodes; iNode++ ) {
5105 aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
5109 // sweep free links into faces
5111 if ( hasFreeLinks ) {
5112 list<const SMDS_MeshElement*> & newVolumes = itElem->second;
5113 int iVol, volNb, nbVolumesByStep = newVolumes.size() / nbSteps;
5115 set<const SMDS_MeshNode*> initNodeSet, topNodeSet, faceNodeSet;
5116 set<const SMDS_MeshNode*> initNodeSetNoCenter/*, topNodeSetNoCenter*/;
5117 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
5118 initNodeSet.insert( vecNewNodes[ iNode ]->first );
5119 topNodeSet .insert( vecNewNodes[ iNode ]->second.back() );
5121 if ( isQuadratic && nbNodes % 2 ) { // node set for the case of a biquadratic
5122 initNodeSetNoCenter = initNodeSet; // swept face and a not biquadratic volume
5123 initNodeSetNoCenter.erase( vecNewNodes.back()->first );
5125 for ( volNb = 0; volNb < nbVolumesByStep; volNb++ ) {
5126 list<const SMDS_MeshElement*>::iterator v = newVolumes.begin();
5127 std::advance( v, volNb );
5128 // find indices of free faces of a volume and their source edges
5129 list< int > freeInd;
5130 list< const SMDS_MeshElement* > srcEdges; // source edges of free faces
5131 SMDS_VolumeTool vTool( *v, /*ignoreCentralNodes=*/false );
5132 int iF, nbF = vTool.NbFaces();
5133 for ( iF = 0; iF < nbF; iF ++ ) {
5134 if (vTool.IsFreeFace( iF ) &&
5135 vTool.GetFaceNodes( iF, faceNodeSet ) &&
5136 initNodeSet != faceNodeSet) // except an initial face
5138 if ( nbSteps == 1 && faceNodeSet == topNodeSet )
5140 if ( faceNodeSet == initNodeSetNoCenter )
5142 freeInd.push_back( iF );
5143 // find source edge of a free face iF
5144 vector<const SMDS_MeshNode*> commonNodes; // shared by the initial and free faces
5145 vector<const SMDS_MeshNode*>::iterator lastCommom;
5146 commonNodes.resize( nbNodes, 0 );
5147 lastCommom = std::set_intersection( faceNodeSet.begin(), faceNodeSet.end(),
5148 initNodeSet.begin(), initNodeSet.end(),
5149 commonNodes.begin());
5150 if ( std::distance( commonNodes.begin(), lastCommom ) == 3 )
5151 srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1],commonNodes[2]));
5153 srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1]));
5155 if ( !srcEdges.back() )
5157 cout << "SMESH_MeshEditor::makeWalls(), no source edge found for a free face #"
5158 << iF << " of volume #" << vTool.ID() << endl;
5163 if ( freeInd.empty() )
5166 // create wall faces for all steps;
5167 // if such a face has been already created by sweep of edge,
5168 // assure that its orientation is OK
5169 for ( int iStep = 0; iStep < nbSteps; iStep++ )
5171 vTool.Set( *v, /*ignoreCentralNodes=*/false );
5172 vTool.SetExternalNormal();
5173 const int nextShift = vTool.IsForward() ? +1 : -1;
5174 list< int >::iterator ind = freeInd.begin();
5175 list< const SMDS_MeshElement* >::iterator srcEdge = srcEdges.begin();
5176 for ( ; ind != freeInd.end(); ++ind, ++srcEdge ) // loop on free faces
5178 const SMDS_MeshNode** nodes = vTool.GetFaceNodes( *ind );
5179 int nbn = vTool.NbFaceNodes( *ind );
5180 const SMDS_MeshElement * f = 0;
5181 if ( nbn == 3 ) ///// triangle
5183 f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ]);
5185 nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
5187 const SMDS_MeshNode* newOrder[3] = { nodes[ 1 - nextShift ],
5189 nodes[ 1 + nextShift ] };
5191 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5193 myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
5197 else if ( nbn == 4 ) ///// quadrangle
5199 f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ], nodes[ 3 ]);
5201 nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
5203 const SMDS_MeshNode* newOrder[4] = { nodes[ 0 ], nodes[ 2-nextShift ],
5204 nodes[ 2 ], nodes[ 2+nextShift ] };
5206 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5208 myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
5209 newOrder[ 2 ], newOrder[ 3 ]));
5212 else if ( nbn == 6 && isQuadratic ) /////// quadratic triangle
5214 f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[1], nodes[3], nodes[5] );
5216 nodes[2] != f->GetNodeWrap( f->GetNodeIndex( nodes[0] ) + 2*nextShift ))
5218 const SMDS_MeshNode* newOrder[6] = { nodes[2 - 2*nextShift],
5220 nodes[2 + 2*nextShift],
5221 nodes[3 - 2*nextShift],
5223 nodes[3 + 2*nextShift]};
5225 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5227 myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ],
5235 else if ( nbn == 8 && isQuadratic ) /////// quadratic quadrangle
5237 f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[6],
5238 nodes[1], nodes[3], nodes[5], nodes[7] );
5240 nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
5242 const SMDS_MeshNode* newOrder[8] = { nodes[0],
5243 nodes[4 - 2*nextShift],
5245 nodes[4 + 2*nextShift],
5247 nodes[5 - 2*nextShift],
5249 nodes[5 + 2*nextShift] };
5251 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5253 myLastCreatedElems.Append(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
5254 newOrder[ 2 ], newOrder[ 3 ],
5255 newOrder[ 4 ], newOrder[ 5 ],
5256 newOrder[ 6 ], newOrder[ 7 ]));
5259 else if ( nbn == 9 && isQuadratic ) /////// bi-quadratic quadrangle
5261 f = aMesh->FindElement( vector<const SMDS_MeshNode*>( nodes, nodes+nbn ),
5262 SMDSAbs_Face, /*noMedium=*/false);
5264 nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
5266 const SMDS_MeshNode* newOrder[9] = { nodes[0],
5267 nodes[4 - 2*nextShift],
5269 nodes[4 + 2*nextShift],
5271 nodes[5 - 2*nextShift],
5273 nodes[5 + 2*nextShift],
5276 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5278 myLastCreatedElems.Append(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
5279 newOrder[ 2 ], newOrder[ 3 ],
5280 newOrder[ 4 ], newOrder[ 5 ],
5281 newOrder[ 6 ], newOrder[ 7 ],
5285 else //////// polygon
5287 vector<const SMDS_MeshNode*> polygon_nodes ( nodes, nodes+nbn );
5288 const SMDS_MeshFace * f = aMesh->FindFace( polygon_nodes );
5290 nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + nextShift ))
5292 if ( !vTool.IsForward() )
5293 std::reverse( polygon_nodes.begin(), polygon_nodes.end());
5295 aMesh->ChangeElementNodes( f, &polygon_nodes[0], nbn );
5297 AddElement( polygon_nodes, polyFace.SetQuad( (*v)->IsQuadratic() ));
5301 while ( srcElements.Length() < myLastCreatedElems.Length() )
5302 srcElements.Append( *srcEdge );
5304 } // loop on free faces
5306 // go to the next volume
5308 while ( iVol++ < nbVolumesByStep ) v++;
5311 } // loop on volumes of one step
5312 } // sweep free links into faces
5314 // Make a ceiling face with a normal external to a volume
5316 // use SMDS_VolumeTool to get a correctly ordered nodes of a ceiling face
5317 SMDS_VolumeTool lastVol( itElem->second.back(), /*ignoreCentralNodes=*/false );
5318 int iF = lastVol.GetFaceIndex( aFaceLastNodes );
5320 if ( iF < 0 && isQuadratic && nbNodes % 2 ) { // remove a central node of biquadratic
5321 aFaceLastNodes.erase( vecNewNodes.back()->second.back() );
5322 iF = lastVol.GetFaceIndex( aFaceLastNodes );
5326 lastVol.SetExternalNormal();
5327 const SMDS_MeshNode** nodes = lastVol.GetFaceNodes( iF );
5328 const int nbn = lastVol.NbFaceNodes( iF );
5329 vector<const SMDS_MeshNode*> nodeVec( nodes, nodes+nbn );
5330 if ( !hasFreeLinks ||
5331 !aMesh->FindElement( nodeVec, SMDSAbs_Face, /*noMedium=*/false) )
5333 const vector<int>& interlace =
5334 SMDS_MeshCell::interlacedSmdsOrder( elem->GetEntityType(), nbn );
5335 SMDS_MeshCell::applyInterlaceRev( interlace, nodeVec );
5337 AddElement( nodeVec, anyFace.Init( elem ));
5339 while ( srcElements.Length() < myLastCreatedElems.Length() )
5340 srcElements.Append( elem );
5343 } // loop on swept elements
5346 //=======================================================================
5347 //function : RotationSweep
5349 //=======================================================================
5351 SMESH_MeshEditor::PGroupIDs
5352 SMESH_MeshEditor::RotationSweep(TIDSortedElemSet theElemSets[2],
5353 const gp_Ax1& theAxis,
5354 const double theAngle,
5355 const int theNbSteps,
5356 const double theTol,
5357 const bool theMakeGroups,
5358 const bool theMakeWalls)
5360 myLastCreatedElems.Clear();
5361 myLastCreatedNodes.Clear();
5363 // source elements for each generated one
5364 SMESH_SequenceOfElemPtr srcElems, srcNodes;
5367 aTrsf.SetRotation( theAxis, theAngle );
5369 aTrsf2.SetRotation( theAxis, theAngle/2. );
5371 gp_Lin aLine( theAxis );
5372 double aSqTol = theTol * theTol;
5374 SMESHDS_Mesh* aMesh = GetMeshDS();
5376 TNodeOfNodeListMap mapNewNodes;
5377 TElemOfVecOfNnlmiMap mapElemNewNodes;
5378 TTElemOfElemListMap newElemsMap;
5380 const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
5381 myMesh->NbFaces(ORDER_QUADRATIC) +
5382 myMesh->NbVolumes(ORDER_QUADRATIC) );
5383 // loop on theElemSets
5384 setElemsFirst( theElemSets );
5385 TIDSortedElemSet::iterator itElem;
5386 for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
5388 TIDSortedElemSet& theElems = theElemSets[ is2ndSet ];
5389 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
5390 const SMDS_MeshElement* elem = *itElem;
5391 if ( !elem || elem->GetType() == SMDSAbs_Volume )
5393 vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5394 newNodesItVec.reserve( elem->NbNodes() );
5396 // loop on elem nodes
5397 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5398 while ( itN->more() )
5400 const SMDS_MeshNode* node = cast2Node( itN->next() );
5402 gp_XYZ aXYZ( node->X(), node->Y(), node->Z() );
5404 aXYZ.Coord( coord[0], coord[1], coord[2] );
5405 bool isOnAxis = ( aLine.SquareDistance( aXYZ ) <= aSqTol );
5407 // check if a node has been already sweeped
5408 TNodeOfNodeListMapItr nIt =
5409 mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5410 list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5411 if ( listNewNodes.empty() )
5413 // check if we are to create medium nodes between corner ones
5414 bool needMediumNodes = false;
5415 if ( isQuadraticMesh )
5417 SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
5418 while (it->more() && !needMediumNodes )
5420 const SMDS_MeshElement* invElem = it->next();
5421 if ( invElem != elem && !theElems.count( invElem )) continue;
5422 needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
5423 if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
5424 needMediumNodes = true;
5429 const SMDS_MeshNode * newNode = node;
5430 for ( int i = 0; i < theNbSteps; i++ ) {
5432 if ( needMediumNodes ) // create a medium node
5434 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
5435 newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5436 myLastCreatedNodes.Append(newNode);
5437 srcNodes.Append( node );
5438 listNewNodes.push_back( newNode );
5439 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
5442 aTrsf.Transforms( coord[0], coord[1], coord[2] );
5444 // create a corner node
5445 newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5446 myLastCreatedNodes.Append(newNode);
5447 srcNodes.Append( node );
5448 listNewNodes.push_back( newNode );
5451 listNewNodes.push_back( newNode );
5452 // if ( needMediumNodes )
5453 // listNewNodes.push_back( newNode );
5457 newNodesItVec.push_back( nIt );
5459 // make new elements
5460 sweepElement( elem, newNodesItVec, newElemsMap[elem], theNbSteps, srcElems );
5465 makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElemSets[0], theNbSteps, srcElems );
5467 PGroupIDs newGroupIDs;
5468 if ( theMakeGroups )
5469 newGroupIDs = generateGroups( srcNodes, srcElems, "rotated");
5474 //=======================================================================
5475 //function : ExtrusParam
5476 //purpose : standard construction
5477 //=======================================================================
5479 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const gp_Vec& theStep,
5480 const int theNbSteps,
5481 const std::list<double>& theScales,
5482 const gp_XYZ* theBasePoint,
5484 const double theTolerance):
5486 myBaseP( Precision::Infinite(), 0, 0 ),
5487 myFlags( theFlags ),
5488 myTolerance( theTolerance ),
5489 myElemsToUse( NULL )
5491 mySteps = new TColStd_HSequenceOfReal;
5492 const double stepSize = theStep.Magnitude();
5493 for (int i=1; i<=theNbSteps; i++ )
5494 mySteps->Append( stepSize );
5496 int nbScales = theScales.size();
5499 if ( IsLinearVariation() && nbScales < theNbSteps )
5501 myScales.reserve( theNbSteps );
5502 std::list<double>::const_iterator scale = theScales.begin();
5503 double prevScale = 1.0;
5504 for ( int iSc = 1; scale != theScales.end(); ++scale, ++iSc )
5506 int iStep = int( iSc / double( nbScales ) * theNbSteps + 0.5 );
5507 int stDelta = Max( 1, iStep - myScales.size());
5508 double scDelta = ( *scale - prevScale ) / stDelta;
5509 for ( int iStep = 0; iStep < stDelta; ++iStep )
5511 myScales.push_back( prevScale + scDelta );
5512 prevScale = myScales.back();
5519 myScales.assign( theScales.begin(), theScales.end() );
5524 myBaseP = *theBasePoint;
5527 if (( theFlags & EXTRUSION_FLAG_SEW ) &&
5528 ( theTolerance > 0 ))
5530 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDirAndSew;
5534 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDir;
5538 //=======================================================================
5539 //function : ExtrusParam
5540 //purpose : steps are given explicitly
5541 //=======================================================================
5543 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const gp_Dir& theDir,
5544 Handle(TColStd_HSequenceOfReal) theSteps,
5546 const double theTolerance):
5548 mySteps( theSteps ),
5549 myFlags( theFlags ),
5550 myTolerance( theTolerance ),
5551 myElemsToUse( NULL )
5553 if (( theFlags & EXTRUSION_FLAG_SEW ) &&
5554 ( theTolerance > 0 ))
5556 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDirAndSew;
5560 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDir;
5564 //=======================================================================
5565 //function : ExtrusParam
5566 //purpose : for extrusion by normal
5567 //=======================================================================
5569 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const double theStepSize,
5570 const int theNbSteps,
5574 mySteps( new TColStd_HSequenceOfReal ),
5575 myFlags( theFlags ),
5577 myElemsToUse( NULL )
5579 for (int i = 0; i < theNbSteps; i++ )
5580 mySteps->Append( theStepSize );
5584 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByNormal1D;
5588 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByNormal2D;
5592 //=======================================================================
5593 //function : ExtrusParam::SetElementsToUse
5594 //purpose : stores elements to use for extrusion by normal, depending on
5595 // state of EXTRUSION_FLAG_USE_INPUT_ELEMS_ONLY flag;
5596 // define myBaseP for scaling
5597 //=======================================================================
5599 void SMESH_MeshEditor::ExtrusParam::SetElementsToUse( const TIDSortedElemSet& elems,
5600 const TIDSortedElemSet& nodes )
5602 myElemsToUse = ToUseInpElemsOnly() ? & elems : 0;
5604 if ( Precision::IsInfinite( myBaseP.X() )) // myBaseP not defined
5606 myBaseP.SetCoord( 0.,0.,0. );
5607 TIDSortedElemSet newNodes;
5609 const TIDSortedElemSet* elemSets[] = { &elems, &nodes };
5610 for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
5612 const TIDSortedElemSet& elements = *( elemSets[ is2ndSet ]);
5613 TIDSortedElemSet::const_iterator itElem = elements.begin();
5614 for ( ; itElem != elements.end(); itElem++ )
5616 const SMDS_MeshElement* elem = *itElem;
5617 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5618 while ( itN->more() ) {
5619 const SMDS_MeshElement* node = itN->next();
5620 if ( newNodes.insert( node ).second )
5621 myBaseP += SMESH_TNodeXYZ( node );
5625 myBaseP /= newNodes.size();
5629 //=======================================================================
5630 //function : ExtrusParam::beginStepIter
5631 //purpose : prepare iteration on steps
5632 //=======================================================================
5634 void SMESH_MeshEditor::ExtrusParam::beginStepIter( bool withMediumNodes )
5636 myWithMediumNodes = withMediumNodes;
5640 //=======================================================================
5641 //function : ExtrusParam::moreSteps
5642 //purpose : are there more steps?
5643 //=======================================================================
5645 bool SMESH_MeshEditor::ExtrusParam::moreSteps()
5647 return myNextStep <= mySteps->Length() || !myCurSteps.empty();
5649 //=======================================================================
5650 //function : ExtrusParam::nextStep
5651 //purpose : returns the next step
5652 //=======================================================================
5654 double SMESH_MeshEditor::ExtrusParam::nextStep()
5657 if ( !myCurSteps.empty() )
5659 res = myCurSteps.back();
5660 myCurSteps.pop_back();
5662 else if ( myNextStep <= mySteps->Length() )
5664 myCurSteps.push_back( mySteps->Value( myNextStep ));
5666 if ( myWithMediumNodes )
5668 myCurSteps.back() /= 2.;
5669 myCurSteps.push_back( myCurSteps.back() );
5676 //=======================================================================
5677 //function : ExtrusParam::makeNodesByDir
5678 //purpose : create nodes for standard extrusion
5679 //=======================================================================
5681 int SMESH_MeshEditor::ExtrusParam::
5682 makeNodesByDir( SMESHDS_Mesh* mesh,
5683 const SMDS_MeshNode* srcNode,
5684 std::list<const SMDS_MeshNode*> & newNodes,
5685 const bool makeMediumNodes)
5687 gp_XYZ p = SMESH_TNodeXYZ( srcNode );
5690 for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5692 p += myDir.XYZ() * nextStep();
5693 const SMDS_MeshNode * newNode = mesh->AddNode( p.X(), p.Y(), p.Z() );
5694 newNodes.push_back( newNode );
5697 if ( !myScales.empty() )
5699 if ( makeMediumNodes && myMediumScales.empty() )
5701 myMediumScales.resize( myScales.size() );
5702 double prevFactor = 1.;
5703 for ( size_t i = 0; i < myScales.size(); ++i )
5705 myMediumScales[i] = 0.5 * ( prevFactor + myScales[i] );
5706 prevFactor = myScales[i];
5709 typedef std::vector<double>::iterator ScaleIt;
5710 ScaleIt scales[] = { myScales.begin(), myMediumScales.begin() };
5712 size_t iSc = 0, nbScales = myScales.size() + myMediumScales.size();
5714 gp_XYZ center = myBaseP;
5715 std::list<const SMDS_MeshNode*>::iterator nIt = newNodes.begin();
5717 for ( beginStepIter( makeMediumNodes ); moreSteps() && ( iN < nbScales ); ++nIt, ++iN )
5719 center += myDir.XYZ() * nextStep();
5721 iSc += int( makeMediumNodes );
5722 ScaleIt& scale = scales[ iSc % 2 ];
5724 gp_XYZ xyz = SMESH_TNodeXYZ( *nIt );
5725 xyz = ( *scale * ( xyz - center )) + center;
5726 mesh->MoveNode( *nIt, xyz.X(), xyz.Y(), xyz.Z() );
5734 //=======================================================================
5735 //function : ExtrusParam::makeNodesByDirAndSew
5736 //purpose : create nodes for standard extrusion with sewing
5737 //=======================================================================
5739 int SMESH_MeshEditor::ExtrusParam::
5740 makeNodesByDirAndSew( SMESHDS_Mesh* mesh,
5741 const SMDS_MeshNode* srcNode,
5742 std::list<const SMDS_MeshNode*> & newNodes,
5743 const bool makeMediumNodes)
5745 gp_XYZ P1 = SMESH_TNodeXYZ( srcNode );
5748 for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5750 P1 += myDir.XYZ() * nextStep();
5752 // try to search in sequence of existing nodes
5753 // if myNodes.Length()>0 we 'nave to use given sequence
5754 // else - use all nodes of mesh
5755 const SMDS_MeshNode * node = 0;
5756 if ( myNodes.Length() > 0 ) {
5758 for(i=1; i<=myNodes.Length(); i++) {
5759 gp_XYZ P2 = SMESH_TNodeXYZ( myNodes.Value(i) );
5760 if (( P1 - P2 ).SquareModulus() < myTolerance * myTolerance )
5762 node = myNodes.Value(i);
5768 SMDS_NodeIteratorPtr itn = mesh->nodesIterator();
5769 while(itn->more()) {
5770 SMESH_TNodeXYZ P2( itn->next() );
5771 if (( P1 - P2 ).SquareModulus() < myTolerance * myTolerance )
5780 node = mesh->AddNode( P1.X(), P1.Y(), P1.Z() );
5782 newNodes.push_back( node );
5789 //=======================================================================
5790 //function : ExtrusParam::makeNodesByNormal2D
5791 //purpose : create nodes for extrusion using normals of faces
5792 //=======================================================================
5794 int SMESH_MeshEditor::ExtrusParam::
5795 makeNodesByNormal2D( SMESHDS_Mesh* mesh,
5796 const SMDS_MeshNode* srcNode,
5797 std::list<const SMDS_MeshNode*> & newNodes,
5798 const bool makeMediumNodes)
5800 const bool alongAvgNorm = ( myFlags & EXTRUSION_FLAG_BY_AVG_NORMAL );
5802 gp_XYZ p = SMESH_TNodeXYZ( srcNode );
5804 // get normals to faces sharing srcNode
5805 vector< gp_XYZ > norms, baryCenters;
5806 gp_XYZ norm, avgNorm( 0,0,0 );
5807 SMDS_ElemIteratorPtr faceIt = srcNode->GetInverseElementIterator( SMDSAbs_Face );
5808 while ( faceIt->more() )
5810 const SMDS_MeshElement* face = faceIt->next();
5811 if ( myElemsToUse && !myElemsToUse->count( face ))
5813 if ( SMESH_MeshAlgos::FaceNormal( face, norm, /*normalized=*/true ))
5815 norms.push_back( norm );
5817 if ( !alongAvgNorm )
5821 for ( SMDS_ElemIteratorPtr nIt = face->nodesIterator(); nIt->more(); ++nbN )
5822 bc += SMESH_TNodeXYZ( nIt->next() );
5823 baryCenters.push_back( bc / nbN );
5828 if ( norms.empty() ) return 0;
5830 double normSize = avgNorm.Modulus();
5831 if ( normSize < std::numeric_limits<double>::min() )
5834 if ( myFlags & EXTRUSION_FLAG_BY_AVG_NORMAL ) // extrude along avgNorm
5837 return makeNodesByDir( mesh, srcNode, newNodes, makeMediumNodes );
5840 avgNorm /= normSize;
5843 for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5846 double stepSize = nextStep();
5848 if ( norms.size() > 1 )
5850 for ( size_t iF = 0; iF < norms.size(); ++iF ) // loop on faces
5852 // translate plane of a face
5853 baryCenters[ iF ] += norms[ iF ] * stepSize;
5855 // find point of intersection of the face plane located at baryCenters[ iF ]
5856 // and avgNorm located at pNew
5857 double d = -( norms[ iF ] * baryCenters[ iF ]); // d of plane equation ax+by+cz+d=0
5858 double dot = ( norms[ iF ] * avgNorm );
5859 if ( dot < std::numeric_limits<double>::min() )
5860 dot = stepSize * 1e-3;
5861 double step = -( norms[ iF ] * pNew + d ) / dot;
5862 pNew += step * avgNorm;
5867 pNew += stepSize * avgNorm;
5871 const SMDS_MeshNode * newNode = mesh->AddNode( p.X(), p.Y(), p.Z() );
5872 newNodes.push_back( newNode );
5877 //=======================================================================
5878 //function : ExtrusParam::makeNodesByNormal1D
5879 //purpose : create nodes for extrusion using normals of edges
5880 //=======================================================================
5882 int SMESH_MeshEditor::ExtrusParam::
5883 makeNodesByNormal1D( SMESHDS_Mesh* mesh,
5884 const SMDS_MeshNode* srcNode,
5885 std::list<const SMDS_MeshNode*> & newNodes,
5886 const bool makeMediumNodes)
5888 throw SALOME_Exception("Extrusion 1D by Normal not implemented");
5892 //=======================================================================
5893 //function : ExtrusionSweep
5895 //=======================================================================
5897 SMESH_MeshEditor::PGroupIDs
5898 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet theElems[2],
5899 const gp_Vec& theStep,
5900 const int theNbSteps,
5901 TTElemOfElemListMap& newElemsMap,
5903 const double theTolerance)
5905 ExtrusParam aParams( theStep, theNbSteps, std::list<double>(), 0, theFlags, theTolerance );
5906 return ExtrusionSweep( theElems, aParams, newElemsMap );
5910 //=======================================================================
5911 //function : ExtrusionSweep
5913 //=======================================================================
5915 SMESH_MeshEditor::PGroupIDs
5916 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet theElemSets[2],
5917 ExtrusParam& theParams,
5918 TTElemOfElemListMap& newElemsMap)
5920 myLastCreatedElems.Clear();
5921 myLastCreatedNodes.Clear();
5923 // source elements for each generated one
5924 SMESH_SequenceOfElemPtr srcElems, srcNodes;
5926 setElemsFirst( theElemSets );
5927 const int nbSteps = theParams.NbSteps();
5928 theParams.SetElementsToUse( theElemSets[0], theElemSets[1] );
5930 TNodeOfNodeListMap mapNewNodes;
5931 TElemOfVecOfNnlmiMap mapElemNewNodes;
5933 const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
5934 myMesh->NbFaces(ORDER_QUADRATIC) +
5935 myMesh->NbVolumes(ORDER_QUADRATIC) );
5937 TIDSortedElemSet::iterator itElem;
5938 for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
5940 TIDSortedElemSet& theElems = theElemSets[ is2ndSet ];
5941 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
5943 // check element type
5944 const SMDS_MeshElement* elem = *itElem;
5945 if ( !elem || elem->GetType() == SMDSAbs_Volume )
5948 const size_t nbNodes = elem->NbNodes();
5949 vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5950 newNodesItVec.reserve( nbNodes );
5952 // loop on elem nodes
5953 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5954 while ( itN->more() )
5956 // check if a node has been already sweeped
5957 const SMDS_MeshNode* node = cast2Node( itN->next() );
5958 TNodeOfNodeListMap::iterator nIt =
5959 mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5960 list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5961 if ( listNewNodes.empty() )
5965 // check if we are to create medium nodes between corner ones
5966 bool needMediumNodes = false;
5967 if ( isQuadraticMesh )
5969 SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
5970 while (it->more() && !needMediumNodes )
5972 const SMDS_MeshElement* invElem = it->next();
5973 if ( invElem != elem && !theElems.count( invElem )) continue;
5974 needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
5975 if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
5976 needMediumNodes = true;
5979 // create nodes for all steps
5980 if ( theParams.MakeNodes( GetMeshDS(), node, listNewNodes, needMediumNodes ))
5982 list<const SMDS_MeshNode*>::iterator newNodesIt = listNewNodes.begin();
5983 for ( ; newNodesIt != listNewNodes.end(); ++newNodesIt )
5985 myLastCreatedNodes.Append( *newNodesIt );
5986 srcNodes.Append( node );
5991 break; // newNodesItVec will be shorter than nbNodes
5994 newNodesItVec.push_back( nIt );
5996 // make new elements
5997 if ( newNodesItVec.size() == nbNodes )
5998 sweepElement( elem, newNodesItVec, newElemsMap[elem], nbSteps, srcElems );
6002 if ( theParams.ToMakeBoundary() ) {
6003 makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElemSets[0], nbSteps, srcElems );
6005 PGroupIDs newGroupIDs;
6006 if ( theParams.ToMakeGroups() )
6007 newGroupIDs = generateGroups( srcNodes, srcElems, "extruded");
6012 //=======================================================================
6013 //function : ExtrusionAlongTrack
6015 //=======================================================================
6016 SMESH_MeshEditor::Extrusion_Error
6017 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet theElements[2],
6018 SMESH_subMesh* theTrack,
6019 const SMDS_MeshNode* theN1,
6020 const bool theHasAngles,
6021 list<double>& theAngles,
6022 const bool theLinearVariation,
6023 const bool theHasRefPoint,
6024 const gp_Pnt& theRefPoint,
6025 const bool theMakeGroups)
6027 myLastCreatedElems.Clear();
6028 myLastCreatedNodes.Clear();
6031 std::list<double> aPrms;
6032 TIDSortedElemSet::iterator itElem;
6035 TopoDS_Edge aTrackEdge;
6036 TopoDS_Vertex aV1, aV2;
6038 SMDS_ElemIteratorPtr aItE;
6039 SMDS_NodeIteratorPtr aItN;
6040 SMDSAbs_ElementType aTypeE;
6042 TNodeOfNodeListMap mapNewNodes;
6045 aNbE = theElements[0].size() + theElements[1].size();
6048 return EXTR_NO_ELEMENTS;
6050 // 1.1 Track Pattern
6053 SMESHDS_SubMesh* pSubMeshDS = theTrack->GetSubMeshDS();
6055 return ExtrusionAlongTrack( theElements, theTrack->GetFather(), theN1,
6056 theHasAngles, theAngles, theLinearVariation,
6057 theHasRefPoint, theRefPoint, theMakeGroups );
6059 aItE = pSubMeshDS->GetElements();
6060 while ( aItE->more() ) {
6061 const SMDS_MeshElement* pE = aItE->next();
6062 aTypeE = pE->GetType();
6063 // Pattern must contain links only
6064 if ( aTypeE != SMDSAbs_Edge )
6065 return EXTR_PATH_NOT_EDGE;
6068 list<SMESH_MeshEditor_PathPoint> fullList;
6070 const TopoDS_Shape& aS = theTrack->GetSubShape();
6071 // Sub-shape for the Pattern must be an Edge or Wire
6072 if( aS.ShapeType() == TopAbs_EDGE ) {
6073 aTrackEdge = TopoDS::Edge( aS );
6074 // the Edge must not be degenerated
6075 if ( SMESH_Algo::isDegenerated( aTrackEdge ) )
6076 return EXTR_BAD_PATH_SHAPE;
6077 TopExp::Vertices( aTrackEdge, aV1, aV2 );
6078 aItN = theTrack->GetFather()->GetSubMesh( aV1 )->GetSubMeshDS()->GetNodes();
6079 const SMDS_MeshNode* aN1 = aItN->next();
6080 aItN = theTrack->GetFather()->GetSubMesh( aV2 )->GetSubMeshDS()->GetNodes();
6081 const SMDS_MeshNode* aN2 = aItN->next();
6082 // starting node must be aN1 or aN2
6083 if ( !( aN1 == theN1 || aN2 == theN1 ) )
6084 return EXTR_BAD_STARTING_NODE;
6085 aItN = pSubMeshDS->GetNodes();
6086 while ( aItN->more() ) {
6087 const SMDS_MeshNode* pNode = aItN->next();
6088 const SMDS_EdgePosition* pEPos =
6089 static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
6090 double aT = pEPos->GetUParameter();
6091 aPrms.push_back( aT );
6093 //Extrusion_Error err =
6094 makeEdgePathPoints(aPrms, aTrackEdge, (aN1==theN1), fullList);
6095 } else if( aS.ShapeType() == TopAbs_WIRE ) {
6096 list< SMESH_subMesh* > LSM;
6097 TopTools_SequenceOfShape Edges;
6098 SMESH_subMeshIteratorPtr itSM = theTrack->getDependsOnIterator(false,true);
6099 while(itSM->more()) {
6100 SMESH_subMesh* SM = itSM->next();
6102 const TopoDS_Shape& aS = SM->GetSubShape();
6105 list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
6106 int startNid = theN1->GetID();
6107 TColStd_MapOfInteger UsedNums;
6109 int NbEdges = Edges.Length();
6111 for(; i<=NbEdges; i++) {
6113 list< SMESH_subMesh* >::iterator itLSM = LSM.begin();
6114 for(; itLSM!=LSM.end(); itLSM++) {
6116 if(UsedNums.Contains(k)) continue;
6117 aTrackEdge = TopoDS::Edge( Edges.Value(k) );
6118 SMESH_subMesh* locTrack = *itLSM;
6119 SMESHDS_SubMesh* locMeshDS = locTrack->GetSubMeshDS();
6120 TopExp::Vertices( aTrackEdge, aV1, aV2 );
6121 aItN = locTrack->GetFather()->GetSubMesh(aV1)->GetSubMeshDS()->GetNodes();
6122 const SMDS_MeshNode* aN1 = aItN->next();
6123 aItN = locTrack->GetFather()->GetSubMesh(aV2)->GetSubMeshDS()->GetNodes();
6124 const SMDS_MeshNode* aN2 = aItN->next();
6125 // starting node must be aN1 or aN2
6126 if ( !( aN1->GetID() == startNid || aN2->GetID() == startNid ) ) continue;
6127 // 2. Collect parameters on the track edge
6129 aItN = locMeshDS->GetNodes();
6130 while ( aItN->more() ) {
6131 const SMDS_MeshNode* pNode = aItN->next();
6132 const SMDS_EdgePosition* pEPos =
6133 static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
6134 double aT = pEPos->GetUParameter();
6135 aPrms.push_back( aT );
6137 list<SMESH_MeshEditor_PathPoint> LPP;
6138 //Extrusion_Error err =
6139 makeEdgePathPoints(aPrms, aTrackEdge,(aN1->GetID()==startNid), LPP);
6140 LLPPs.push_back(LPP);
6142 // update startN for search following egde
6143 if( aN1->GetID() == startNid ) startNid = aN2->GetID();
6144 else startNid = aN1->GetID();
6148 list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
6149 list<SMESH_MeshEditor_PathPoint> firstList = *itLLPP;
6150 list<SMESH_MeshEditor_PathPoint>::iterator itPP = firstList.begin();
6151 for(; itPP!=firstList.end(); itPP++) {
6152 fullList.push_back( *itPP );
6154 SMESH_MeshEditor_PathPoint PP1 = fullList.back();
6155 fullList.pop_back();
6157 for(; itLLPP!=LLPPs.end(); itLLPP++) {
6158 list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
6159 itPP = currList.begin();
6160 SMESH_MeshEditor_PathPoint PP2 = currList.front();
6161 gp_Dir D1 = PP1.Tangent();
6162 gp_Dir D2 = PP2.Tangent();
6163 gp_Dir Dnew( gp_Vec( (D1.X()+D2.X())/2, (D1.Y()+D2.Y())/2,
6164 (D1.Z()+D2.Z())/2 ) );
6165 PP1.SetTangent(Dnew);
6166 fullList.push_back(PP1);
6168 for(; itPP!=firstList.end(); itPP++) {
6169 fullList.push_back( *itPP );
6171 PP1 = fullList.back();
6172 fullList.pop_back();
6174 // if wire not closed
6175 fullList.push_back(PP1);
6179 return EXTR_BAD_PATH_SHAPE;
6182 return makeExtrElements(theElements, fullList, theHasAngles, theAngles, theLinearVariation,
6183 theHasRefPoint, theRefPoint, theMakeGroups);
6187 //=======================================================================
6188 //function : ExtrusionAlongTrack
6190 //=======================================================================
6191 SMESH_MeshEditor::Extrusion_Error
6192 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet theElements[2],
6193 SMESH_Mesh* theTrack,
6194 const SMDS_MeshNode* theN1,
6195 const bool theHasAngles,
6196 list<double>& theAngles,
6197 const bool theLinearVariation,
6198 const bool theHasRefPoint,
6199 const gp_Pnt& theRefPoint,
6200 const bool theMakeGroups)
6202 myLastCreatedElems.Clear();
6203 myLastCreatedNodes.Clear();
6206 std::list<double> aPrms;
6207 TIDSortedElemSet::iterator itElem;
6210 TopoDS_Edge aTrackEdge;
6211 TopoDS_Vertex aV1, aV2;
6213 SMDS_ElemIteratorPtr aItE;
6214 SMDS_NodeIteratorPtr aItN;
6215 SMDSAbs_ElementType aTypeE;
6217 TNodeOfNodeListMap mapNewNodes;
6220 aNbE = theElements[0].size() + theElements[1].size();
6223 return EXTR_NO_ELEMENTS;
6225 // 1.1 Track Pattern
6228 SMESHDS_Mesh* pMeshDS = theTrack->GetMeshDS();
6230 aItE = pMeshDS->elementsIterator();
6231 while ( aItE->more() ) {
6232 const SMDS_MeshElement* pE = aItE->next();
6233 aTypeE = pE->GetType();
6234 // Pattern must contain links only
6235 if ( aTypeE != SMDSAbs_Edge )
6236 return EXTR_PATH_NOT_EDGE;
6239 list<SMESH_MeshEditor_PathPoint> fullList;
6241 const TopoDS_Shape& aS = theTrack->GetShapeToMesh();
6243 if ( !theTrack->HasShapeToMesh() ) {
6244 //Mesh without shape
6245 const SMDS_MeshNode* currentNode = NULL;
6246 const SMDS_MeshNode* prevNode = theN1;
6247 std::vector<const SMDS_MeshNode*> aNodesList;
6248 aNodesList.push_back(theN1);
6249 int nbEdges = 0, conn=0;
6250 const SMDS_MeshElement* prevElem = NULL;
6251 const SMDS_MeshElement* currentElem = NULL;
6252 int totalNbEdges = theTrack->NbEdges();
6253 SMDS_ElemIteratorPtr nIt;
6256 if( !theTrack->GetMeshDS()->Contains(theN1) ) {
6257 return EXTR_BAD_STARTING_NODE;
6260 conn = nbEdgeConnectivity(theN1);
6262 return EXTR_PATH_NOT_EDGE;
6264 aItE = theN1->GetInverseElementIterator();
6265 prevElem = aItE->next();
6266 currentElem = prevElem;
6268 if(totalNbEdges == 1 ) {
6269 nIt = currentElem->nodesIterator();
6270 currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
6271 if(currentNode == prevNode)
6272 currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
6273 aNodesList.push_back(currentNode);
6275 nIt = currentElem->nodesIterator();
6276 while( nIt->more() ) {
6277 currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
6278 if(currentNode == prevNode)
6279 currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
6280 aNodesList.push_back(currentNode);
6282 //case of the closed mesh
6283 if(currentNode == theN1) {
6288 conn = nbEdgeConnectivity(currentNode);
6290 return EXTR_PATH_NOT_EDGE;
6291 }else if( conn == 1 && nbEdges > 0 ) {
6296 prevNode = currentNode;
6297 aItE = currentNode->GetInverseElementIterator();
6298 currentElem = aItE->next();
6299 if( currentElem == prevElem)
6300 currentElem = aItE->next();
6301 nIt = currentElem->nodesIterator();
6302 prevElem = currentElem;
6308 if(nbEdges != totalNbEdges)
6309 return EXTR_PATH_NOT_EDGE;
6311 TopTools_SequenceOfShape Edges;
6312 list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
6313 int startNid = theN1->GetID();
6314 for ( size_t i = 1; i < aNodesList.size(); i++ )
6316 gp_Pnt p1 = SMESH_TNodeXYZ( aNodesList[i-1] );
6317 gp_Pnt p2 = SMESH_TNodeXYZ( aNodesList[i] );
6318 TopoDS_Edge e = BRepBuilderAPI_MakeEdge( p1, p2 );
6319 list<SMESH_MeshEditor_PathPoint> LPP;
6321 makeEdgePathPoints(aPrms, e, (aNodesList[i-1]->GetID()==startNid), LPP);
6322 LLPPs.push_back(LPP);
6323 if ( aNodesList[i-1]->GetID() == startNid ) startNid = aNodesList[i ]->GetID();
6324 else startNid = aNodesList[i-1]->GetID();
6327 list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
6328 list<SMESH_MeshEditor_PathPoint> firstList = *itLLPP;
6329 list<SMESH_MeshEditor_PathPoint>::iterator itPP = firstList.begin();
6330 for(; itPP!=firstList.end(); itPP++) {
6331 fullList.push_back( *itPP );
6334 SMESH_MeshEditor_PathPoint PP1 = fullList.back();
6335 SMESH_MeshEditor_PathPoint PP2;
6336 fullList.pop_back();
6338 for(; itLLPP!=LLPPs.end(); itLLPP++) {
6339 list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
6340 itPP = currList.begin();
6341 PP2 = currList.front();
6342 gp_Dir D1 = PP1.Tangent();
6343 gp_Dir D2 = PP2.Tangent();
6344 gp_Dir Dnew( 0.5 * ( D1.XYZ() + D2.XYZ() ));
6345 PP1.SetTangent(Dnew);
6346 fullList.push_back(PP1);
6348 for(; itPP!=currList.end(); itPP++) {
6349 fullList.push_back( *itPP );
6351 PP1 = fullList.back();
6352 fullList.pop_back();
6354 fullList.push_back(PP1);
6356 } // Sub-shape for the Pattern must be an Edge or Wire
6357 else if ( aS.ShapeType() == TopAbs_EDGE )
6359 aTrackEdge = TopoDS::Edge( aS );
6360 // the Edge must not be degenerated
6361 if ( SMESH_Algo::isDegenerated( aTrackEdge ) )
6362 return EXTR_BAD_PATH_SHAPE;
6363 TopExp::Vertices( aTrackEdge, aV1, aV2 );
6364 const SMDS_MeshNode* aN1 = SMESH_Algo::VertexNode( aV1, pMeshDS );
6365 const SMDS_MeshNode* aN2 = SMESH_Algo::VertexNode( aV2, pMeshDS );
6366 // starting node must be aN1 or aN2
6367 if ( !( aN1 == theN1 || aN2 == theN1 ) )
6368 return EXTR_BAD_STARTING_NODE;
6369 aItN = pMeshDS->nodesIterator();
6370 while ( aItN->more() ) {
6371 const SMDS_MeshNode* pNode = aItN->next();
6372 if( pNode==aN1 || pNode==aN2 ) continue;
6373 const SMDS_EdgePosition* pEPos =
6374 static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
6375 double aT = pEPos->GetUParameter();
6376 aPrms.push_back( aT );
6378 //Extrusion_Error err =
6379 makeEdgePathPoints(aPrms, aTrackEdge, (aN1==theN1), fullList);
6381 else if( aS.ShapeType() == TopAbs_WIRE ) {
6382 list< SMESH_subMesh* > LSM;
6383 TopTools_SequenceOfShape Edges;
6384 TopExp_Explorer eExp(aS, TopAbs_EDGE);
6385 for(; eExp.More(); eExp.Next()) {
6386 TopoDS_Edge E = TopoDS::Edge( eExp.Current() );
6387 if( SMESH_Algo::isDegenerated(E) ) continue;
6388 SMESH_subMesh* SM = theTrack->GetSubMesh(E);
6394 list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
6395 TopoDS_Vertex aVprev;
6396 TColStd_MapOfInteger UsedNums;
6397 int NbEdges = Edges.Length();
6399 for(; i<=NbEdges; i++) {
6401 list< SMESH_subMesh* >::iterator itLSM = LSM.begin();
6402 for(; itLSM!=LSM.end(); itLSM++) {
6404 if(UsedNums.Contains(k)) continue;
6405 aTrackEdge = TopoDS::Edge( Edges.Value(k) );
6406 SMESH_subMesh* locTrack = *itLSM;
6407 SMESHDS_SubMesh* locMeshDS = locTrack->GetSubMeshDS();
6408 TopExp::Vertices( aTrackEdge, aV1, aV2 );
6409 bool aN1isOK = false, aN2isOK = false;
6410 if ( aVprev.IsNull() ) {
6411 // if previous vertex is not yet defined, it means that we in the beginning of wire
6412 // and we have to find initial vertex corresponding to starting node theN1
6413 const SMDS_MeshNode* aN1 = SMESH_Algo::VertexNode( aV1, pMeshDS );
6414 const SMDS_MeshNode* aN2 = SMESH_Algo::VertexNode( aV2, pMeshDS );
6415 // starting node must be aN1 or aN2
6416 aN1isOK = ( aN1 && aN1 == theN1 );
6417 aN2isOK = ( aN2 && aN2 == theN1 );
6420 // we have specified ending vertex of the previous edge on the previous iteration
6421 // and we have just to check that it corresponds to any vertex in current segment
6422 aN1isOK = aVprev.IsSame( aV1 );
6423 aN2isOK = aVprev.IsSame( aV2 );
6425 if ( !aN1isOK && !aN2isOK ) continue;
6426 // 2. Collect parameters on the track edge
6428 aItN = locMeshDS->GetNodes();
6429 while ( aItN->more() ) {
6430 const SMDS_MeshNode* pNode = aItN->next();
6431 const SMDS_EdgePosition* pEPos =
6432 static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
6433 double aT = pEPos->GetUParameter();
6434 aPrms.push_back( aT );
6436 list<SMESH_MeshEditor_PathPoint> LPP;
6437 //Extrusion_Error err =
6438 makeEdgePathPoints(aPrms, aTrackEdge, aN1isOK, LPP);
6439 LLPPs.push_back(LPP);
6441 // update startN for search following egde
6442 if ( aN1isOK ) aVprev = aV2;
6447 list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
6448 list<SMESH_MeshEditor_PathPoint>& firstList = *itLLPP;
6449 fullList.splice( fullList.end(), firstList );
6451 SMESH_MeshEditor_PathPoint PP1 = fullList.back();
6452 fullList.pop_back();
6454 for(; itLLPP!=LLPPs.end(); itLLPP++) {
6455 list<SMESH_MeshEditor_PathPoint>& currList = *itLLPP;
6456 SMESH_MeshEditor_PathPoint PP2 = currList.front();
6457 gp_Dir D1 = PP1.Tangent();
6458 gp_Dir D2 = PP2.Tangent();
6459 gp_Dir Dnew( D1.XYZ() + D2.XYZ() );
6460 PP1.SetTangent(Dnew);
6461 fullList.push_back(PP1);
6462 fullList.splice( fullList.end(), currList, ++currList.begin(), currList.end() );
6463 PP1 = fullList.back();
6464 fullList.pop_back();
6466 // if wire not closed
6467 fullList.push_back(PP1);
6471 return EXTR_BAD_PATH_SHAPE;
6474 return makeExtrElements(theElements, fullList, theHasAngles, theAngles, theLinearVariation,
6475 theHasRefPoint, theRefPoint, theMakeGroups);
6479 //=======================================================================
6480 //function : makeEdgePathPoints
6481 //purpose : auxiliary for ExtrusionAlongTrack
6482 //=======================================================================
6483 SMESH_MeshEditor::Extrusion_Error
6484 SMESH_MeshEditor::makeEdgePathPoints(std::list<double>& aPrms,
6485 const TopoDS_Edge& aTrackEdge,
6487 list<SMESH_MeshEditor_PathPoint>& LPP)
6489 Standard_Real aTx1, aTx2, aL2, aTolVec, aTolVec2;
6491 aTolVec2=aTolVec*aTolVec;
6493 TopoDS_Vertex aV1, aV2;
6494 TopExp::Vertices( aTrackEdge, aV1, aV2 );
6495 aT1=BRep_Tool::Parameter( aV1, aTrackEdge );
6496 aT2=BRep_Tool::Parameter( aV2, aTrackEdge );
6497 // 2. Collect parameters on the track edge
6498 aPrms.push_front( aT1 );
6499 aPrms.push_back( aT2 );
6502 if( FirstIsStart ) {
6513 SMESH_MeshEditor_PathPoint aPP;
6514 Handle(Geom_Curve) aC3D = BRep_Tool::Curve( aTrackEdge, aTx1, aTx2 );
6515 std::list<double>::iterator aItD = aPrms.begin();
6516 for(; aItD != aPrms.end(); ++aItD) {
6520 aC3D->D1( aT, aP3D, aVec );
6521 aL2 = aVec.SquareMagnitude();
6522 if ( aL2 < aTolVec2 )
6523 return EXTR_CANT_GET_TANGENT;
6524 gp_Dir aTgt( FirstIsStart ? aVec : -aVec );
6526 aPP.SetTangent( aTgt );
6527 aPP.SetParameter( aT );
6534 //=======================================================================
6535 //function : makeExtrElements
6536 //purpose : auxiliary for ExtrusionAlongTrack
6537 //=======================================================================
6538 SMESH_MeshEditor::Extrusion_Error
6539 SMESH_MeshEditor::makeExtrElements(TIDSortedElemSet theElemSets[2],
6540 list<SMESH_MeshEditor_PathPoint>& fullList,
6541 const bool theHasAngles,
6542 list<double>& theAngles,
6543 const bool theLinearVariation,
6544 const bool theHasRefPoint,
6545 const gp_Pnt& theRefPoint,
6546 const bool theMakeGroups)
6548 const int aNbTP = fullList.size();
6551 if( theHasAngles && !theAngles.empty() && theLinearVariation )
6552 linearAngleVariation(aNbTP-1, theAngles);
6554 // fill vector of path points with angles
6555 vector<SMESH_MeshEditor_PathPoint> aPPs;
6556 list<SMESH_MeshEditor_PathPoint>::iterator itPP = fullList.begin();
6557 list<double>::iterator itAngles = theAngles.begin();
6558 aPPs.push_back( *itPP++ );
6559 for( ; itPP != fullList.end(); itPP++) {
6560 aPPs.push_back( *itPP );
6561 if ( theHasAngles && itAngles != theAngles.end() )
6562 aPPs.back().SetAngle( *itAngles++ );
6565 TNodeOfNodeListMap mapNewNodes;
6566 TElemOfVecOfNnlmiMap mapElemNewNodes;
6567 TTElemOfElemListMap newElemsMap;
6568 TIDSortedElemSet::iterator itElem;
6569 // source elements for each generated one
6570 SMESH_SequenceOfElemPtr srcElems, srcNodes;
6572 // 3. Center of rotation aV0
6573 gp_Pnt aV0 = theRefPoint;
6574 if ( !theHasRefPoint )
6576 gp_XYZ aGC( 0.,0.,0. );
6577 TIDSortedElemSet newNodes;
6579 for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
6581 TIDSortedElemSet& theElements = theElemSets[ is2ndSet ];
6582 itElem = theElements.begin();
6583 for ( ; itElem != theElements.end(); itElem++ )
6585 const SMDS_MeshElement* elem = *itElem;
6586 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6587 while ( itN->more() ) {
6588 const SMDS_MeshElement* node = itN->next();
6589 if ( newNodes.insert( node ).second )
6590 aGC += SMESH_TNodeXYZ( node );
6594 aGC /= newNodes.size();
6596 } // if (!theHasRefPoint) {
6598 // 4. Processing the elements
6599 SMESHDS_Mesh* aMesh = GetMeshDS();
6600 list<const SMDS_MeshNode*> emptyList;
6602 setElemsFirst( theElemSets );
6603 for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
6605 TIDSortedElemSet& theElements = theElemSets[ is2ndSet ];
6606 for ( itElem = theElements.begin(); itElem != theElements.end(); itElem++ )
6608 const SMDS_MeshElement* elem = *itElem;
6610 vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
6611 newNodesItVec.reserve( elem->NbNodes() );
6613 // loop on elem nodes
6615 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6616 while ( itN->more() )
6619 // check if a node has been already processed
6620 const SMDS_MeshNode* node = cast2Node( itN->next() );
6621 TNodeOfNodeListMap::iterator nIt = mapNewNodes.insert( make_pair( node, emptyList )).first;
6622 list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
6623 if ( listNewNodes.empty() )
6626 Standard_Real aAngle1x, aAngleT1T0, aTolAng;
6627 gp_Pnt aP0x, aP1x, aPN0, aPN1, aV0x, aV1x;
6628 gp_Ax1 anAx1, anAxT1T0;
6629 gp_Dir aDT1x, aDT0x, aDT1T0;
6634 aPN0 = SMESH_TNodeXYZ( node );
6636 const SMESH_MeshEditor_PathPoint& aPP0 = aPPs[0];
6638 aDT0x= aPP0.Tangent();
6640 for ( int j = 1; j < aNbTP; ++j ) {
6641 const SMESH_MeshEditor_PathPoint& aPP1 = aPPs[j];
6643 aDT1x = aPP1.Tangent();
6644 aAngle1x = aPP1.Angle();
6646 gp_Trsf aTrsf, aTrsfRot, aTrsfRotT1T0;
6648 gp_Vec aV01x( aP0x, aP1x );
6649 aTrsf.SetTranslation( aV01x );
6652 aV1x = aV0x.Transformed( aTrsf );
6653 aPN1 = aPN0.Transformed( aTrsf );
6655 // rotation 1 [ T1,T0 ]
6656 aAngleT1T0=-aDT1x.Angle( aDT0x );
6657 if (fabs(aAngleT1T0) > aTolAng)
6660 anAxT1T0.SetLocation( aV1x );
6661 anAxT1T0.SetDirection( aDT1T0 );
6662 aTrsfRotT1T0.SetRotation( anAxT1T0, aAngleT1T0 );
6664 aPN1 = aPN1.Transformed( aTrsfRotT1T0 );
6668 if ( theHasAngles ) {
6669 anAx1.SetLocation( aV1x );
6670 anAx1.SetDirection( aDT1x );
6671 aTrsfRot.SetRotation( anAx1, aAngle1x );
6673 aPN1 = aPN1.Transformed( aTrsfRot );
6677 if ( elem->IsQuadratic() && !elem->IsMediumNode(node) )
6679 // create additional node
6680 gp_XYZ midP = 0.5 * ( aPN1.XYZ() + aPN0.XYZ() );
6681 const SMDS_MeshNode* newNode = aMesh->AddNode( midP.X(), midP.Y(), midP.Z() );
6682 myLastCreatedNodes.Append(newNode);
6683 srcNodes.Append( node );
6684 listNewNodes.push_back( newNode );
6686 const SMDS_MeshNode* newNode = aMesh->AddNode( aPN1.X(), aPN1.Y(), aPN1.Z() );
6687 myLastCreatedNodes.Append(newNode);
6688 srcNodes.Append( node );
6689 listNewNodes.push_back( newNode );
6697 else if( elem->IsQuadratic() && !elem->IsMediumNode(node) )
6699 // if current elem is quadratic and current node is not medium
6700 // we have to check - may be it is needed to insert additional nodes
6701 list< const SMDS_MeshNode* > & listNewNodes = nIt->second;
6702 if ((int) listNewNodes.size() == aNbTP-1 )
6704 vector<const SMDS_MeshNode*> aNodes(2*(aNbTP-1));
6705 gp_XYZ P(node->X(), node->Y(), node->Z());
6706 list< const SMDS_MeshNode* >::iterator it = listNewNodes.begin();
6708 for(i=0; i<aNbTP-1; i++) {
6709 const SMDS_MeshNode* N = *it;
6710 double x = ( N->X() + P.X() )/2.;
6711 double y = ( N->Y() + P.Y() )/2.;
6712 double z = ( N->Z() + P.Z() )/2.;
6713 const SMDS_MeshNode* newN = aMesh->AddNode(x,y,z);
6714 srcNodes.Append( node );
6715 myLastCreatedNodes.Append(newN);
6718 P = gp_XYZ(N->X(),N->Y(),N->Z());
6720 listNewNodes.clear();
6721 for(i=0; i<2*(aNbTP-1); i++) {
6722 listNewNodes.push_back(aNodes[i]);
6727 newNodesItVec.push_back( nIt );
6730 // make new elements
6731 sweepElement( elem, newNodesItVec, newElemsMap[elem], aNbTP-1, srcElems );
6735 makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElemSets[0], aNbTP-1, srcElems );
6737 if ( theMakeGroups )
6738 generateGroups( srcNodes, srcElems, "extruded");
6744 //=======================================================================
6745 //function : linearAngleVariation
6746 //purpose : spread values over nbSteps
6747 //=======================================================================
6749 void SMESH_MeshEditor::linearAngleVariation(const int nbSteps,
6750 list<double>& Angles)
6752 int nbAngles = Angles.size();
6753 if( nbSteps > nbAngles && nbAngles > 0 )
6755 vector<double> theAngles(nbAngles);
6756 theAngles.assign( Angles.begin(), Angles.end() );
6759 double rAn2St = double( nbAngles ) / double( nbSteps );
6760 double angPrev = 0, angle;
6761 for ( int iSt = 0; iSt < nbSteps; ++iSt )
6763 double angCur = rAn2St * ( iSt+1 );
6764 double angCurFloor = floor( angCur );
6765 double angPrevFloor = floor( angPrev );
6766 if ( angPrevFloor == angCurFloor )
6767 angle = rAn2St * theAngles[ int( angCurFloor ) ];
6769 int iP = int( angPrevFloor );
6770 double angPrevCeil = ceil(angPrev);
6771 angle = ( angPrevCeil - angPrev ) * theAngles[ iP ];
6773 int iC = int( angCurFloor );
6774 if ( iC < nbAngles )
6775 angle += ( angCur - angCurFloor ) * theAngles[ iC ];
6777 iP = int( angPrevCeil );
6779 angle += theAngles[ iC ];
6781 res.push_back(angle);
6789 //================================================================================
6791 * \brief Move or copy theElements applying theTrsf to their nodes
6792 * \param theElems - elements to transform, if theElems is empty then apply to all mesh nodes
6793 * \param theTrsf - transformation to apply
6794 * \param theCopy - if true, create translated copies of theElems
6795 * \param theMakeGroups - if true and theCopy, create translated groups
6796 * \param theTargetMesh - mesh to copy translated elements into
6797 * \return SMESH_MeshEditor::PGroupIDs - list of ids of created groups
6799 //================================================================================
6801 SMESH_MeshEditor::PGroupIDs
6802 SMESH_MeshEditor::Transform (TIDSortedElemSet & theElems,
6803 const gp_Trsf& theTrsf,
6805 const bool theMakeGroups,
6806 SMESH_Mesh* theTargetMesh)
6808 myLastCreatedElems.Clear();
6809 myLastCreatedNodes.Clear();
6811 bool needReverse = false;
6812 string groupPostfix;
6813 switch ( theTrsf.Form() ) {
6816 groupPostfix = "mirrored";
6819 groupPostfix = "mirrored";
6823 groupPostfix = "mirrored";
6826 groupPostfix = "rotated";
6828 case gp_Translation:
6829 groupPostfix = "translated";
6832 groupPostfix = "scaled";
6834 case gp_CompoundTrsf: // different scale by axis
6835 groupPostfix = "scaled";
6838 needReverse = false;
6839 groupPostfix = "transformed";
6842 SMESHDS_Mesh* aTgtMesh = theTargetMesh ? theTargetMesh->GetMeshDS() : 0;
6843 SMESHDS_Mesh* aMesh = GetMeshDS();
6845 SMESH_MeshEditor targetMeshEditor( theTargetMesh );
6846 SMESH_MeshEditor* editor = theTargetMesh ? & targetMeshEditor : theCopy ? this : 0;
6847 SMESH_MeshEditor::ElemFeatures elemType;
6849 // map old node to new one
6850 TNodeNodeMap nodeMap;
6852 // elements sharing moved nodes; those of them which have all
6853 // nodes mirrored but are not in theElems are to be reversed
6854 TIDSortedElemSet inverseElemSet;
6856 // source elements for each generated one
6857 SMESH_SequenceOfElemPtr srcElems, srcNodes;
6859 // issue 021015: EDF 1578 SMESH: Free nodes are removed when translating a mesh
6860 TIDSortedElemSet orphanNode;
6862 if ( theElems.empty() ) // transform the whole mesh
6865 SMDS_ElemIteratorPtr eIt = aMesh->elementsIterator();
6866 while ( eIt->more() ) theElems.insert( eIt->next() );
6868 SMDS_NodeIteratorPtr nIt = aMesh->nodesIterator();
6869 while ( nIt->more() )
6871 const SMDS_MeshNode* node = nIt->next();
6872 if ( node->NbInverseElements() == 0)
6873 orphanNode.insert( node );
6877 // loop on elements to transform nodes : first orphan nodes then elems
6878 TIDSortedElemSet::iterator itElem;
6879 TIDSortedElemSet *elements[] = { &orphanNode, &theElems };
6880 for (int i=0; i<2; i++)
6881 for ( itElem = elements[i]->begin(); itElem != elements[i]->end(); itElem++ )
6883 const SMDS_MeshElement* elem = *itElem;
6887 // loop on elem nodes
6889 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6890 while ( itN->more() )
6892 const SMDS_MeshNode* node = cast2Node( itN->next() );
6893 // check if a node has been already transformed
6894 pair<TNodeNodeMap::iterator,bool> n2n_isnew =
6895 nodeMap.insert( make_pair ( node, node ));
6896 if ( !n2n_isnew.second )
6899 node->GetXYZ( coord );
6900 theTrsf.Transforms( coord[0], coord[1], coord[2] );
6901 if ( theTargetMesh ) {
6902 const SMDS_MeshNode * newNode = aTgtMesh->AddNode( coord[0], coord[1], coord[2] );
6903 n2n_isnew.first->second = newNode;
6904 myLastCreatedNodes.Append(newNode);
6905 srcNodes.Append( node );
6907 else if ( theCopy ) {
6908 const SMDS_MeshNode * newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
6909 n2n_isnew.first->second = newNode;
6910 myLastCreatedNodes.Append(newNode);
6911 srcNodes.Append( node );
6914 aMesh->MoveNode( node, coord[0], coord[1], coord[2] );
6915 // node position on shape becomes invalid
6916 const_cast< SMDS_MeshNode* > ( node )->SetPosition
6917 ( SMDS_SpacePosition::originSpacePosition() );
6920 // keep inverse elements
6921 if ( !theCopy && !theTargetMesh && needReverse ) {
6922 SMDS_ElemIteratorPtr invElemIt = node->GetInverseElementIterator();
6923 while ( invElemIt->more() ) {
6924 const SMDS_MeshElement* iel = invElemIt->next();
6925 inverseElemSet.insert( iel );
6929 } // loop on elems in { &orphanNode, &theElems };
6931 // either create new elements or reverse mirrored ones
6932 if ( !theCopy && !needReverse && !theTargetMesh )
6935 theElems.insert( inverseElemSet.begin(),inverseElemSet.end() );
6937 // Replicate or reverse elements
6939 std::vector<int> iForw;
6940 vector<const SMDS_MeshNode*> nodes;
6941 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
6943 const SMDS_MeshElement* elem = *itElem;
6944 if ( !elem ) continue;
6946 SMDSAbs_GeometryType geomType = elem->GetGeomType();
6947 size_t nbNodes = elem->NbNodes();
6948 if ( geomType == SMDSGeom_NONE ) continue; // node
6950 nodes.resize( nbNodes );
6952 if ( geomType == SMDSGeom_POLYHEDRA ) // ------------------ polyhedral volume
6954 const SMDS_VtkVolume* aPolyedre = dynamic_cast<const SMDS_VtkVolume*>( elem );
6958 bool allTransformed = true;
6959 int nbFaces = aPolyedre->NbFaces();
6960 for (int iface = 1; iface <= nbFaces && allTransformed; iface++)
6962 int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
6963 for (int inode = 1; inode <= nbFaceNodes && allTransformed; inode++)
6965 const SMDS_MeshNode* node = aPolyedre->GetFaceNode(iface, inode);
6966 TNodeNodeMap::iterator nodeMapIt = nodeMap.find(node);
6967 if ( nodeMapIt == nodeMap.end() )
6968 allTransformed = false; // not all nodes transformed
6970 nodes.push_back((*nodeMapIt).second);
6972 if ( needReverse && allTransformed )
6973 std::reverse( nodes.end() - nbFaceNodes, nodes.end() );
6975 if ( !allTransformed )
6976 continue; // not all nodes transformed
6978 else // ----------------------- the rest element types
6980 while ( iForw.size() < nbNodes ) iForw.push_back( iForw.size() );
6981 const vector<int>& iRev = SMDS_MeshCell::reverseSmdsOrder( elem->GetEntityType(), nbNodes );
6982 const vector<int>& i = needReverse ? iRev : iForw;
6984 // find transformed nodes
6986 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6987 while ( itN->more() ) {
6988 const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( itN->next() );
6989 TNodeNodeMap::iterator nodeMapIt = nodeMap.find( node );
6990 if ( nodeMapIt == nodeMap.end() )
6991 break; // not all nodes transformed
6992 nodes[ i [ iNode++ ]] = (*nodeMapIt).second;
6994 if ( iNode != nbNodes )
6995 continue; // not all nodes transformed
6999 // copy in this or a new mesh
7000 if ( editor->AddElement( nodes, elemType.Init( elem, /*basicOnly=*/false )))
7001 srcElems.Append( elem );
7004 // reverse element as it was reversed by transformation
7006 aMesh->ChangeElementNodes( elem, &nodes[0], nbNodes );
7009 } // loop on elements
7011 if ( editor && editor != this )
7012 myLastCreatedElems = editor->myLastCreatedElems;
7014 PGroupIDs newGroupIDs;
7016 if ( ( theMakeGroups && theCopy ) ||
7017 ( theMakeGroups && theTargetMesh ) )
7018 newGroupIDs = generateGroups( srcNodes, srcElems, groupPostfix, theTargetMesh, false );
7023 //=======================================================================
7025 * \brief Create groups of elements made during transformation
7026 * \param nodeGens - nodes making corresponding myLastCreatedNodes
7027 * \param elemGens - elements making corresponding myLastCreatedElems
7028 * \param postfix - to append to names of new groups
7029 * \param targetMesh - mesh to create groups in
7030 * \param topPresent - is there "top" elements that are created by sweeping
7032 //=======================================================================
7034 SMESH_MeshEditor::PGroupIDs
7035 SMESH_MeshEditor::generateGroups(const SMESH_SequenceOfElemPtr& nodeGens,
7036 const SMESH_SequenceOfElemPtr& elemGens,
7037 const std::string& postfix,
7038 SMESH_Mesh* targetMesh,
7039 const bool topPresent)
7041 PGroupIDs newGroupIDs( new list<int> );
7042 SMESH_Mesh* mesh = targetMesh ? targetMesh : GetMesh();
7044 // Sort existing groups by types and collect their names
7046 // containers to store an old group and generated new ones;
7047 // 1st new group is for result elems of different type than a source one;
7048 // 2nd new group is for same type result elems ("top" group at extrusion)
7050 using boost::make_tuple;
7051 typedef tuple< SMESHDS_GroupBase*, SMESHDS_Group*, SMESHDS_Group* > TOldNewGroup;
7052 vector< list< TOldNewGroup > > groupsByType( SMDSAbs_NbElementTypes );
7053 vector< TOldNewGroup* > orderedOldNewGroups; // in order of old groups
7055 set< string > groupNames;
7057 SMESH_Mesh::GroupIteratorPtr groupIt = GetMesh()->GetGroups();
7058 if ( !groupIt->more() ) return newGroupIDs;
7060 int newGroupID = mesh->GetGroupIds().back()+1;
7061 while ( groupIt->more() )
7063 SMESH_Group * group = groupIt->next();
7064 if ( !group ) continue;
7065 SMESHDS_GroupBase* groupDS = group->GetGroupDS();
7066 if ( !groupDS || groupDS->IsEmpty() ) continue;
7067 groupNames.insert ( group->GetName() );
7068 groupDS->SetStoreName( group->GetName() );
7069 const SMDSAbs_ElementType type = groupDS->GetType();
7070 SMESHDS_Group* newGroup = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
7071 SMESHDS_Group* newTopGroup = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
7072 groupsByType[ type ].push_back( make_tuple( groupDS, newGroup, newTopGroup ));
7073 orderedOldNewGroups.push_back( & groupsByType[ type ].back() );
7076 // Loop on nodes and elements to add them in new groups
7078 vector< const SMDS_MeshElement* > resultElems;
7079 for ( int isNodes = 0; isNodes < 2; ++isNodes )
7081 const SMESH_SequenceOfElemPtr& gens = isNodes ? nodeGens : elemGens;
7082 const SMESH_SequenceOfElemPtr& elems = isNodes ? myLastCreatedNodes : myLastCreatedElems;
7083 if ( gens.Length() != elems.Length() )
7084 throw SALOME_Exception("SMESH_MeshEditor::generateGroups(): invalid args");
7086 // loop on created elements
7087 for (int iElem = 1; iElem <= elems.Length(); ++iElem )
7089 const SMDS_MeshElement* sourceElem = gens( iElem );
7090 if ( !sourceElem ) {
7091 MESSAGE("generateGroups(): NULL source element");
7094 list< TOldNewGroup > & groupsOldNew = groupsByType[ sourceElem->GetType() ];
7095 if ( groupsOldNew.empty() ) { // no groups of this type at all
7096 while ( iElem < gens.Length() && gens( iElem+1 ) == sourceElem )
7097 ++iElem; // skip all elements made by sourceElem
7100 // collect all elements made by the iElem-th sourceElem
7101 resultElems.clear();
7102 if ( const SMDS_MeshElement* resElem = elems( iElem ))
7103 if ( resElem != sourceElem )
7104 resultElems.push_back( resElem );
7105 while ( iElem < gens.Length() && gens( iElem+1 ) == sourceElem )
7106 if ( const SMDS_MeshElement* resElem = elems( ++iElem ))
7107 if ( resElem != sourceElem )
7108 resultElems.push_back( resElem );
7110 const SMDS_MeshElement* topElem = 0;
7111 if ( isNodes ) // there must be a top element
7113 topElem = resultElems.back();
7114 resultElems.pop_back();
7118 vector< const SMDS_MeshElement* >::reverse_iterator resElemIt = resultElems.rbegin();
7119 for ( ; resElemIt != resultElems.rend() ; ++resElemIt )
7120 if ( (*resElemIt)->GetType() == sourceElem->GetType() )
7122 topElem = *resElemIt;
7123 *resElemIt = 0; // erase *resElemIt
7127 // add resultElems to groups originted from ones the sourceElem belongs to
7128 list< TOldNewGroup >::iterator gOldNew, gLast = groupsOldNew.end();
7129 for ( gOldNew = groupsOldNew.begin(); gOldNew != gLast; ++gOldNew )
7131 SMESHDS_GroupBase* oldGroup = gOldNew->get<0>();
7132 if ( oldGroup->Contains( sourceElem )) // sourceElem is in oldGroup
7134 // fill in a new group
7135 SMDS_MeshGroup & newGroup = gOldNew->get<1>()->SMDSGroup();
7136 vector< const SMDS_MeshElement* >::iterator resLast = resultElems.end(), resElemIt;
7137 for ( resElemIt = resultElems.begin(); resElemIt != resLast; ++resElemIt )
7139 newGroup.Add( *resElemIt );
7141 // fill a "top" group
7144 SMDS_MeshGroup & newTopGroup = gOldNew->get<2>()->SMDSGroup();
7145 newTopGroup.Add( topElem );
7149 } // loop on created elements
7150 }// loop on nodes and elements
7152 // Create new SMESH_Groups from SMESHDS_Groups and remove empty SMESHDS_Groups
7154 list<int> topGrouIds;
7155 for ( size_t i = 0; i < orderedOldNewGroups.size(); ++i )
7157 SMESHDS_GroupBase* oldGroupDS = orderedOldNewGroups[i]->get<0>();
7158 SMESHDS_Group* newGroups[2] = { orderedOldNewGroups[i]->get<1>(),
7159 orderedOldNewGroups[i]->get<2>() };
7160 for ( int is2nd = 0; is2nd < 2; ++is2nd )
7162 SMESHDS_Group* newGroupDS = newGroups[ is2nd ];
7163 if ( newGroupDS->IsEmpty() )
7165 mesh->GetMeshDS()->RemoveGroup( newGroupDS );
7170 newGroupDS->SetType( newGroupDS->GetElements()->next()->GetType() );
7173 const bool isTop = ( topPresent &&
7174 newGroupDS->GetType() == oldGroupDS->GetType() &&
7177 string name = oldGroupDS->GetStoreName();
7178 { // remove trailing whitespaces (issue 22599)
7179 size_t size = name.size();
7180 while ( size > 1 && isspace( name[ size-1 ]))
7182 if ( size != name.size() )
7184 name.resize( size );
7185 oldGroupDS->SetStoreName( name.c_str() );
7188 if ( !targetMesh ) {
7189 string suffix = ( isTop ? "top": postfix.c_str() );
7193 while ( !groupNames.insert( name ).second ) // name exists
7194 name = SMESH_Comment( oldGroupDS->GetStoreName() ) << "_" << suffix << "_" << nb++;
7199 newGroupDS->SetStoreName( name.c_str() );
7201 // make a SMESH_Groups
7202 mesh->AddGroup( newGroupDS );
7204 topGrouIds.push_back( newGroupDS->GetID() );
7206 newGroupIDs->push_back( newGroupDS->GetID() );
7210 newGroupIDs->splice( newGroupIDs->end(), topGrouIds );
7215 //================================================================================
7217 * * \brief Return list of group of nodes close to each other within theTolerance
7218 * * Search among theNodes or in the whole mesh if theNodes is empty using
7219 * * an Octree algorithm
7220 * \param [in,out] theNodes - the nodes to treat
7221 * \param [in] theTolerance - the tolerance
7222 * \param [out] theGroupsOfNodes - the result groups of coincident nodes
7223 * \param [in] theSeparateCornersAndMedium - if \c true, in quadratic mesh puts
7224 * corner and medium nodes in separate groups
7226 //================================================================================
7228 void SMESH_MeshEditor::FindCoincidentNodes (TIDSortedNodeSet & theNodes,
7229 const double theTolerance,
7230 TListOfListOfNodes & theGroupsOfNodes,
7231 bool theSeparateCornersAndMedium)
7233 myLastCreatedElems.Clear();
7234 myLastCreatedNodes.Clear();
7236 if ( myMesh->NbEdges ( ORDER_QUADRATIC ) +
7237 myMesh->NbFaces ( ORDER_QUADRATIC ) +
7238 myMesh->NbVolumes( ORDER_QUADRATIC ) == 0 )
7239 theSeparateCornersAndMedium = false;
7241 TIDSortedNodeSet& corners = theNodes;
7242 TIDSortedNodeSet medium;
7244 if ( theNodes.empty() ) // get all nodes in the mesh
7246 TIDSortedNodeSet* nodes[2] = { &corners, &medium };
7247 SMDS_NodeIteratorPtr nIt = GetMeshDS()->nodesIterator(/*idInceasingOrder=*/true);
7248 if ( theSeparateCornersAndMedium )
7249 while ( nIt->more() )
7251 const SMDS_MeshNode* n = nIt->next();
7252 TIDSortedNodeSet* & nodeSet = nodes[ SMESH_MesherHelper::IsMedium( n )];
7253 nodeSet->insert( nodeSet->end(), n );
7256 while ( nIt->more() )
7257 theNodes.insert( theNodes.end(), nIt->next() );
7259 else if ( theSeparateCornersAndMedium ) // separate corners from medium nodes
7261 TIDSortedNodeSet::iterator nIt = corners.begin();
7262 while ( nIt != corners.end() )
7263 if ( SMESH_MesherHelper::IsMedium( *nIt ))
7265 medium.insert( medium.end(), *nIt );
7266 corners.erase( nIt++ );
7274 if ( !corners.empty() )
7275 SMESH_OctreeNode::FindCoincidentNodes ( corners, &theGroupsOfNodes, theTolerance );
7276 if ( !medium.empty() )
7277 SMESH_OctreeNode::FindCoincidentNodes ( medium, &theGroupsOfNodes, theTolerance );
7280 //=======================================================================
7281 //function : SimplifyFace
7282 //purpose : split a chain of nodes into several closed chains
7283 //=======================================================================
7285 int SMESH_MeshEditor::SimplifyFace (const vector<const SMDS_MeshNode *>& faceNodes,
7286 vector<const SMDS_MeshNode *>& poly_nodes,
7287 vector<int>& quantities) const
7289 int nbNodes = faceNodes.size();
7290 while ( faceNodes[ 0 ] == faceNodes[ nbNodes-1 ] && nbNodes > 2 )
7294 size_t prevNbQuant = quantities.size();
7296 vector< const SMDS_MeshNode* > simpleNodes; simpleNodes.reserve( nbNodes );
7297 map< const SMDS_MeshNode*, int > nodeIndices; // indices within simpleNodes
7298 map< const SMDS_MeshNode*, int >::iterator nInd;
7300 nodeIndices.insert( make_pair( faceNodes[0], 0 ));
7301 simpleNodes.push_back( faceNodes[0] );
7302 for ( int iCur = 1; iCur < nbNodes; iCur++ )
7304 if ( faceNodes[ iCur ] != simpleNodes.back() )
7306 int index = simpleNodes.size();
7307 nInd = nodeIndices.insert( make_pair( faceNodes[ iCur ], index )).first;
7308 int prevIndex = nInd->second;
7309 if ( prevIndex < index )
7312 int loopLen = index - prevIndex;
7315 // store the sub-loop
7316 quantities.push_back( loopLen );
7317 for ( int i = prevIndex; i < index; i++ )
7318 poly_nodes.push_back( simpleNodes[ i ]);
7320 simpleNodes.resize( prevIndex+1 );
7324 simpleNodes.push_back( faceNodes[ iCur ]);
7329 if ( simpleNodes.size() > 2 )
7331 quantities.push_back( simpleNodes.size() );
7332 poly_nodes.insert ( poly_nodes.end(), simpleNodes.begin(), simpleNodes.end() );
7335 return quantities.size() - prevNbQuant;
7338 //=======================================================================
7339 //function : MergeNodes
7340 //purpose : In each group, the cdr of nodes are substituted by the first one
7342 //=======================================================================
7344 void SMESH_MeshEditor::MergeNodes (TListOfListOfNodes & theGroupsOfNodes,
7345 const bool theAvoidMakingHoles)
7347 myLastCreatedElems.Clear();
7348 myLastCreatedNodes.Clear();
7350 SMESHDS_Mesh* mesh = GetMeshDS();
7352 TNodeNodeMap nodeNodeMap; // node to replace - new node
7353 set<const SMDS_MeshElement*> elems; // all elements with changed nodes
7354 list< int > rmElemIds, rmNodeIds;
7355 vector< ElemFeatures > newElemDefs;
7357 // Fill nodeNodeMap and elems
7359 TListOfListOfNodes::iterator grIt = theGroupsOfNodes.begin();
7360 for ( ; grIt != theGroupsOfNodes.end(); grIt++ )
7362 list<const SMDS_MeshNode*>& nodes = *grIt;
7363 list<const SMDS_MeshNode*>::iterator nIt = nodes.begin();
7364 const SMDS_MeshNode* nToKeep = *nIt;
7365 for ( ++nIt; nIt != nodes.end(); nIt++ )
7367 const SMDS_MeshNode* nToRemove = *nIt;
7368 nodeNodeMap.insert( make_pair( nToRemove, nToKeep ));
7369 SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
7370 while ( invElemIt->more() ) {
7371 const SMDS_MeshElement* elem = invElemIt->next();
7377 // Apply recursive replacements (BUG 0020185)
7378 TNodeNodeMap::iterator nnIt = nodeNodeMap.begin();
7379 for ( ; nnIt != nodeNodeMap.end(); ++nnIt )
7381 const SMDS_MeshNode* nToKeep = nnIt->second;
7382 TNodeNodeMap::iterator nnIt_i = nodeNodeMap.find( nToKeep );
7383 while ( nnIt_i != nodeNodeMap.end() && nnIt_i->second != nnIt->second )
7384 nToKeep = nnIt_i->second;
7385 nnIt->second = nToKeep;
7388 if ( theAvoidMakingHoles )
7390 // find elements whose topology changes
7392 vector<const SMDS_MeshElement*> pbElems;
7393 set<const SMDS_MeshElement*>::iterator eIt = elems.begin();
7394 for ( ; eIt != elems.end(); ++eIt )
7396 const SMDS_MeshElement* elem = *eIt;
7397 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
7398 while ( itN->more() )
7400 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( itN->next() );
7401 TNodeNodeMap::iterator nnIt = nodeNodeMap.find( n );
7402 if ( nnIt != nodeNodeMap.end() && elem->GetNodeIndex( nnIt->second ) >= 0 )
7404 // several nodes of elem stick
7405 pbElems.push_back( elem );
7410 // exclude from merge nodes causing spoiling element
7411 for ( size_t iLoop = 0; iLoop < pbElems.size(); ++iLoop ) // avoid infinite cycle
7413 bool nodesExcluded = false;
7414 for ( size_t i = 0; i < pbElems.size(); ++i )
7416 size_t prevNbMergeNodes = nodeNodeMap.size();
7417 if ( !applyMerge( pbElems[i], newElemDefs, nodeNodeMap, /*noHoles=*/true ) &&
7418 prevNbMergeNodes < nodeNodeMap.size() )
7419 nodesExcluded = true;
7421 if ( !nodesExcluded )
7426 for ( nnIt = nodeNodeMap.begin(); nnIt != nodeNodeMap.end(); ++nnIt )
7428 const SMDS_MeshNode* nToRemove = nnIt->first;
7429 const SMDS_MeshNode* nToKeep = nnIt->second;
7430 if ( nToRemove != nToKeep )
7432 rmNodeIds.push_back( nToRemove->GetID() );
7433 AddToSameGroups( nToKeep, nToRemove, mesh );
7434 // set _alwaysComputed to a sub-mesh of VERTEX to enable further mesh computing
7435 // w/o creating node in place of merged ones.
7436 const SMDS_PositionPtr& pos = nToRemove->GetPosition();
7437 if ( pos && pos->GetTypeOfPosition() == SMDS_TOP_VERTEX )
7438 if ( SMESH_subMesh* sm = myMesh->GetSubMeshContaining( nToRemove->getshapeId() ))
7439 sm->SetIsAlwaysComputed( true );
7443 // Change element nodes or remove an element
7445 set<const SMDS_MeshElement*>::iterator eIt = elems.begin();
7446 for ( ; eIt != elems.end(); eIt++ )
7448 const SMDS_MeshElement* elem = *eIt;
7449 SMESHDS_SubMesh* sm = mesh->MeshElements( elem->getshapeId() );
7451 bool keepElem = applyMerge( elem, newElemDefs, nodeNodeMap, /*noHoles=*/false );
7453 rmElemIds.push_back( elem->GetID() );
7455 for ( size_t i = 0; i < newElemDefs.size(); ++i )
7457 if ( i > 0 || !mesh->ChangeElementNodes( elem,
7458 & newElemDefs[i].myNodes[0],
7459 newElemDefs[i].myNodes.size() ))
7463 newElemDefs[i].SetID( elem->GetID() );
7464 mesh->RemoveFreeElement(elem, sm, /*fromGroups=*/false);
7465 if ( !keepElem ) rmElemIds.pop_back();
7469 newElemDefs[i].SetID( -1 );
7471 SMDS_MeshElement* newElem = this->AddElement( newElemDefs[i].myNodes, newElemDefs[i] );
7472 if ( sm && newElem )
7473 sm->AddElement( newElem );
7474 if ( elem != newElem )
7475 ReplaceElemInGroups( elem, newElem, mesh );
7480 // Remove bad elements, then equal nodes (order important)
7481 Remove( rmElemIds, /*isNodes=*/false );
7482 Remove( rmNodeIds, /*isNodes=*/true );
7487 //=======================================================================
7488 //function : applyMerge
7489 //purpose : Compute new connectivity of an element after merging nodes
7490 // \param [in] elems - the element
7491 // \param [out] newElemDefs - definition(s) of result element(s)
7492 // \param [inout] nodeNodeMap - nodes to merge
7493 // \param [in] avoidMakingHoles - if true and and the element becomes invalid
7494 // after merging (but not degenerated), removes nodes causing
7495 // the invalidity from \a nodeNodeMap.
7496 // \return bool - true if the element should be removed
7497 //=======================================================================
7499 bool SMESH_MeshEditor::applyMerge( const SMDS_MeshElement* elem,
7500 vector< ElemFeatures >& newElemDefs,
7501 TNodeNodeMap& nodeNodeMap,
7502 const bool avoidMakingHoles )
7504 bool toRemove = false; // to remove elem
7505 int nbResElems = 1; // nb new elements
7507 newElemDefs.resize(nbResElems);
7508 newElemDefs[0].Init( elem );
7509 newElemDefs[0].myNodes.clear();
7511 set<const SMDS_MeshNode*> nodeSet;
7512 vector< const SMDS_MeshNode*> curNodes;
7513 vector< const SMDS_MeshNode*> & uniqueNodes = newElemDefs[0].myNodes;
7516 const int nbNodes = elem->NbNodes();
7517 SMDSAbs_EntityType entity = elem->GetEntityType();
7519 curNodes.resize( nbNodes );
7520 uniqueNodes.resize( nbNodes );
7521 iRepl.resize( nbNodes );
7522 int iUnique = 0, iCur = 0, nbRepl = 0;
7524 // Get new seq of nodes
7526 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
7527 while ( itN->more() )
7529 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( itN->next() );
7531 TNodeNodeMap::iterator nnIt = nodeNodeMap.find( n );
7532 if ( nnIt != nodeNodeMap.end() ) {
7535 curNodes[ iCur ] = n;
7536 bool isUnique = nodeSet.insert( n ).second;
7538 uniqueNodes[ iUnique++ ] = n;
7540 iRepl[ nbRepl++ ] = iCur;
7544 // Analyse element topology after replacement
7546 int nbUniqueNodes = nodeSet.size();
7547 if ( nbNodes != nbUniqueNodes ) // some nodes stick
7552 if ( newElemDefs[0].myIsQuad && newElemDefs[0].myType == SMDSAbs_Face && nbNodes > 6 )
7554 // if corner nodes stick, remove medium nodes between them from uniqueNodes
7555 int nbCorners = nbNodes / 2;
7556 for ( int iCur = 0; iCur < nbCorners; ++iCur )
7558 int iNext = ( iCur + 1 ) % nbCorners;
7559 if ( curNodes[ iCur ] == curNodes[ iNext ] ) // corners stick
7561 int iMedium = iCur + nbCorners;
7562 vector< const SMDS_MeshNode* >::iterator i =
7563 std::find( uniqueNodes.begin() + nbCorners - nbRepl,
7565 curNodes[ iMedium ]);
7566 if ( i != uniqueNodes.end() )
7569 for ( ; i+1 != uniqueNodes.end(); ++i )
7578 case SMDSEntity_Polygon:
7579 case SMDSEntity_Quad_Polygon: // Polygon
7581 ElemFeatures* elemType = & newElemDefs[0];
7582 const bool isQuad = elemType->myIsQuad;
7584 SMDS_MeshCell::applyInterlace // interlace medium and corner nodes
7585 ( SMDS_MeshCell::interlacedSmdsOrder( SMDSEntity_Quad_Polygon, nbNodes ), curNodes );
7587 // a polygon can divide into several elements
7588 vector<const SMDS_MeshNode *> polygons_nodes;
7589 vector<int> quantities;
7590 nbResElems = SimplifyFace( curNodes, polygons_nodes, quantities );
7591 newElemDefs.resize( nbResElems );
7592 for ( int inode = 0, iface = 0; iface < nbResElems; iface++ )
7594 ElemFeatures* elemType = & newElemDefs[iface];
7595 if ( iface ) elemType->Init( elem );
7597 vector<const SMDS_MeshNode *>& face_nodes = elemType->myNodes;
7598 int nbNewNodes = quantities[iface];
7599 face_nodes.assign( polygons_nodes.begin() + inode,
7600 polygons_nodes.begin() + inode + nbNewNodes );
7601 inode += nbNewNodes;
7602 if ( isQuad ) // check if a result elem is a valid quadratic polygon
7604 bool isValid = ( nbNewNodes % 2 == 0 );
7605 for ( int i = 0; i < nbNewNodes && isValid; ++i )
7606 isValid = ( elem->IsMediumNode( face_nodes[i]) == bool( i % 2 ));
7607 elemType->SetQuad( isValid );
7608 if ( isValid ) // put medium nodes after corners
7609 SMDS_MeshCell::applyInterlaceRev
7610 ( SMDS_MeshCell::interlacedSmdsOrder( SMDSEntity_Quad_Polygon,
7611 nbNewNodes ), face_nodes );
7613 elemType->SetPoly(( nbNewNodes / ( elemType->myIsQuad + 1 ) > 4 ));
7615 nbUniqueNodes = newElemDefs[0].myNodes.size();
7619 case SMDSEntity_Polyhedra: // Polyhedral volume
7621 if ( nbUniqueNodes >= 4 )
7623 // each face has to be analyzed in order to check volume validity
7624 if ( const SMDS_VtkVolume* aPolyedre = dynamic_cast<const SMDS_VtkVolume*>( elem ))
7626 int nbFaces = aPolyedre->NbFaces();
7628 vector<const SMDS_MeshNode *>& poly_nodes = newElemDefs[0].myNodes;
7629 vector<int> & quantities = newElemDefs[0].myPolyhedQuantities;
7630 vector<const SMDS_MeshNode *> faceNodes;
7634 for (int iface = 1; iface <= nbFaces; iface++)
7636 int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
7637 faceNodes.resize( nbFaceNodes );
7638 for (int inode = 1; inode <= nbFaceNodes; inode++)
7640 const SMDS_MeshNode * faceNode = aPolyedre->GetFaceNode(iface, inode);
7641 TNodeNodeMap::iterator nnIt = nodeNodeMap.find(faceNode);
7642 if ( nnIt != nodeNodeMap.end() ) // faceNode sticks
7643 faceNode = (*nnIt).second;
7644 faceNodes[inode - 1] = faceNode;
7646 SimplifyFace(faceNodes, poly_nodes, quantities);
7649 if ( quantities.size() > 3 )
7651 // TODO: remove coincident faces
7653 nbUniqueNodes = newElemDefs[0].myNodes.size();
7661 // TODO not all the possible cases are solved. Find something more generic?
7662 case SMDSEntity_Edge: //////// EDGE
7663 case SMDSEntity_Triangle: //// TRIANGLE
7664 case SMDSEntity_Quad_Triangle:
7665 case SMDSEntity_Tetra:
7666 case SMDSEntity_Quad_Tetra: // TETRAHEDRON
7670 case SMDSEntity_Quad_Edge:
7674 case SMDSEntity_Quadrangle: //////////////////////////////////// QUADRANGLE
7676 if ( nbUniqueNodes < 3 )
7678 else if ( nbRepl == 1 && curNodes[ iRepl[0]] == curNodes[( iRepl[0]+2 )%4 ])
7679 toRemove = true; // opposite nodes stick
7684 case SMDSEntity_Quad_Quadrangle: // Quadratic QUADRANGLE
7693 if ( nbUniqueNodes == 6 &&
7695 ( nbRepl == 1 || iRepl[1] >= 4 ))
7701 case SMDSEntity_BiQuad_Quadrangle: // Bi-Quadratic QUADRANGLE
7710 if ( nbUniqueNodes == 7 &&
7712 ( nbRepl == 1 || iRepl[1] != 8 ))
7718 case SMDSEntity_Penta: ///////////////////////////////////// PENTAHEDRON
7720 if ( nbUniqueNodes == 4 ) {
7721 // ---------------------------------> tetrahedron
7722 if ( curNodes[3] == curNodes[4] &&
7723 curNodes[3] == curNodes[5] ) {
7727 else if ( curNodes[0] == curNodes[1] &&
7728 curNodes[0] == curNodes[2] ) {
7729 // bottom nodes stick: set a top before
7730 uniqueNodes[ 3 ] = uniqueNodes [ 0 ];
7731 uniqueNodes[ 0 ] = curNodes [ 5 ];
7732 uniqueNodes[ 1 ] = curNodes [ 4 ];
7733 uniqueNodes[ 2 ] = curNodes [ 3 ];
7736 else if (( curNodes[0] == curNodes[3] ) +
7737 ( curNodes[1] == curNodes[4] ) +
7738 ( curNodes[2] == curNodes[5] ) == 2 ) {
7739 // a lateral face turns into a line
7743 else if ( nbUniqueNodes == 5 ) {
7744 // PENTAHEDRON --------------------> pyramid
7745 if ( curNodes[0] == curNodes[3] )
7747 uniqueNodes[ 0 ] = curNodes[ 1 ];
7748 uniqueNodes[ 1 ] = curNodes[ 4 ];
7749 uniqueNodes[ 2 ] = curNodes[ 5 ];
7750 uniqueNodes[ 3 ] = curNodes[ 2 ];
7751 uniqueNodes[ 4 ] = curNodes[ 0 ];
7754 if ( curNodes[1] == curNodes[4] )
7756 uniqueNodes[ 0 ] = curNodes[ 0 ];
7757 uniqueNodes[ 1 ] = curNodes[ 2 ];
7758 uniqueNodes[ 2 ] = curNodes[ 5 ];
7759 uniqueNodes[ 3 ] = curNodes[ 3 ];
7760 uniqueNodes[ 4 ] = curNodes[ 1 ];
7763 if ( curNodes[2] == curNodes[5] )
7765 uniqueNodes[ 0 ] = curNodes[ 0 ];
7766 uniqueNodes[ 1 ] = curNodes[ 3 ];
7767 uniqueNodes[ 2 ] = curNodes[ 4 ];
7768 uniqueNodes[ 3 ] = curNodes[ 1 ];
7769 uniqueNodes[ 4 ] = curNodes[ 2 ];
7775 case SMDSEntity_Hexa:
7777 //////////////////////////////////// HEXAHEDRON
7778 SMDS_VolumeTool hexa (elem);
7779 hexa.SetExternalNormal();
7780 if ( nbUniqueNodes == 4 && nbRepl == 4 ) {
7781 //////////////////////// HEX ---> tetrahedron
7782 for ( int iFace = 0; iFace < 6; iFace++ ) {
7783 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7784 if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7785 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7786 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7787 // one face turns into a point ...
7788 int pickInd = ind[ 0 ];
7789 int iOppFace = hexa.GetOppFaceIndex( iFace );
7790 ind = hexa.GetFaceNodesIndices( iOppFace );
7792 uniqueNodes.clear();
7793 for ( iCur = 0; iCur < 4 && nbStick < 2; iCur++ ) {
7794 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7797 uniqueNodes.push_back( curNodes[ind[ iCur ]]);
7799 if ( nbStick == 1 ) {
7800 // ... and the opposite one - into a triangle.
7802 uniqueNodes.push_back( curNodes[ pickInd ]);
7809 else if ( nbUniqueNodes == 6 && nbRepl == 2 ) {
7810 //////////////////////// HEX ---> prism
7811 int nbTria = 0, iTria[3];
7812 const int *ind; // indices of face nodes
7813 // look for triangular faces
7814 for ( int iFace = 0; iFace < 6 && nbTria < 3; iFace++ ) {
7815 ind = hexa.GetFaceNodesIndices( iFace );
7816 TIDSortedNodeSet faceNodes;
7817 for ( iCur = 0; iCur < 4; iCur++ )
7818 faceNodes.insert( curNodes[ind[iCur]] );
7819 if ( faceNodes.size() == 3 )
7820 iTria[ nbTria++ ] = iFace;
7822 // check if triangles are opposite
7823 if ( nbTria == 2 && iTria[0] == hexa.GetOppFaceIndex( iTria[1] ))
7825 // set nodes of the bottom triangle
7826 ind = hexa.GetFaceNodesIndices( iTria[ 0 ]);
7828 for ( iCur = 0; iCur < 4; iCur++ )
7829 if ( ind[iCur] != iRepl[0] && ind[iCur] != iRepl[1])
7830 indB.push_back( ind[iCur] );
7831 if ( !hexa.IsForward() )
7832 std::swap( indB[0], indB[2] );
7833 for ( iCur = 0; iCur < 3; iCur++ )
7834 uniqueNodes[ iCur ] = curNodes[indB[iCur]];
7835 // set nodes of the top triangle
7836 const int *indT = hexa.GetFaceNodesIndices( iTria[ 1 ]);
7837 for ( iCur = 0; iCur < 3; ++iCur )
7838 for ( int j = 0; j < 4; ++j )
7839 if ( hexa.IsLinked( indB[ iCur ], indT[ j ] ))
7841 uniqueNodes[ iCur + 3 ] = curNodes[ indT[ j ]];
7848 else if (nbUniqueNodes == 5 && nbRepl == 3 ) {
7849 //////////////////// HEXAHEDRON ---> pyramid
7850 for ( int iFace = 0; iFace < 6; iFace++ ) {
7851 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7852 if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7853 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7854 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7855 // one face turns into a point ...
7856 int iOppFace = hexa.GetOppFaceIndex( iFace );
7857 ind = hexa.GetFaceNodesIndices( iOppFace );
7858 uniqueNodes.clear();
7859 for ( iCur = 0; iCur < 4; iCur++ ) {
7860 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7863 uniqueNodes.push_back( curNodes[ind[ iCur ]]);
7865 if ( uniqueNodes.size() == 4 ) {
7866 // ... and the opposite one is a quadrangle
7868 const int* indTop = hexa.GetFaceNodesIndices( iFace );
7869 uniqueNodes.push_back( curNodes[indTop[ 0 ]]);
7877 if ( toRemove && nbUniqueNodes > 4 ) {
7878 ////////////////// HEXAHEDRON ---> polyhedron
7879 hexa.SetExternalNormal();
7880 vector<const SMDS_MeshNode *>& poly_nodes = newElemDefs[0].myNodes;
7881 vector<int> & quantities = newElemDefs[0].myPolyhedQuantities;
7882 poly_nodes.reserve( 6 * 4 ); poly_nodes.clear();
7883 quantities.reserve( 6 ); quantities.clear();
7884 for ( int iFace = 0; iFace < 6; iFace++ )
7886 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7887 if ( curNodes[ind[0]] == curNodes[ind[2]] ||
7888 curNodes[ind[1]] == curNodes[ind[3]] )
7891 break; // opposite nodes stick
7894 for ( iCur = 0; iCur < 4; iCur++ )
7896 if ( nodeSet.insert( curNodes[ind[ iCur ]] ).second )
7897 poly_nodes.push_back( curNodes[ind[ iCur ]]);
7899 if ( nodeSet.size() < 3 )
7900 poly_nodes.resize( poly_nodes.size() - nodeSet.size() );
7902 quantities.push_back( nodeSet.size() );
7904 if ( quantities.size() >= 4 )
7907 nbUniqueNodes = poly_nodes.size();
7908 newElemDefs[0].SetPoly(true);
7912 } // case HEXAHEDRON
7917 } // switch ( entity )
7919 if ( toRemove && nbResElems == 0 && avoidMakingHoles )
7921 // erase from nodeNodeMap nodes whose merge spoils elem
7922 vector< const SMDS_MeshNode* > noMergeNodes;
7923 SMESH_MeshAlgos::DeMerge( elem, curNodes, noMergeNodes );
7924 for ( size_t i = 0; i < noMergeNodes.size(); ++i )
7925 nodeNodeMap.erase( noMergeNodes[i] );
7928 } // if ( nbNodes != nbUniqueNodes ) // some nodes stick
7930 uniqueNodes.resize( nbUniqueNodes );
7932 if ( !toRemove && nbResElems == 0 )
7935 newElemDefs.resize( nbResElems );
7941 // ========================================================
7942 // class : SortableElement
7943 // purpose : allow sorting elements basing on their nodes
7944 // ========================================================
7945 class SortableElement : public set <const SMDS_MeshElement*>
7949 SortableElement( const SMDS_MeshElement* theElem )
7952 SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator();
7953 while ( nodeIt->more() )
7954 this->insert( nodeIt->next() );
7957 const SMDS_MeshElement* Get() const
7961 mutable const SMDS_MeshElement* myElem;
7964 //=======================================================================
7965 //function : FindEqualElements
7966 //purpose : Return list of group of elements built on the same nodes.
7967 // Search among theElements or in the whole mesh if theElements is empty
7968 //=======================================================================
7970 void SMESH_MeshEditor::FindEqualElements(TIDSortedElemSet & theElements,
7971 TListOfListOfElementsID & theGroupsOfElementsID)
7973 myLastCreatedElems.Clear();
7974 myLastCreatedNodes.Clear();
7976 typedef map< SortableElement, int > TMapOfNodeSet;
7977 typedef list<int> TGroupOfElems;
7979 if ( theElements.empty() )
7980 { // get all elements in the mesh
7981 SMDS_ElemIteratorPtr eIt = GetMeshDS()->elementsIterator();
7982 while ( eIt->more() )
7983 theElements.insert( theElements.end(), eIt->next() );
7986 vector< TGroupOfElems > arrayOfGroups;
7987 TGroupOfElems groupOfElems;
7988 TMapOfNodeSet mapOfNodeSet;
7990 TIDSortedElemSet::iterator elemIt = theElements.begin();
7991 for ( int i = 0; elemIt != theElements.end(); ++elemIt )
7993 const SMDS_MeshElement* curElem = *elemIt;
7994 SortableElement SE(curElem);
7996 pair< TMapOfNodeSet::iterator, bool> pp = mapOfNodeSet.insert(make_pair(SE, i));
7997 if ( !pp.second ) { // one more coincident elem
7998 TMapOfNodeSet::iterator& itSE = pp.first;
7999 int ind = (*itSE).second;
8000 arrayOfGroups[ind].push_back( curElem->GetID() );
8003 arrayOfGroups.push_back( groupOfElems );
8004 arrayOfGroups.back().push_back( curElem->GetID() );
8009 groupOfElems.clear();
8010 vector< TGroupOfElems >::iterator groupIt = arrayOfGroups.begin();
8011 for ( ; groupIt != arrayOfGroups.end(); ++groupIt )
8013 if ( groupIt->size() > 1 ) {
8014 //groupOfElems.sort(); -- theElements is sorted already
8015 theGroupsOfElementsID.push_back( groupOfElems );
8016 theGroupsOfElementsID.back().splice( theGroupsOfElementsID.back().end(), *groupIt );
8021 //=======================================================================
8022 //function : MergeElements
8023 //purpose : In each given group, substitute all elements by the first one.
8024 //=======================================================================
8026 void SMESH_MeshEditor::MergeElements(TListOfListOfElementsID & theGroupsOfElementsID)
8028 myLastCreatedElems.Clear();
8029 myLastCreatedNodes.Clear();
8031 typedef list<int> TListOfIDs;
8032 TListOfIDs rmElemIds; // IDs of elems to remove
8034 SMESHDS_Mesh* aMesh = GetMeshDS();
8036 TListOfListOfElementsID::iterator groupsIt = theGroupsOfElementsID.begin();
8037 while ( groupsIt != theGroupsOfElementsID.end() ) {
8038 TListOfIDs& aGroupOfElemID = *groupsIt;
8039 aGroupOfElemID.sort();
8040 int elemIDToKeep = aGroupOfElemID.front();
8041 const SMDS_MeshElement* elemToKeep = aMesh->FindElement(elemIDToKeep);
8042 aGroupOfElemID.pop_front();
8043 TListOfIDs::iterator idIt = aGroupOfElemID.begin();
8044 while ( idIt != aGroupOfElemID.end() ) {
8045 int elemIDToRemove = *idIt;
8046 const SMDS_MeshElement* elemToRemove = aMesh->FindElement(elemIDToRemove);
8047 // add the kept element in groups of removed one (PAL15188)
8048 AddToSameGroups( elemToKeep, elemToRemove, aMesh );
8049 rmElemIds.push_back( elemIDToRemove );
8055 Remove( rmElemIds, false );
8058 //=======================================================================
8059 //function : MergeEqualElements
8060 //purpose : Remove all but one of elements built on the same nodes.
8061 //=======================================================================
8063 void SMESH_MeshEditor::MergeEqualElements()
8065 TIDSortedElemSet aMeshElements; /* empty input ==
8066 to merge equal elements in the whole mesh */
8067 TListOfListOfElementsID aGroupsOfElementsID;
8068 FindEqualElements(aMeshElements, aGroupsOfElementsID);
8069 MergeElements(aGroupsOfElementsID);
8072 //=======================================================================
8073 //function : findAdjacentFace
8075 //=======================================================================
8077 static const SMDS_MeshElement* findAdjacentFace(const SMDS_MeshNode* n1,
8078 const SMDS_MeshNode* n2,
8079 const SMDS_MeshElement* elem)
8081 TIDSortedElemSet elemSet, avoidSet;
8083 avoidSet.insert ( elem );
8084 return SMESH_MeshAlgos::FindFaceInSet( n1, n2, elemSet, avoidSet );
8087 //=======================================================================
8088 //function : findSegment
8089 //purpose : Return a mesh segment by two nodes one of which can be medium
8090 //=======================================================================
8092 static const SMDS_MeshElement* findSegment(const SMDS_MeshNode* n1,
8093 const SMDS_MeshNode* n2)
8095 SMDS_ElemIteratorPtr it = n1->GetInverseElementIterator( SMDSAbs_Edge );
8096 while ( it->more() )
8098 const SMDS_MeshElement* seg = it->next();
8099 if ( seg->GetNodeIndex( n2 ) >= 0 )
8105 //=======================================================================
8106 //function : FindFreeBorder
8108 //=======================================================================
8110 #define ControlFreeBorder SMESH::Controls::FreeEdges::IsFreeEdge
8112 bool SMESH_MeshEditor::FindFreeBorder (const SMDS_MeshNode* theFirstNode,
8113 const SMDS_MeshNode* theSecondNode,
8114 const SMDS_MeshNode* theLastNode,
8115 list< const SMDS_MeshNode* > & theNodes,
8116 list< const SMDS_MeshElement* >& theFaces)
8118 if ( !theFirstNode || !theSecondNode )
8120 // find border face between theFirstNode and theSecondNode
8121 const SMDS_MeshElement* curElem = findAdjacentFace( theFirstNode, theSecondNode, 0 );
8125 theFaces.push_back( curElem );
8126 theNodes.push_back( theFirstNode );
8127 theNodes.push_back( theSecondNode );
8129 const SMDS_MeshNode *nIgnore = theFirstNode, *nStart = theSecondNode;
8130 TIDSortedElemSet foundElems;
8131 bool needTheLast = ( theLastNode != 0 );
8133 while ( nStart != theLastNode ) {
8134 if ( nStart == theFirstNode )
8135 return !needTheLast;
8137 // find all free border faces sharing form nStart
8139 list< const SMDS_MeshElement* > curElemList;
8140 list< const SMDS_MeshNode* > nStartList;
8141 SMDS_ElemIteratorPtr invElemIt = nStart->GetInverseElementIterator(SMDSAbs_Face);
8142 while ( invElemIt->more() ) {
8143 const SMDS_MeshElement* e = invElemIt->next();
8144 if ( e == curElem || foundElems.insert( e ).second ) {
8146 int iNode = 0, nbNodes = e->NbNodes();
8147 vector<const SMDS_MeshNode*> nodes(nbNodes+1);
8149 if ( e->IsQuadratic() ) {
8150 const SMDS_VtkFace* F =
8151 dynamic_cast<const SMDS_VtkFace*>(e);
8152 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8153 // use special nodes iterator
8154 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8155 while( anIter->more() ) {
8156 nodes[ iNode++ ] = cast2Node(anIter->next());
8160 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
8161 while ( nIt->more() )
8162 nodes[ iNode++ ] = static_cast<const SMDS_MeshNode*>( nIt->next() );
8164 nodes[ iNode ] = nodes[ 0 ];
8166 for ( iNode = 0; iNode < nbNodes; iNode++ )
8167 if (((nodes[ iNode ] == nStart && nodes[ iNode + 1] != nIgnore ) ||
8168 (nodes[ iNode + 1] == nStart && nodes[ iNode ] != nIgnore )) &&
8169 ControlFreeBorder( &nodes[ iNode ], e->GetID() ))
8171 nStartList.push_back( nodes[ iNode + ( nodes[ iNode ] == nStart ? 1 : 0 )]);
8172 curElemList.push_back( e );
8176 // analyse the found
8178 int nbNewBorders = curElemList.size();
8179 if ( nbNewBorders == 0 ) {
8180 // no free border furthermore
8181 return !needTheLast;
8183 else if ( nbNewBorders == 1 ) {
8184 // one more element found
8186 nStart = nStartList.front();
8187 curElem = curElemList.front();
8188 theFaces.push_back( curElem );
8189 theNodes.push_back( nStart );
8192 // several continuations found
8193 list< const SMDS_MeshElement* >::iterator curElemIt;
8194 list< const SMDS_MeshNode* >::iterator nStartIt;
8195 // check if one of them reached the last node
8196 if ( needTheLast ) {
8197 for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
8198 curElemIt!= curElemList.end();
8199 curElemIt++, nStartIt++ )
8200 if ( *nStartIt == theLastNode ) {
8201 theFaces.push_back( *curElemIt );
8202 theNodes.push_back( *nStartIt );
8206 // find the best free border by the continuations
8207 list<const SMDS_MeshNode*> contNodes[ 2 ], *cNL;
8208 list<const SMDS_MeshElement*> contFaces[ 2 ], *cFL;
8209 for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
8210 curElemIt!= curElemList.end();
8211 curElemIt++, nStartIt++ )
8213 cNL = & contNodes[ contNodes[0].empty() ? 0 : 1 ];
8214 cFL = & contFaces[ contFaces[0].empty() ? 0 : 1 ];
8215 // find one more free border
8216 if ( ! SMESH_MeshEditor::FindFreeBorder( nStart, *nStartIt, theLastNode, *cNL, *cFL )) {
8220 else if ( !contNodes[0].empty() && !contNodes[1].empty() ) {
8221 // choice: clear a worse one
8222 int iLongest = ( contNodes[0].size() < contNodes[1].size() ? 1 : 0 );
8223 int iWorse = ( needTheLast ? 1 - iLongest : iLongest );
8224 contNodes[ iWorse ].clear();
8225 contFaces[ iWorse ].clear();
8228 if ( contNodes[0].empty() && contNodes[1].empty() )
8231 // append the best free border
8232 cNL = & contNodes[ contNodes[0].empty() ? 1 : 0 ];
8233 cFL = & contFaces[ contFaces[0].empty() ? 1 : 0 ];
8234 theNodes.pop_back(); // remove nIgnore
8235 theNodes.pop_back(); // remove nStart
8236 theFaces.pop_back(); // remove curElem
8237 list< const SMDS_MeshNode* >::iterator nIt = cNL->begin();
8238 list< const SMDS_MeshElement* >::iterator fIt = cFL->begin();
8239 for ( ; nIt != cNL->end(); nIt++ ) theNodes.push_back( *nIt );
8240 for ( ; fIt != cFL->end(); fIt++ ) theFaces.push_back( *fIt );
8243 } // several continuations found
8244 } // while ( nStart != theLastNode )
8249 //=======================================================================
8250 //function : CheckFreeBorderNodes
8251 //purpose : Return true if the tree nodes are on a free border
8252 //=======================================================================
8254 bool SMESH_MeshEditor::CheckFreeBorderNodes(const SMDS_MeshNode* theNode1,
8255 const SMDS_MeshNode* theNode2,
8256 const SMDS_MeshNode* theNode3)
8258 list< const SMDS_MeshNode* > nodes;
8259 list< const SMDS_MeshElement* > faces;
8260 return FindFreeBorder( theNode1, theNode2, theNode3, nodes, faces);
8263 //=======================================================================
8264 //function : SewFreeBorder
8266 //warning : for border-to-side sewing theSideSecondNode is considered as
8267 // the last side node and theSideThirdNode is not used
8268 //=======================================================================
8270 SMESH_MeshEditor::Sew_Error
8271 SMESH_MeshEditor::SewFreeBorder (const SMDS_MeshNode* theBordFirstNode,
8272 const SMDS_MeshNode* theBordSecondNode,
8273 const SMDS_MeshNode* theBordLastNode,
8274 const SMDS_MeshNode* theSideFirstNode,
8275 const SMDS_MeshNode* theSideSecondNode,
8276 const SMDS_MeshNode* theSideThirdNode,
8277 const bool theSideIsFreeBorder,
8278 const bool toCreatePolygons,
8279 const bool toCreatePolyedrs)
8281 myLastCreatedElems.Clear();
8282 myLastCreatedNodes.Clear();
8284 Sew_Error aResult = SEW_OK;
8286 // ====================================
8287 // find side nodes and elements
8288 // ====================================
8290 list< const SMDS_MeshNode* > nSide[ 2 ];
8291 list< const SMDS_MeshElement* > eSide[ 2 ];
8292 list< const SMDS_MeshNode* >::iterator nIt[ 2 ];
8293 list< const SMDS_MeshElement* >::iterator eIt[ 2 ];
8297 if (!FindFreeBorder(theBordFirstNode,theBordSecondNode,theBordLastNode,
8298 nSide[0], eSide[0])) {
8299 MESSAGE(" Free Border 1 not found " );
8300 aResult = SEW_BORDER1_NOT_FOUND;
8302 if (theSideIsFreeBorder) {
8305 if (!FindFreeBorder(theSideFirstNode, theSideSecondNode, theSideThirdNode,
8306 nSide[1], eSide[1])) {
8307 MESSAGE(" Free Border 2 not found " );
8308 aResult = ( aResult != SEW_OK ? SEW_BOTH_BORDERS_NOT_FOUND : SEW_BORDER2_NOT_FOUND );
8311 if ( aResult != SEW_OK )
8314 if (!theSideIsFreeBorder) {
8318 // -------------------------------------------------------------------------
8320 // 1. If nodes to merge are not coincident, move nodes of the free border
8321 // from the coord sys defined by the direction from the first to last
8322 // nodes of the border to the correspondent sys of the side 2
8323 // 2. On the side 2, find the links most co-directed with the correspondent
8324 // links of the free border
8325 // -------------------------------------------------------------------------
8327 // 1. Since sewing may break if there are volumes to split on the side 2,
8328 // we won't move nodes but just compute new coordinates for them
8329 typedef map<const SMDS_MeshNode*, gp_XYZ> TNodeXYZMap;
8330 TNodeXYZMap nBordXYZ;
8331 list< const SMDS_MeshNode* >& bordNodes = nSide[ 0 ];
8332 list< const SMDS_MeshNode* >::iterator nBordIt;
8334 gp_XYZ Pb1( theBordFirstNode->X(), theBordFirstNode->Y(), theBordFirstNode->Z() );
8335 gp_XYZ Pb2( theBordLastNode->X(), theBordLastNode->Y(), theBordLastNode->Z() );
8336 gp_XYZ Ps1( theSideFirstNode->X(), theSideFirstNode->Y(), theSideFirstNode->Z() );
8337 gp_XYZ Ps2( theSideSecondNode->X(), theSideSecondNode->Y(), theSideSecondNode->Z() );
8338 double tol2 = 1.e-8;
8339 gp_Vec Vbs1( Pb1 - Ps1 ),Vbs2( Pb2 - Ps2 );
8340 if ( Vbs1.SquareMagnitude() > tol2 || Vbs2.SquareMagnitude() > tol2 ) {
8341 // Need node movement.
8343 // find X and Z axes to create trsf
8344 gp_Vec Zb( Pb1 - Pb2 ), Zs( Ps1 - Ps2 );
8346 if ( X.SquareMagnitude() <= gp::Resolution() * gp::Resolution() )
8348 X = gp_Ax2( gp::Origin(), Zb ).XDirection();
8351 gp_Ax3 toBordAx( Pb1, Zb, X );
8352 gp_Ax3 fromSideAx( Ps1, Zs, X );
8353 gp_Ax3 toGlobalAx( gp::Origin(), gp::DZ(), gp::DX() );
8355 gp_Trsf toBordSys, fromSide2Sys;
8356 toBordSys.SetTransformation( toBordAx );
8357 fromSide2Sys.SetTransformation( fromSideAx, toGlobalAx );
8358 fromSide2Sys.SetScaleFactor( Zs.Magnitude() / Zb.Magnitude() );
8361 for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
8362 const SMDS_MeshNode* n = *nBordIt;
8363 gp_XYZ xyz( n->X(),n->Y(),n->Z() );
8364 toBordSys.Transforms( xyz );
8365 fromSide2Sys.Transforms( xyz );
8366 nBordXYZ.insert( TNodeXYZMap::value_type( n, xyz ));
8370 // just insert nodes XYZ in the nBordXYZ map
8371 for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
8372 const SMDS_MeshNode* n = *nBordIt;
8373 nBordXYZ.insert( TNodeXYZMap::value_type( n, gp_XYZ( n->X(),n->Y(),n->Z() )));
8377 // 2. On the side 2, find the links most co-directed with the correspondent
8378 // links of the free border
8380 list< const SMDS_MeshElement* >& sideElems = eSide[ 1 ];
8381 list< const SMDS_MeshNode* >& sideNodes = nSide[ 1 ];
8382 sideNodes.push_back( theSideFirstNode );
8384 bool hasVolumes = false;
8385 LinkID_Gen aLinkID_Gen( GetMeshDS() );
8386 set<long> foundSideLinkIDs, checkedLinkIDs;
8387 SMDS_VolumeTool volume;
8388 //const SMDS_MeshNode* faceNodes[ 4 ];
8390 const SMDS_MeshNode* sideNode;
8391 const SMDS_MeshElement* sideElem = 0;
8392 const SMDS_MeshNode* prevSideNode = theSideFirstNode;
8393 const SMDS_MeshNode* prevBordNode = theBordFirstNode;
8394 nBordIt = bordNodes.begin();
8396 // border node position and border link direction to compare with
8397 gp_XYZ bordPos = nBordXYZ[ *nBordIt ];
8398 gp_XYZ bordDir = bordPos - nBordXYZ[ prevBordNode ];
8399 // choose next side node by link direction or by closeness to
8400 // the current border node:
8401 bool searchByDir = ( *nBordIt != theBordLastNode );
8403 // find the next node on the Side 2
8405 double maxDot = -DBL_MAX, minDist = DBL_MAX;
8407 checkedLinkIDs.clear();
8408 gp_XYZ prevXYZ( prevSideNode->X(), prevSideNode->Y(), prevSideNode->Z() );
8410 // loop on inverse elements of current node (prevSideNode) on the Side 2
8411 SMDS_ElemIteratorPtr invElemIt = prevSideNode->GetInverseElementIterator();
8412 while ( invElemIt->more() )
8414 const SMDS_MeshElement* elem = invElemIt->next();
8415 // prepare data for a loop on links coming to prevSideNode, of a face or a volume
8416 int iPrevNode = 0, iNode = 0, nbNodes = elem->NbNodes();
8417 vector< const SMDS_MeshNode* > faceNodes( nbNodes, (const SMDS_MeshNode*)0 );
8418 bool isVolume = volume.Set( elem );
8419 const SMDS_MeshNode** nodes = isVolume ? volume.GetNodes() : & faceNodes[0];
8420 if ( isVolume ) // --volume
8422 else if ( elem->GetType()==SMDSAbs_Face ) { // --face
8423 // retrieve all face nodes and find iPrevNode - an index of the prevSideNode
8424 if(elem->IsQuadratic()) {
8425 const SMDS_VtkFace* F =
8426 dynamic_cast<const SMDS_VtkFace*>(elem);
8427 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8428 // use special nodes iterator
8429 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8430 while( anIter->more() ) {
8431 nodes[ iNode ] = cast2Node(anIter->next());
8432 if ( nodes[ iNode++ ] == prevSideNode )
8433 iPrevNode = iNode - 1;
8437 SMDS_ElemIteratorPtr nIt = elem->nodesIterator();
8438 while ( nIt->more() ) {
8439 nodes[ iNode ] = cast2Node( nIt->next() );
8440 if ( nodes[ iNode++ ] == prevSideNode )
8441 iPrevNode = iNode - 1;
8444 // there are 2 links to check
8449 // loop on links, to be precise, on the second node of links
8450 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
8451 const SMDS_MeshNode* n = nodes[ iNode ];
8453 if ( !volume.IsLinked( n, prevSideNode ))
8457 if ( iNode ) // a node before prevSideNode
8458 n = nodes[ iPrevNode == 0 ? elem->NbNodes() - 1 : iPrevNode - 1 ];
8459 else // a node after prevSideNode
8460 n = nodes[ iPrevNode + 1 == elem->NbNodes() ? 0 : iPrevNode + 1 ];
8462 // check if this link was already used
8463 long iLink = aLinkID_Gen.GetLinkID( prevSideNode, n );
8464 bool isJustChecked = !checkedLinkIDs.insert( iLink ).second;
8465 if (!isJustChecked &&
8466 foundSideLinkIDs.find( iLink ) == foundSideLinkIDs.end() )
8468 // test a link geometrically
8469 gp_XYZ nextXYZ ( n->X(), n->Y(), n->Z() );
8470 bool linkIsBetter = false;
8471 double dot = 0.0, dist = 0.0;
8472 if ( searchByDir ) { // choose most co-directed link
8473 dot = bordDir * ( nextXYZ - prevXYZ ).Normalized();
8474 linkIsBetter = ( dot > maxDot );
8476 else { // choose link with the node closest to bordPos
8477 dist = ( nextXYZ - bordPos ).SquareModulus();
8478 linkIsBetter = ( dist < minDist );
8480 if ( linkIsBetter ) {
8489 } // loop on inverse elements of prevSideNode
8492 MESSAGE(" Can't find path by links of the Side 2 ");
8493 return SEW_BAD_SIDE_NODES;
8495 sideNodes.push_back( sideNode );
8496 sideElems.push_back( sideElem );
8497 foundSideLinkIDs.insert ( linkID );
8498 prevSideNode = sideNode;
8500 if ( *nBordIt == theBordLastNode )
8501 searchByDir = false;
8503 // find the next border link to compare with
8504 gp_XYZ sidePos( sideNode->X(), sideNode->Y(), sideNode->Z() );
8505 searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8506 // move to next border node if sideNode is before forward border node (bordPos)
8507 while ( *nBordIt != theBordLastNode && !searchByDir ) {
8508 prevBordNode = *nBordIt;
8510 bordPos = nBordXYZ[ *nBordIt ];
8511 bordDir = bordPos - nBordXYZ[ prevBordNode ];
8512 searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8516 while ( sideNode != theSideSecondNode );
8518 if ( hasVolumes && sideNodes.size () != bordNodes.size() && !toCreatePolyedrs) {
8519 MESSAGE("VOLUME SPLITTING IS FORBIDDEN");
8520 return SEW_VOLUMES_TO_SPLIT; // volume splitting is forbidden
8522 } // end nodes search on the side 2
8524 // ============================
8525 // sew the border to the side 2
8526 // ============================
8528 int nbNodes[] = { (int)nSide[0].size(), (int)nSide[1].size() };
8529 int maxNbNodes = Max( nbNodes[0], nbNodes[1] );
8531 bool toMergeConformal = ( nbNodes[0] == nbNodes[1] );
8532 if ( toMergeConformal && toCreatePolygons )
8534 // do not merge quadrangles if polygons are OK (IPAL0052824)
8535 eIt[0] = eSide[0].begin();
8536 eIt[1] = eSide[1].begin();
8537 bool allQuads[2] = { true, true };
8538 for ( int iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8539 for ( ; allQuads[iBord] && eIt[iBord] != eSide[iBord].end(); ++eIt[iBord] )
8540 allQuads[iBord] = ( (*eIt[iBord])->NbCornerNodes() == 4 );
8542 toMergeConformal = ( !allQuads[0] && !allQuads[1] );
8545 TListOfListOfNodes nodeGroupsToMerge;
8546 if (( toMergeConformal ) ||
8547 ( theSideIsFreeBorder && !theSideThirdNode )) {
8549 // all nodes are to be merged
8551 for (nIt[0] = nSide[0].begin(), nIt[1] = nSide[1].begin();
8552 nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end();
8553 nIt[0]++, nIt[1]++ )
8555 nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8556 nodeGroupsToMerge.back().push_back( *nIt[1] ); // to keep
8557 nodeGroupsToMerge.back().push_back( *nIt[0] ); // to remove
8562 // insert new nodes into the border and the side to get equal nb of segments
8564 // get normalized parameters of nodes on the borders
8565 vector< double > param[ 2 ];
8566 param[0].resize( maxNbNodes );
8567 param[1].resize( maxNbNodes );
8569 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8570 list< const SMDS_MeshNode* >& nodes = nSide[ iBord ];
8571 list< const SMDS_MeshNode* >::iterator nIt = nodes.begin();
8572 const SMDS_MeshNode* nPrev = *nIt;
8573 double bordLength = 0;
8574 for ( iNode = 0; nIt != nodes.end(); nIt++, iNode++ ) { // loop on border nodes
8575 const SMDS_MeshNode* nCur = *nIt;
8576 gp_XYZ segment (nCur->X() - nPrev->X(),
8577 nCur->Y() - nPrev->Y(),
8578 nCur->Z() - nPrev->Z());
8579 double segmentLen = segment.Modulus();
8580 bordLength += segmentLen;
8581 param[ iBord ][ iNode ] = bordLength;
8584 // normalize within [0,1]
8585 for ( iNode = 0; iNode < nbNodes[ iBord ]; iNode++ ) {
8586 param[ iBord ][ iNode ] /= bordLength;
8590 // loop on border segments
8591 const SMDS_MeshNode *nPrev[ 2 ] = { 0, 0 };
8592 int i[ 2 ] = { 0, 0 };
8593 nIt[0] = nSide[0].begin(); eIt[0] = eSide[0].begin();
8594 nIt[1] = nSide[1].begin(); eIt[1] = eSide[1].begin();
8596 TElemOfNodeListMap insertMap;
8597 TElemOfNodeListMap::iterator insertMapIt;
8599 // key: elem to insert nodes into
8600 // value: 2 nodes to insert between + nodes to be inserted
8602 bool next[ 2 ] = { false, false };
8604 // find min adjacent segment length after sewing
8605 double nextParam = 10., prevParam = 0;
8606 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8607 if ( i[ iBord ] + 1 < nbNodes[ iBord ])
8608 nextParam = Min( nextParam, param[iBord][ i[iBord] + 1 ]);
8609 if ( i[ iBord ] > 0 )
8610 prevParam = Max( prevParam, param[iBord][ i[iBord] - 1 ]);
8612 double minParam = Min( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8613 double maxParam = Max( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8614 double minSegLen = Min( nextParam - minParam, maxParam - prevParam );
8616 // choose to insert or to merge nodes
8617 double du = param[ 1 ][ i[1] ] - param[ 0 ][ i[0] ];
8618 if ( Abs( du ) <= minSegLen * 0.2 ) {
8621 nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8622 const SMDS_MeshNode* n0 = *nIt[0];
8623 const SMDS_MeshNode* n1 = *nIt[1];
8624 nodeGroupsToMerge.back().push_back( n1 );
8625 nodeGroupsToMerge.back().push_back( n0 );
8626 // position of node of the border changes due to merge
8627 param[ 0 ][ i[0] ] += du;
8628 // move n1 for the sake of elem shape evaluation during insertion.
8629 // n1 will be removed by MergeNodes() anyway
8630 const_cast<SMDS_MeshNode*>( n0 )->setXYZ( n1->X(), n1->Y(), n1->Z() );
8631 next[0] = next[1] = true;
8636 int intoBord = ( du < 0 ) ? 0 : 1;
8637 const SMDS_MeshElement* elem = *eIt [ intoBord ];
8638 const SMDS_MeshNode* n1 = nPrev[ intoBord ];
8639 const SMDS_MeshNode* n2 = *nIt [ intoBord ];
8640 const SMDS_MeshNode* nIns = *nIt [ 1 - intoBord ];
8641 if ( intoBord == 1 ) {
8642 // move node of the border to be on a link of elem of the side
8643 gp_XYZ p1 (n1->X(), n1->Y(), n1->Z());
8644 gp_XYZ p2 (n2->X(), n2->Y(), n2->Z());
8645 double ratio = du / ( param[ 1 ][ i[1] ] - param[ 1 ][ i[1]-1 ]);
8646 gp_XYZ p = p2 * ( 1 - ratio ) + p1 * ratio;
8647 GetMeshDS()->MoveNode( nIns, p.X(), p.Y(), p.Z() );
8649 insertMapIt = insertMap.find( elem );
8650 bool notFound = ( insertMapIt == insertMap.end() );
8651 bool otherLink = ( !notFound && (*insertMapIt).second.front() != n1 );
8653 // insert into another link of the same element:
8654 // 1. perform insertion into the other link of the elem
8655 list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8656 const SMDS_MeshNode* n12 = nodeList.front(); nodeList.pop_front();
8657 const SMDS_MeshNode* n22 = nodeList.front(); nodeList.pop_front();
8658 InsertNodesIntoLink( elem, n12, n22, nodeList, toCreatePolygons );
8659 // 2. perform insertion into the link of adjacent faces
8660 while ( const SMDS_MeshElement* adjElem = findAdjacentFace( n12, n22, elem )) {
8661 InsertNodesIntoLink( adjElem, n12, n22, nodeList, toCreatePolygons );
8663 while ( const SMDS_MeshElement* seg = findSegment( n12, n22 )) {
8664 InsertNodesIntoLink( seg, n12, n22, nodeList );
8666 if (toCreatePolyedrs) {
8667 // perform insertion into the links of adjacent volumes
8668 UpdateVolumes(n12, n22, nodeList);
8670 // 3. find an element appeared on n1 and n2 after the insertion
8671 insertMap.erase( elem );
8672 elem = findAdjacentFace( n1, n2, 0 );
8674 if ( notFound || otherLink ) {
8675 // add element and nodes of the side into the insertMap
8676 insertMapIt = insertMap.insert( make_pair( elem, list<const SMDS_MeshNode*>() )).first;
8677 (*insertMapIt).second.push_back( n1 );
8678 (*insertMapIt).second.push_back( n2 );
8680 // add node to be inserted into elem
8681 (*insertMapIt).second.push_back( nIns );
8682 next[ 1 - intoBord ] = true;
8685 // go to the next segment
8686 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8687 if ( next[ iBord ] ) {
8688 if ( i[ iBord ] != 0 && eIt[ iBord ] != eSide[ iBord ].end())
8690 nPrev[ iBord ] = *nIt[ iBord ];
8691 nIt[ iBord ]++; i[ iBord ]++;
8695 while ( nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end());
8697 // perform insertion of nodes into elements
8699 for (insertMapIt = insertMap.begin();
8700 insertMapIt != insertMap.end();
8703 const SMDS_MeshElement* elem = (*insertMapIt).first;
8704 list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8705 const SMDS_MeshNode* n1 = nodeList.front(); nodeList.pop_front();
8706 const SMDS_MeshNode* n2 = nodeList.front(); nodeList.pop_front();
8708 InsertNodesIntoLink( elem, n1, n2, nodeList, toCreatePolygons );
8710 while ( const SMDS_MeshElement* seg = findSegment( n1, n2 )) {
8711 InsertNodesIntoLink( seg, n1, n2, nodeList );
8714 if ( !theSideIsFreeBorder ) {
8715 // look for and insert nodes into the faces adjacent to elem
8716 while ( const SMDS_MeshElement* adjElem = findAdjacentFace( n1, n2, elem )) {
8717 InsertNodesIntoLink( adjElem, n1, n2, nodeList, toCreatePolygons );
8720 if (toCreatePolyedrs) {
8721 // perform insertion into the links of adjacent volumes
8722 UpdateVolumes(n1, n2, nodeList);
8725 } // end: insert new nodes
8727 MergeNodes ( nodeGroupsToMerge );
8730 // Remove coincident segments
8733 TIDSortedElemSet segments;
8734 SMESH_SequenceOfElemPtr newFaces;
8735 for ( int i = 1; i <= myLastCreatedElems.Length(); ++i )
8737 if ( !myLastCreatedElems(i) ) continue;
8738 if ( myLastCreatedElems(i)->GetType() == SMDSAbs_Edge )
8739 segments.insert( segments.end(), myLastCreatedElems(i) );
8741 newFaces.Append( myLastCreatedElems(i) );
8743 // get segments adjacent to merged nodes
8744 TListOfListOfNodes::iterator groupIt = nodeGroupsToMerge.begin();
8745 for ( ; groupIt != nodeGroupsToMerge.end(); groupIt++ )
8747 const list<const SMDS_MeshNode*>& nodes = *groupIt;
8748 SMDS_ElemIteratorPtr segIt = nodes.front()->GetInverseElementIterator( SMDSAbs_Edge );
8749 while ( segIt->more() )
8750 segments.insert( segIt->next() );
8754 TListOfListOfElementsID equalGroups;
8755 if ( !segments.empty() )
8756 FindEqualElements( segments, equalGroups );
8757 if ( !equalGroups.empty() )
8759 // remove from segments those that will be removed
8760 TListOfListOfElementsID::iterator itGroups = equalGroups.begin();
8761 for ( ; itGroups != equalGroups.end(); ++itGroups )
8763 list< int >& group = *itGroups;
8764 list< int >::iterator id = group.begin();
8765 for ( ++id; id != group.end(); ++id )
8766 if ( const SMDS_MeshElement* seg = GetMeshDS()->FindElement( *id ))
8767 segments.erase( seg );
8769 // remove equal segments
8770 MergeElements( equalGroups );
8772 // restore myLastCreatedElems
8773 myLastCreatedElems = newFaces;
8774 TIDSortedElemSet::iterator seg = segments.begin();
8775 for ( ; seg != segments.end(); ++seg )
8776 myLastCreatedElems.Append( *seg );
8782 //=======================================================================
8783 //function : InsertNodesIntoLink
8784 //purpose : insert theNodesToInsert into theElement between theBetweenNode1
8785 // and theBetweenNode2 and split theElement
8786 //=======================================================================
8788 void SMESH_MeshEditor::InsertNodesIntoLink(const SMDS_MeshElement* theElement,
8789 const SMDS_MeshNode* theBetweenNode1,
8790 const SMDS_MeshNode* theBetweenNode2,
8791 list<const SMDS_MeshNode*>& theNodesToInsert,
8792 const bool toCreatePoly)
8794 if ( !theElement ) return;
8796 SMESHDS_Mesh *aMesh = GetMeshDS();
8797 vector<const SMDS_MeshElement*> newElems;
8799 if ( theElement->GetType() == SMDSAbs_Edge )
8801 theNodesToInsert.push_front( theBetweenNode1 );
8802 theNodesToInsert.push_back ( theBetweenNode2 );
8803 list<const SMDS_MeshNode*>::iterator n = theNodesToInsert.begin();
8804 const SMDS_MeshNode* n1 = *n;
8805 for ( ++n; n != theNodesToInsert.end(); ++n )
8807 const SMDS_MeshNode* n2 = *n;
8808 if ( const SMDS_MeshElement* seg = aMesh->FindEdge( n1, n2 ))
8809 AddToSameGroups( seg, theElement, aMesh );
8811 newElems.push_back( aMesh->AddEdge ( n1, n2 ));
8814 theNodesToInsert.pop_front();
8815 theNodesToInsert.pop_back();
8817 if ( theElement->IsQuadratic() ) // add a not split part
8819 vector<const SMDS_MeshNode*> nodes( theElement->begin_nodes(),
8820 theElement->end_nodes() );
8821 int iOther = 0, nbN = nodes.size();
8822 for ( ; iOther < nbN; ++iOther )
8823 if ( nodes[iOther] != theBetweenNode1 &&
8824 nodes[iOther] != theBetweenNode2 )
8828 if ( const SMDS_MeshElement* seg = aMesh->FindEdge( nodes[0], nodes[1] ))
8829 AddToSameGroups( seg, theElement, aMesh );
8831 newElems.push_back( aMesh->AddEdge ( nodes[0], nodes[1] ));
8833 else if ( iOther == 2 )
8835 if ( const SMDS_MeshElement* seg = aMesh->FindEdge( nodes[1], nodes[2] ))
8836 AddToSameGroups( seg, theElement, aMesh );
8838 newElems.push_back( aMesh->AddEdge ( nodes[1], nodes[2] ));
8841 // treat new elements
8842 for ( size_t i = 0; i < newElems.size(); ++i )
8845 aMesh->SetMeshElementOnShape( newElems[i], theElement->getshapeId() );
8846 myLastCreatedElems.Append( newElems[i] );
8848 ReplaceElemInGroups( theElement, newElems, aMesh );
8849 aMesh->RemoveElement( theElement );
8852 } // if ( theElement->GetType() == SMDSAbs_Edge )
8854 const SMDS_MeshElement* theFace = theElement;
8855 if ( theFace->GetType() != SMDSAbs_Face ) return;
8857 // find indices of 2 link nodes and of the rest nodes
8858 int iNode = 0, il1, il2, i3, i4;
8859 il1 = il2 = i3 = i4 = -1;
8860 vector<const SMDS_MeshNode*> nodes( theFace->NbNodes() );
8862 SMDS_NodeIteratorPtr nodeIt = theFace->interlacedNodesIterator();
8863 while ( nodeIt->more() ) {
8864 const SMDS_MeshNode* n = nodeIt->next();
8865 if ( n == theBetweenNode1 )
8867 else if ( n == theBetweenNode2 )
8873 nodes[ iNode++ ] = n;
8875 if ( il1 < 0 || il2 < 0 || i3 < 0 )
8878 // arrange link nodes to go one after another regarding the face orientation
8879 bool reverse = ( Abs( il2 - il1 ) == 1 ? il2 < il1 : il1 < il2 );
8880 list<const SMDS_MeshNode *> aNodesToInsert = theNodesToInsert;
8885 aNodesToInsert.reverse();
8887 // check that not link nodes of a quadrangles are in good order
8888 int nbFaceNodes = theFace->NbNodes();
8889 if ( nbFaceNodes == 4 && i4 - i3 != 1 ) {
8895 if (toCreatePoly || theFace->IsPoly()) {
8898 vector<const SMDS_MeshNode *> poly_nodes (nbFaceNodes + aNodesToInsert.size());
8900 // add nodes of face up to first node of link
8903 if ( theFace->IsQuadratic() ) {
8904 const SMDS_VtkFace* F = dynamic_cast<const SMDS_VtkFace*>(theFace);
8905 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8906 // use special nodes iterator
8907 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8908 while( anIter->more() && !isFLN ) {
8909 const SMDS_MeshNode* n = cast2Node(anIter->next());
8910 poly_nodes[iNode++] = n;
8911 if (n == nodes[il1]) {
8915 // add nodes to insert
8916 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8917 for (; nIt != aNodesToInsert.end(); nIt++) {
8918 poly_nodes[iNode++] = *nIt;
8920 // add nodes of face starting from last node of link
8921 while ( anIter->more() ) {
8922 poly_nodes[iNode++] = cast2Node(anIter->next());
8926 SMDS_ElemIteratorPtr nodeIt = theFace->nodesIterator();
8927 while ( nodeIt->more() && !isFLN ) {
8928 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8929 poly_nodes[iNode++] = n;
8930 if (n == nodes[il1]) {
8934 // add nodes to insert
8935 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8936 for (; nIt != aNodesToInsert.end(); nIt++) {
8937 poly_nodes[iNode++] = *nIt;
8939 // add nodes of face starting from last node of link
8940 while ( nodeIt->more() ) {
8941 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8942 poly_nodes[iNode++] = n;
8947 newElems.push_back( aMesh->AddPolygonalFace( poly_nodes ));
8950 else if ( !theFace->IsQuadratic() )
8952 // put aNodesToInsert between theBetweenNode1 and theBetweenNode2
8953 int nbLinkNodes = 2 + aNodesToInsert.size();
8954 //const SMDS_MeshNode* linkNodes[ nbLinkNodes ];
8955 vector<const SMDS_MeshNode*> linkNodes( nbLinkNodes );
8956 linkNodes[ 0 ] = nodes[ il1 ];
8957 linkNodes[ nbLinkNodes - 1 ] = nodes[ il2 ];
8958 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8959 for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
8960 linkNodes[ iNode++ ] = *nIt;
8962 // decide how to split a quadrangle: compare possible variants
8963 // and choose which of splits to be a quadrangle
8964 int i1, i2, iSplit, nbSplits = nbLinkNodes - 1, iBestQuad = 0;
8965 if ( nbFaceNodes == 3 ) {
8966 iBestQuad = nbSplits;
8969 else if ( nbFaceNodes == 4 ) {
8970 SMESH::Controls::NumericalFunctorPtr aCrit( new SMESH::Controls::AspectRatio);
8971 double aBestRate = DBL_MAX;
8972 for ( int iQuad = 0; iQuad < nbSplits; iQuad++ ) {
8974 double aBadRate = 0;
8975 // evaluate elements quality
8976 for ( iSplit = 0; iSplit < nbSplits; iSplit++ ) {
8977 if ( iSplit == iQuad ) {
8978 SMDS_FaceOfNodes quad (linkNodes[ i1++ ],
8982 aBadRate += getBadRate( &quad, aCrit );
8985 SMDS_FaceOfNodes tria (linkNodes[ i1++ ],
8987 nodes[ iSplit < iQuad ? i4 : i3 ]);
8988 aBadRate += getBadRate( &tria, aCrit );
8992 if ( aBadRate < aBestRate ) {
8994 aBestRate = aBadRate;
8999 // create new elements
9001 for ( iSplit = 0; iSplit < nbSplits - 1; iSplit++ )
9003 if ( iSplit == iBestQuad )
9004 newElems.push_back( aMesh->AddFace (linkNodes[ i1++ ],
9009 newElems.push_back( aMesh->AddFace (linkNodes[ i1++ ],
9011 nodes[ iSplit < iBestQuad ? i4 : i3 ]));
9014 const SMDS_MeshNode* newNodes[ 4 ];
9015 newNodes[ 0 ] = linkNodes[ i1 ];
9016 newNodes[ 1 ] = linkNodes[ i2 ];
9017 newNodes[ 2 ] = nodes[ iSplit >= iBestQuad ? i3 : i4 ];
9018 newNodes[ 3 ] = nodes[ i4 ];
9019 if (iSplit == iBestQuad)
9020 newElems.push_back( aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2], newNodes[3] ));
9022 newElems.push_back( aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2] ));
9024 } // end if(!theFace->IsQuadratic())
9026 else { // theFace is quadratic
9027 // we have to split theFace on simple triangles and one simple quadrangle
9029 int nbshift = tmp*2;
9030 // shift nodes in nodes[] by nbshift
9032 for(i=0; i<nbshift; i++) {
9033 const SMDS_MeshNode* n = nodes[0];
9034 for(j=0; j<nbFaceNodes-1; j++) {
9035 nodes[j] = nodes[j+1];
9037 nodes[nbFaceNodes-1] = n;
9039 il1 = il1 - nbshift;
9040 // now have to insert nodes between n0 and n1 or n1 and n2 (see below)
9041 // n0 n1 n2 n0 n1 n2
9042 // +-----+-----+ +-----+-----+
9051 // create new elements
9053 if ( nbFaceNodes == 6 ) { // quadratic triangle
9054 newElems.push_back( aMesh->AddFace( nodes[3], nodes[4], nodes[5] ));
9055 if ( theFace->IsMediumNode(nodes[il1]) ) {
9056 // create quadrangle
9057 newElems.push_back( aMesh->AddFace( nodes[0], nodes[1], nodes[3], nodes[5] ));
9063 // create quadrangle
9064 newElems.push_back( aMesh->AddFace( nodes[1], nodes[2], nodes[3], nodes[5] ));
9070 else { // nbFaceNodes==8 - quadratic quadrangle
9071 newElems.push_back( aMesh->AddFace( nodes[3], nodes[4], nodes[5] ));
9072 newElems.push_back( aMesh->AddFace( nodes[5], nodes[6], nodes[7] ));
9073 newElems.push_back( aMesh->AddFace( nodes[5], nodes[7], nodes[3] ));
9074 if ( theFace->IsMediumNode( nodes[ il1 ])) {
9075 // create quadrangle
9076 newElems.push_back( aMesh->AddFace( nodes[0], nodes[1], nodes[3], nodes[7] ));
9082 // create quadrangle
9083 newElems.push_back( aMesh->AddFace( nodes[1], nodes[2], nodes[3], nodes[7] ));
9089 // create needed triangles using n1,n2,n3 and inserted nodes
9090 int nbn = 2 + aNodesToInsert.size();
9091 vector<const SMDS_MeshNode*> aNodes(nbn);
9092 aNodes[0 ] = nodes[n1];
9093 aNodes[nbn-1] = nodes[n2];
9094 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
9095 for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
9096 aNodes[iNode++] = *nIt;
9098 for ( i = 1; i < nbn; i++ )
9099 newElems.push_back( aMesh->AddFace( aNodes[i-1], aNodes[i], nodes[n3] ));
9102 // remove the old face
9103 for ( size_t i = 0; i < newElems.size(); ++i )
9106 aMesh->SetMeshElementOnShape( newElems[i], theFace->getshapeId() );
9107 myLastCreatedElems.Append( newElems[i] );
9109 ReplaceElemInGroups( theFace, newElems, aMesh );
9110 aMesh->RemoveElement(theFace);
9112 } // InsertNodesIntoLink()
9114 //=======================================================================
9115 //function : UpdateVolumes
9117 //=======================================================================
9119 void SMESH_MeshEditor::UpdateVolumes (const SMDS_MeshNode* theBetweenNode1,
9120 const SMDS_MeshNode* theBetweenNode2,
9121 list<const SMDS_MeshNode*>& theNodesToInsert)
9123 myLastCreatedElems.Clear();
9124 myLastCreatedNodes.Clear();
9126 SMDS_ElemIteratorPtr invElemIt = theBetweenNode1->GetInverseElementIterator(SMDSAbs_Volume);
9127 while (invElemIt->more()) { // loop on inverse elements of theBetweenNode1
9128 const SMDS_MeshElement* elem = invElemIt->next();
9130 // check, if current volume has link theBetweenNode1 - theBetweenNode2
9131 SMDS_VolumeTool aVolume (elem);
9132 if (!aVolume.IsLinked(theBetweenNode1, theBetweenNode2))
9135 // insert new nodes in all faces of the volume, sharing link theBetweenNode1 - theBetweenNode2
9136 int iface, nbFaces = aVolume.NbFaces();
9137 vector<const SMDS_MeshNode *> poly_nodes;
9138 vector<int> quantities (nbFaces);
9140 for (iface = 0; iface < nbFaces; iface++) {
9141 int nbFaceNodes = aVolume.NbFaceNodes(iface), nbInserted = 0;
9142 // faceNodes will contain (nbFaceNodes + 1) nodes, last = first
9143 const SMDS_MeshNode** faceNodes = aVolume.GetFaceNodes(iface);
9145 for (int inode = 0; inode < nbFaceNodes; inode++) {
9146 poly_nodes.push_back(faceNodes[inode]);
9148 if (nbInserted == 0) {
9149 if (faceNodes[inode] == theBetweenNode1) {
9150 if (faceNodes[inode + 1] == theBetweenNode2) {
9151 nbInserted = theNodesToInsert.size();
9153 // add nodes to insert
9154 list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.begin();
9155 for (; nIt != theNodesToInsert.end(); nIt++) {
9156 poly_nodes.push_back(*nIt);
9160 else if (faceNodes[inode] == theBetweenNode2) {
9161 if (faceNodes[inode + 1] == theBetweenNode1) {
9162 nbInserted = theNodesToInsert.size();
9164 // add nodes to insert in reversed order
9165 list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.end();
9167 for (; nIt != theNodesToInsert.begin(); nIt--) {
9168 poly_nodes.push_back(*nIt);
9170 poly_nodes.push_back(*nIt);
9177 quantities[iface] = nbFaceNodes + nbInserted;
9180 // Replace the volume
9181 SMESHDS_Mesh *aMesh = GetMeshDS();
9183 if ( SMDS_MeshElement* newElem = aMesh->AddPolyhedralVolume( poly_nodes, quantities ))
9185 aMesh->SetMeshElementOnShape( newElem, elem->getshapeId() );
9186 myLastCreatedElems.Append( newElem );
9187 ReplaceElemInGroups( elem, newElem, aMesh );
9189 aMesh->RemoveElement( elem );
9195 //================================================================================
9197 * \brief Transform any volume into data of SMDSEntity_Polyhedra
9199 //================================================================================
9201 void volumeToPolyhedron( const SMDS_MeshElement* elem,
9202 vector<const SMDS_MeshNode *> & nodes,
9203 vector<int> & nbNodeInFaces )
9206 nbNodeInFaces.clear();
9207 SMDS_VolumeTool vTool ( elem );
9208 for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
9210 const SMDS_MeshNode** fNodes = vTool.GetFaceNodes( iF );
9211 nodes.insert( nodes.end(), fNodes, fNodes + vTool.NbFaceNodes( iF ));
9212 nbNodeInFaces.push_back( vTool.NbFaceNodes( iF ));
9217 //=======================================================================
9219 * \brief Convert elements contained in a sub-mesh to quadratic
9220 * \return int - nb of checked elements
9222 //=======================================================================
9224 int SMESH_MeshEditor::convertElemToQuadratic(SMESHDS_SubMesh * theSm,
9225 SMESH_MesherHelper& theHelper,
9226 const bool theForce3d)
9228 //MESSAGE("convertElemToQuadratic");
9230 if( !theSm ) return nbElem;
9232 vector<int> nbNodeInFaces;
9233 vector<const SMDS_MeshNode *> nodes;
9234 SMDS_ElemIteratorPtr ElemItr = theSm->GetElements();
9235 while(ElemItr->more())
9238 const SMDS_MeshElement* elem = ElemItr->next();
9239 if( !elem ) continue;
9241 // analyse a necessity of conversion
9242 const SMDSAbs_ElementType aType = elem->GetType();
9243 if ( aType < SMDSAbs_Edge || aType > SMDSAbs_Volume )
9245 const SMDSAbs_EntityType aGeomType = elem->GetEntityType();
9246 bool hasCentralNodes = false;
9247 if ( elem->IsQuadratic() )
9250 switch ( aGeomType ) {
9251 case SMDSEntity_Quad_Triangle:
9252 case SMDSEntity_Quad_Quadrangle:
9253 case SMDSEntity_Quad_Hexa:
9254 case SMDSEntity_Quad_Penta:
9255 alreadyOK = !theHelper.GetIsBiQuadratic(); break;
9257 case SMDSEntity_BiQuad_Triangle:
9258 case SMDSEntity_BiQuad_Quadrangle:
9259 case SMDSEntity_TriQuad_Hexa:
9260 case SMDSEntity_BiQuad_Penta:
9261 alreadyOK = theHelper.GetIsBiQuadratic();
9262 hasCentralNodes = true;
9267 // take into account already present medium nodes
9269 case SMDSAbs_Volume:
9270 theHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( elem )); break;
9272 theHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( elem )); break;
9274 theHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( elem )); break;
9280 // get elem data needed to re-create it
9282 const int id = elem->GetID();
9283 const int nbNodes = elem->NbCornerNodes();
9284 nodes.assign(elem->begin_nodes(), elem->end_nodes());
9285 if ( aGeomType == SMDSEntity_Polyhedra )
9286 nbNodeInFaces = static_cast<const SMDS_VtkVolume* >( elem )->GetQuantities();
9287 else if ( aGeomType == SMDSEntity_Hexagonal_Prism )
9288 volumeToPolyhedron( elem, nodes, nbNodeInFaces );
9290 // remove a linear element
9291 GetMeshDS()->RemoveFreeElement(elem, theSm, /*fromGroups=*/false);
9293 // remove central nodes of biquadratic elements (biquad->quad conversion)
9294 if ( hasCentralNodes )
9295 for ( size_t i = nbNodes * 2; i < nodes.size(); ++i )
9296 if ( nodes[i]->NbInverseElements() == 0 )
9297 GetMeshDS()->RemoveFreeNode( nodes[i], theSm, /*fromGroups=*/true );
9299 const SMDS_MeshElement* NewElem = 0;
9305 NewElem = theHelper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9313 NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
9316 NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9319 NewElem = theHelper.AddPolygonalFace(nodes, id, theForce3d);
9323 case SMDSAbs_Volume :
9327 case SMDSEntity_Tetra:
9328 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9330 case SMDSEntity_Pyramid:
9331 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], id, theForce3d);
9333 case SMDSEntity_Penta:
9334 case SMDSEntity_Quad_Penta:
9335 case SMDSEntity_BiQuad_Penta:
9336 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], nodes[5], id, theForce3d);
9338 case SMDSEntity_Hexa:
9339 case SMDSEntity_Quad_Hexa:
9340 case SMDSEntity_TriQuad_Hexa:
9341 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9342 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9344 case SMDSEntity_Hexagonal_Prism:
9346 NewElem = theHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9353 ReplaceElemInGroups( elem, NewElem, GetMeshDS());
9354 if( NewElem && NewElem->getshapeId() < 1 )
9355 theSm->AddElement( NewElem );
9359 //=======================================================================
9360 //function : ConvertToQuadratic
9362 //=======================================================================
9364 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d, const bool theToBiQuad)
9366 //MESSAGE("ConvertToQuadratic "<< theForce3d << " " << theToBiQuad);
9367 SMESHDS_Mesh* meshDS = GetMeshDS();
9369 SMESH_MesherHelper aHelper(*myMesh);
9371 aHelper.SetIsQuadratic( true );
9372 aHelper.SetIsBiQuadratic( theToBiQuad );
9373 aHelper.SetElementsOnShape(true);
9374 aHelper.ToFixNodeParameters( true );
9376 // convert elements assigned to sub-meshes
9377 int nbCheckedElems = 0;
9378 if ( myMesh->HasShapeToMesh() )
9380 if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9382 SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9383 while ( smIt->more() ) {
9384 SMESH_subMesh* sm = smIt->next();
9385 if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() ) {
9386 aHelper.SetSubShape( sm->GetSubShape() );
9387 nbCheckedElems += convertElemToQuadratic(smDS, aHelper, theForce3d);
9393 // convert elements NOT assigned to sub-meshes
9394 int totalNbElems = meshDS->NbEdges() + meshDS->NbFaces() + meshDS->NbVolumes();
9395 if ( nbCheckedElems < totalNbElems ) // not all elements are in sub-meshes
9397 aHelper.SetElementsOnShape(false);
9398 SMESHDS_SubMesh *smDS = 0;
9401 SMDS_EdgeIteratorPtr aEdgeItr = meshDS->edgesIterator();
9402 while( aEdgeItr->more() )
9404 const SMDS_MeshEdge* edge = aEdgeItr->next();
9405 if ( !edge->IsQuadratic() )
9407 int id = edge->GetID();
9408 const SMDS_MeshNode* n1 = edge->GetNode(0);
9409 const SMDS_MeshNode* n2 = edge->GetNode(1);
9411 meshDS->RemoveFreeElement(edge, smDS, /*fromGroups=*/false);
9413 const SMDS_MeshEdge* NewEdge = aHelper.AddEdge(n1, n2, id, theForce3d);
9414 ReplaceElemInGroups( edge, NewEdge, GetMeshDS());
9418 aHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( edge ));
9423 SMDS_FaceIteratorPtr aFaceItr = meshDS->facesIterator();
9424 while( aFaceItr->more() )
9426 const SMDS_MeshFace* face = aFaceItr->next();
9427 if ( !face ) continue;
9429 const SMDSAbs_EntityType type = face->GetEntityType();
9433 case SMDSEntity_Quad_Triangle:
9434 case SMDSEntity_Quad_Quadrangle:
9435 alreadyOK = !theToBiQuad;
9436 aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
9438 case SMDSEntity_BiQuad_Triangle:
9439 case SMDSEntity_BiQuad_Quadrangle:
9440 alreadyOK = theToBiQuad;
9441 aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
9443 default: alreadyOK = false;
9448 const int id = face->GetID();
9449 vector<const SMDS_MeshNode *> nodes ( face->begin_nodes(), face->end_nodes());
9451 meshDS->RemoveFreeElement(face, smDS, /*fromGroups=*/false);
9453 SMDS_MeshFace * NewFace = 0;
9456 case SMDSEntity_Triangle:
9457 case SMDSEntity_Quad_Triangle:
9458 case SMDSEntity_BiQuad_Triangle:
9459 NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
9460 if ( nodes.size() == 7 && nodes[6]->NbInverseElements() == 0 ) // rm a central node
9461 GetMeshDS()->RemoveFreeNode( nodes[6], /*sm=*/0, /*fromGroups=*/true );
9464 case SMDSEntity_Quadrangle:
9465 case SMDSEntity_Quad_Quadrangle:
9466 case SMDSEntity_BiQuad_Quadrangle:
9467 NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9468 if ( nodes.size() == 9 && nodes[8]->NbInverseElements() == 0 ) // rm a central node
9469 GetMeshDS()->RemoveFreeNode( nodes[8], /*sm=*/0, /*fromGroups=*/true );
9473 NewFace = aHelper.AddPolygonalFace(nodes, id, theForce3d);
9475 ReplaceElemInGroups( face, NewFace, GetMeshDS());
9479 vector<int> nbNodeInFaces;
9480 SMDS_VolumeIteratorPtr aVolumeItr = meshDS->volumesIterator();
9481 while(aVolumeItr->more())
9483 const SMDS_MeshVolume* volume = aVolumeItr->next();
9484 if ( !volume ) continue;
9486 const SMDSAbs_EntityType type = volume->GetEntityType();
9487 if ( volume->IsQuadratic() )
9492 case SMDSEntity_Quad_Hexa: alreadyOK = !theToBiQuad; break;
9493 case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; break;
9494 case SMDSEntity_Quad_Penta: alreadyOK = !theToBiQuad; break;
9495 case SMDSEntity_BiQuad_Penta: alreadyOK = theToBiQuad; break;
9496 default: alreadyOK = true;
9500 aHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( volume ));
9504 const int id = volume->GetID();
9505 vector<const SMDS_MeshNode *> nodes (volume->begin_nodes(), volume->end_nodes());
9506 if ( type == SMDSEntity_Polyhedra )
9507 nbNodeInFaces = static_cast<const SMDS_VtkVolume* >(volume)->GetQuantities();
9508 else if ( type == SMDSEntity_Hexagonal_Prism )
9509 volumeToPolyhedron( volume, nodes, nbNodeInFaces );
9511 meshDS->RemoveFreeElement(volume, smDS, /*fromGroups=*/false);
9513 SMDS_MeshVolume * NewVolume = 0;
9516 case SMDSEntity_Tetra:
9517 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d );
9519 case SMDSEntity_Hexa:
9520 case SMDSEntity_Quad_Hexa:
9521 case SMDSEntity_TriQuad_Hexa:
9522 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9523 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9524 for ( size_t i = 20; i < nodes.size(); ++i ) // rm central nodes
9525 if ( nodes[i]->NbInverseElements() == 0 )
9526 GetMeshDS()->RemoveFreeNode( nodes[i], /*sm=*/0, /*fromGroups=*/true );
9528 case SMDSEntity_Pyramid:
9529 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9530 nodes[3], nodes[4], id, theForce3d);
9532 case SMDSEntity_Penta:
9533 case SMDSEntity_Quad_Penta:
9534 case SMDSEntity_BiQuad_Penta:
9535 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9536 nodes[3], nodes[4], nodes[5], id, theForce3d);
9537 for ( size_t i = 15; i < nodes.size(); ++i ) // rm central nodes
9538 if ( nodes[i]->NbInverseElements() == 0 )
9539 GetMeshDS()->RemoveFreeNode( nodes[i], /*sm=*/0, /*fromGroups=*/true );
9541 case SMDSEntity_Hexagonal_Prism:
9543 NewVolume = aHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9545 ReplaceElemInGroups(volume, NewVolume, meshDS);
9550 { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9551 // aHelper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9552 // aHelper.FixQuadraticElements(myError);
9553 SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
9557 //================================================================================
9559 * \brief Makes given elements quadratic
9560 * \param theForce3d - if true, the medium nodes will be placed in the middle of link
9561 * \param theElements - elements to make quadratic
9563 //================================================================================
9565 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d,
9566 TIDSortedElemSet& theElements,
9567 const bool theToBiQuad)
9569 if ( theElements.empty() ) return;
9571 // we believe that all theElements are of the same type
9572 const SMDSAbs_ElementType elemType = (*theElements.begin())->GetType();
9574 // get all nodes shared by theElements
9575 TIDSortedNodeSet allNodes;
9576 TIDSortedElemSet::iterator eIt = theElements.begin();
9577 for ( ; eIt != theElements.end(); ++eIt )
9578 allNodes.insert( (*eIt)->begin_nodes(), (*eIt)->end_nodes() );
9580 // complete theElements with elements of lower dim whose all nodes are in allNodes
9582 TIDSortedElemSet quadAdjacentElems [ SMDSAbs_NbElementTypes ]; // quadratic adjacent elements
9583 TIDSortedElemSet checkedAdjacentElems [ SMDSAbs_NbElementTypes ];
9584 TIDSortedNodeSet::iterator nIt = allNodes.begin();
9585 for ( ; nIt != allNodes.end(); ++nIt )
9587 const SMDS_MeshNode* n = *nIt;
9588 SMDS_ElemIteratorPtr invIt = n->GetInverseElementIterator();
9589 while ( invIt->more() )
9591 const SMDS_MeshElement* e = invIt->next();
9592 const SMDSAbs_ElementType type = e->GetType();
9593 if ( e->IsQuadratic() )
9595 quadAdjacentElems[ type ].insert( e );
9598 switch ( e->GetEntityType() ) {
9599 case SMDSEntity_Quad_Triangle:
9600 case SMDSEntity_Quad_Quadrangle:
9601 case SMDSEntity_Quad_Hexa: alreadyOK = !theToBiQuad; break;
9602 case SMDSEntity_BiQuad_Triangle:
9603 case SMDSEntity_BiQuad_Quadrangle:
9604 case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; break;
9605 default: alreadyOK = true;
9610 if ( type >= elemType )
9611 continue; // same type or more complex linear element
9613 if ( !checkedAdjacentElems[ type ].insert( e ).second )
9614 continue; // e is already checked
9618 SMDS_NodeIteratorPtr nodeIt = e->nodeIterator();
9619 while ( nodeIt->more() && allIn )
9620 allIn = allNodes.count( nodeIt->next() );
9622 theElements.insert(e );
9626 SMESH_MesherHelper helper(*myMesh);
9627 helper.SetIsQuadratic( true );
9628 helper.SetIsBiQuadratic( theToBiQuad );
9630 // add links of quadratic adjacent elements to the helper
9632 if ( !quadAdjacentElems[SMDSAbs_Edge].empty() )
9633 for ( eIt = quadAdjacentElems[SMDSAbs_Edge].begin();
9634 eIt != quadAdjacentElems[SMDSAbs_Edge].end(); ++eIt )
9636 helper.AddTLinks( static_cast< const SMDS_MeshEdge*> (*eIt) );
9638 if ( !quadAdjacentElems[SMDSAbs_Face].empty() )
9639 for ( eIt = quadAdjacentElems[SMDSAbs_Face].begin();
9640 eIt != quadAdjacentElems[SMDSAbs_Face].end(); ++eIt )
9642 helper.AddTLinks( static_cast< const SMDS_MeshFace*> (*eIt) );
9644 if ( !quadAdjacentElems[SMDSAbs_Volume].empty() )
9645 for ( eIt = quadAdjacentElems[SMDSAbs_Volume].begin();
9646 eIt != quadAdjacentElems[SMDSAbs_Volume].end(); ++eIt )
9648 helper.AddTLinks( static_cast< const SMDS_MeshVolume*> (*eIt) );
9651 // make quadratic (or bi-tri-quadratic) elements instead of linear ones
9653 SMESHDS_Mesh* meshDS = GetMeshDS();
9654 SMESHDS_SubMesh* smDS = 0;
9655 for ( eIt = theElements.begin(); eIt != theElements.end(); ++eIt )
9657 const SMDS_MeshElement* elem = *eIt;
9660 int nbCentralNodes = 0;
9661 switch ( elem->GetEntityType() ) {
9662 // linear convertible
9663 case SMDSEntity_Edge:
9664 case SMDSEntity_Triangle:
9665 case SMDSEntity_Quadrangle:
9666 case SMDSEntity_Tetra:
9667 case SMDSEntity_Pyramid:
9668 case SMDSEntity_Hexa:
9669 case SMDSEntity_Penta: alreadyOK = false; nbCentralNodes = 0; break;
9670 // quadratic that can become bi-quadratic
9671 case SMDSEntity_Quad_Triangle:
9672 case SMDSEntity_Quad_Quadrangle:
9673 case SMDSEntity_Quad_Hexa: alreadyOK =!theToBiQuad; nbCentralNodes = 0; break;
9675 case SMDSEntity_BiQuad_Triangle:
9676 case SMDSEntity_BiQuad_Quadrangle: alreadyOK = theToBiQuad; nbCentralNodes = 1; break;
9677 case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; nbCentralNodes = 7; break;
9679 default: alreadyOK = true;
9681 if ( alreadyOK ) continue;
9683 const SMDSAbs_ElementType type = elem->GetType();
9684 const int id = elem->GetID();
9685 const int nbNodes = elem->NbCornerNodes();
9686 vector<const SMDS_MeshNode *> nodes ( elem->begin_nodes(), elem->end_nodes());
9688 helper.SetSubShape( elem->getshapeId() );
9690 if ( !smDS || !smDS->Contains( elem ))
9691 smDS = meshDS->MeshElements( elem->getshapeId() );
9692 meshDS->RemoveFreeElement(elem, smDS, /*fromGroups=*/false);
9694 SMDS_MeshElement * newElem = 0;
9697 case 4: // cases for most frequently used element types go first (for optimization)
9698 if ( type == SMDSAbs_Volume )
9699 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9701 newElem = helper.AddFace (nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9704 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9705 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9708 newElem = helper.AddFace (nodes[0], nodes[1], nodes[2], id, theForce3d);
9711 newElem = helper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9714 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9715 nodes[4], id, theForce3d);
9718 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9719 nodes[4], nodes[5], id, theForce3d);
9723 ReplaceElemInGroups( elem, newElem, meshDS);
9724 if( newElem && smDS )
9725 smDS->AddElement( newElem );
9727 // remove central nodes
9728 for ( size_t i = nodes.size() - nbCentralNodes; i < nodes.size(); ++i )
9729 if ( nodes[i]->NbInverseElements() == 0 )
9730 meshDS->RemoveFreeNode( nodes[i], smDS, /*fromGroups=*/true );
9732 } // loop on theElements
9735 { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9736 // helper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9737 // helper.FixQuadraticElements( myError );
9738 SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
9742 //=======================================================================
9744 * \brief Convert quadratic elements to linear ones and remove quadratic nodes
9745 * \return int - nb of checked elements
9747 //=======================================================================
9749 int SMESH_MeshEditor::removeQuadElem(SMESHDS_SubMesh * theSm,
9750 SMDS_ElemIteratorPtr theItr,
9751 const int theShapeID)
9754 SMESHDS_Mesh* meshDS = GetMeshDS();
9755 ElemFeatures elemType;
9756 vector<const SMDS_MeshNode *> nodes;
9758 while( theItr->more() )
9760 const SMDS_MeshElement* elem = theItr->next();
9762 if( elem && elem->IsQuadratic())
9765 int nbCornerNodes = elem->NbCornerNodes();
9766 nodes.assign( elem->begin_nodes(), elem->end_nodes() );
9768 elemType.Init( elem, /*basicOnly=*/false ).SetID( elem->GetID() ).SetQuad( false );
9770 //remove a quadratic element
9771 if ( !theSm || !theSm->Contains( elem ))
9772 theSm = meshDS->MeshElements( elem->getshapeId() );
9773 meshDS->RemoveFreeElement( elem, theSm, /*fromGroups=*/false );
9775 // remove medium nodes
9776 for ( size_t i = nbCornerNodes; i < nodes.size(); ++i )
9777 if ( nodes[i]->NbInverseElements() == 0 )
9778 meshDS->RemoveFreeNode( nodes[i], theSm );
9780 // add a linear element
9781 nodes.resize( nbCornerNodes );
9782 SMDS_MeshElement * newElem = AddElement( nodes, elemType );
9783 ReplaceElemInGroups(elem, newElem, meshDS);
9784 if( theSm && newElem )
9785 theSm->AddElement( newElem );
9791 //=======================================================================
9792 //function : ConvertFromQuadratic
9794 //=======================================================================
9796 bool SMESH_MeshEditor::ConvertFromQuadratic()
9798 int nbCheckedElems = 0;
9799 if ( myMesh->HasShapeToMesh() )
9801 if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9803 SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9804 while ( smIt->more() ) {
9805 SMESH_subMesh* sm = smIt->next();
9806 if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() )
9807 nbCheckedElems += removeQuadElem( smDS, smDS->GetElements(), sm->GetId() );
9813 GetMeshDS()->NbEdges() + GetMeshDS()->NbFaces() + GetMeshDS()->NbVolumes();
9814 if ( nbCheckedElems < totalNbElems ) // not all elements are in submeshes
9816 SMESHDS_SubMesh *aSM = 0;
9817 removeQuadElem( aSM, GetMeshDS()->elementsIterator(), 0 );
9825 //================================================================================
9827 * \brief Return true if all medium nodes of the element are in the node set
9829 //================================================================================
9831 bool allMediumNodesIn(const SMDS_MeshElement* elem, TIDSortedNodeSet& nodeSet )
9833 for ( int i = elem->NbCornerNodes(); i < elem->NbNodes(); ++i )
9834 if ( !nodeSet.count( elem->GetNode(i) ))
9840 //================================================================================
9842 * \brief Makes given elements linear
9844 //================================================================================
9846 void SMESH_MeshEditor::ConvertFromQuadratic(TIDSortedElemSet& theElements)
9848 if ( theElements.empty() ) return;
9850 // collect IDs of medium nodes of theElements; some of these nodes will be removed
9851 set<int> mediumNodeIDs;
9852 TIDSortedElemSet::iterator eIt = theElements.begin();
9853 for ( ; eIt != theElements.end(); ++eIt )
9855 const SMDS_MeshElement* e = *eIt;
9856 for ( int i = e->NbCornerNodes(); i < e->NbNodes(); ++i )
9857 mediumNodeIDs.insert( e->GetNode(i)->GetID() );
9860 // replace given elements by linear ones
9861 SMDS_ElemIteratorPtr elemIt = elemSetIterator( theElements );
9862 removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9864 // we need to convert remaining elements whose all medium nodes are in mediumNodeIDs
9865 // except those elements sharing medium nodes of quadratic element whose medium nodes
9866 // are not all in mediumNodeIDs
9868 // get remaining medium nodes
9869 TIDSortedNodeSet mediumNodes;
9870 set<int>::iterator nIdsIt = mediumNodeIDs.begin();
9871 for ( ; nIdsIt != mediumNodeIDs.end(); ++nIdsIt )
9872 if ( const SMDS_MeshNode* n = GetMeshDS()->FindNode( *nIdsIt ))
9873 mediumNodes.insert( mediumNodes.end(), n );
9875 // find more quadratic elements to convert
9876 TIDSortedElemSet moreElemsToConvert;
9877 TIDSortedNodeSet::iterator nIt = mediumNodes.begin();
9878 for ( ; nIt != mediumNodes.end(); ++nIt )
9880 SMDS_ElemIteratorPtr invIt = (*nIt)->GetInverseElementIterator();
9881 while ( invIt->more() )
9883 const SMDS_MeshElement* e = invIt->next();
9884 if ( e->IsQuadratic() && allMediumNodesIn( e, mediumNodes ))
9886 // find a more complex element including e and
9887 // whose medium nodes are not in mediumNodes
9888 bool complexFound = false;
9889 for ( int type = e->GetType() + 1; type < SMDSAbs_0DElement; ++type )
9891 SMDS_ElemIteratorPtr invIt2 =
9892 (*nIt)->GetInverseElementIterator( SMDSAbs_ElementType( type ));
9893 while ( invIt2->more() )
9895 const SMDS_MeshElement* eComplex = invIt2->next();
9896 if ( eComplex->IsQuadratic() && !allMediumNodesIn( eComplex, mediumNodes))
9898 int nbCommonNodes = SMESH_MeshAlgos::GetCommonNodes( e, eComplex ).size();
9899 if ( nbCommonNodes == e->NbNodes())
9901 complexFound = true;
9902 type = SMDSAbs_NbElementTypes; // to quit from the outer loop
9908 if ( !complexFound )
9909 moreElemsToConvert.insert( e );
9913 elemIt = elemSetIterator( moreElemsToConvert );
9914 removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9917 //=======================================================================
9918 //function : SewSideElements
9920 //=======================================================================
9922 SMESH_MeshEditor::Sew_Error
9923 SMESH_MeshEditor::SewSideElements (TIDSortedElemSet& theSide1,
9924 TIDSortedElemSet& theSide2,
9925 const SMDS_MeshNode* theFirstNode1,
9926 const SMDS_MeshNode* theFirstNode2,
9927 const SMDS_MeshNode* theSecondNode1,
9928 const SMDS_MeshNode* theSecondNode2)
9930 myLastCreatedElems.Clear();
9931 myLastCreatedNodes.Clear();
9933 if ( theSide1.size() != theSide2.size() )
9934 return SEW_DIFF_NB_OF_ELEMENTS;
9936 Sew_Error aResult = SEW_OK;
9938 // 1. Build set of faces representing each side
9939 // 2. Find which nodes of the side 1 to merge with ones on the side 2
9940 // 3. Replace nodes in elements of the side 1 and remove replaced nodes
9942 // =======================================================================
9943 // 1. Build set of faces representing each side:
9944 // =======================================================================
9945 // a. build set of nodes belonging to faces
9946 // b. complete set of faces: find missing faces whose nodes are in set of nodes
9947 // c. create temporary faces representing side of volumes if correspondent
9948 // face does not exist
9950 SMESHDS_Mesh* aMesh = GetMeshDS();
9951 // TODO algorithm not OK with vtkUnstructuredGrid: 2 meshes can't share nodes
9952 //SMDS_Mesh aTmpFacesMesh; // try to use the same mesh
9953 TIDSortedElemSet faceSet1, faceSet2;
9954 set<const SMDS_MeshElement*> volSet1, volSet2;
9955 set<const SMDS_MeshNode*> nodeSet1, nodeSet2;
9956 TIDSortedElemSet * faceSetPtr[] = { &faceSet1, &faceSet2 };
9957 set<const SMDS_MeshElement*> * volSetPtr[] = { &volSet1, &volSet2 };
9958 set<const SMDS_MeshNode*> * nodeSetPtr[] = { &nodeSet1, &nodeSet2 };
9959 TIDSortedElemSet * elemSetPtr[] = { &theSide1, &theSide2 };
9960 int iSide, iFace, iNode;
9962 list<const SMDS_MeshElement* > tempFaceList;
9963 for ( iSide = 0; iSide < 2; iSide++ ) {
9964 set<const SMDS_MeshNode*> * nodeSet = nodeSetPtr[ iSide ];
9965 TIDSortedElemSet * elemSet = elemSetPtr[ iSide ];
9966 TIDSortedElemSet * faceSet = faceSetPtr[ iSide ];
9967 set<const SMDS_MeshElement*> * volSet = volSetPtr [ iSide ];
9968 set<const SMDS_MeshElement*>::iterator vIt;
9969 TIDSortedElemSet::iterator eIt;
9970 set<const SMDS_MeshNode*>::iterator nIt;
9972 // check that given nodes belong to given elements
9973 const SMDS_MeshNode* n1 = ( iSide == 0 ) ? theFirstNode1 : theFirstNode2;
9974 const SMDS_MeshNode* n2 = ( iSide == 0 ) ? theSecondNode1 : theSecondNode2;
9975 int firstIndex = -1, secondIndex = -1;
9976 for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9977 const SMDS_MeshElement* elem = *eIt;
9978 if ( firstIndex < 0 ) firstIndex = elem->GetNodeIndex( n1 );
9979 if ( secondIndex < 0 ) secondIndex = elem->GetNodeIndex( n2 );
9980 if ( firstIndex > -1 && secondIndex > -1 ) break;
9982 if ( firstIndex < 0 || secondIndex < 0 ) {
9983 // we can simply return until temporary faces created
9984 return (iSide == 0 ) ? SEW_BAD_SIDE1_NODES : SEW_BAD_SIDE2_NODES;
9987 // -----------------------------------------------------------
9988 // 1a. Collect nodes of existing faces
9989 // and build set of face nodes in order to detect missing
9990 // faces corresponding to sides of volumes
9991 // -----------------------------------------------------------
9993 set< set <const SMDS_MeshNode*> > setOfFaceNodeSet;
9995 // loop on the given element of a side
9996 for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9997 //const SMDS_MeshElement* elem = *eIt;
9998 const SMDS_MeshElement* elem = *eIt;
9999 if ( elem->GetType() == SMDSAbs_Face ) {
10000 faceSet->insert( elem );
10001 set <const SMDS_MeshNode*> faceNodeSet;
10002 SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
10003 while ( nodeIt->more() ) {
10004 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10005 nodeSet->insert( n );
10006 faceNodeSet.insert( n );
10008 setOfFaceNodeSet.insert( faceNodeSet );
10010 else if ( elem->GetType() == SMDSAbs_Volume )
10011 volSet->insert( elem );
10013 // ------------------------------------------------------------------------------
10014 // 1b. Complete set of faces: find missing faces whose nodes are in set of nodes
10015 // ------------------------------------------------------------------------------
10017 for ( nIt = nodeSet->begin(); nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
10018 SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
10019 while ( fIt->more() ) { // loop on faces sharing a node
10020 const SMDS_MeshElement* f = fIt->next();
10021 if ( faceSet->find( f ) == faceSet->end() ) {
10022 // check if all nodes are in nodeSet and
10023 // complete setOfFaceNodeSet if they are
10024 set <const SMDS_MeshNode*> faceNodeSet;
10025 SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
10026 bool allInSet = true;
10027 while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
10028 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10029 if ( nodeSet->find( n ) == nodeSet->end() )
10032 faceNodeSet.insert( n );
10035 faceSet->insert( f );
10036 setOfFaceNodeSet.insert( faceNodeSet );
10042 // -------------------------------------------------------------------------
10043 // 1c. Create temporary faces representing sides of volumes if correspondent
10044 // face does not exist
10045 // -------------------------------------------------------------------------
10047 if ( !volSet->empty() ) {
10048 //int nodeSetSize = nodeSet->size();
10050 // loop on given volumes
10051 for ( vIt = volSet->begin(); vIt != volSet->end(); vIt++ ) {
10052 SMDS_VolumeTool vol (*vIt);
10053 // loop on volume faces: find free faces
10054 // --------------------------------------
10055 list<const SMDS_MeshElement* > freeFaceList;
10056 for ( iFace = 0; iFace < vol.NbFaces(); iFace++ ) {
10057 if ( !vol.IsFreeFace( iFace ))
10059 // check if there is already a face with same nodes in a face set
10060 const SMDS_MeshElement* aFreeFace = 0;
10061 const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iFace );
10062 int nbNodes = vol.NbFaceNodes( iFace );
10063 set <const SMDS_MeshNode*> faceNodeSet;
10064 vol.GetFaceNodes( iFace, faceNodeSet );
10065 bool isNewFace = setOfFaceNodeSet.insert( faceNodeSet ).second;
10067 // no such a face is given but it still can exist, check it
10068 vector<const SMDS_MeshNode *> nodes ( fNodes, fNodes + nbNodes);
10069 aFreeFace = aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false );
10071 if ( !aFreeFace ) {
10072 // create a temporary face
10073 if ( nbNodes == 3 ) {
10074 //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2] );
10075 aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2] );
10077 else if ( nbNodes == 4 ) {
10078 //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
10079 aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
10082 vector<const SMDS_MeshNode *> poly_nodes ( fNodes, & fNodes[nbNodes]);
10083 //aFreeFace = aTmpFacesMesh.AddPolygonalFace(poly_nodes);
10084 aFreeFace = aMesh->AddPolygonalFace(poly_nodes);
10087 tempFaceList.push_back( aFreeFace );
10091 freeFaceList.push_back( aFreeFace );
10093 } // loop on faces of a volume
10095 // choose one of several free faces of a volume
10096 // --------------------------------------------
10097 if ( freeFaceList.size() > 1 ) {
10098 // choose a face having max nb of nodes shared by other elems of a side
10099 int maxNbNodes = -1;
10100 list<const SMDS_MeshElement* >::iterator fIt = freeFaceList.begin();
10101 while ( fIt != freeFaceList.end() ) { // loop on free faces
10102 int nbSharedNodes = 0;
10103 SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
10104 while ( nodeIt->more() ) { // loop on free face nodes
10105 const SMDS_MeshNode* n =
10106 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10107 SMDS_ElemIteratorPtr invElemIt = n->GetInverseElementIterator();
10108 while ( invElemIt->more() ) {
10109 const SMDS_MeshElement* e = invElemIt->next();
10110 nbSharedNodes += faceSet->count( e );
10111 nbSharedNodes += elemSet->count( e );
10114 if ( nbSharedNodes > maxNbNodes ) {
10115 maxNbNodes = nbSharedNodes;
10116 freeFaceList.erase( freeFaceList.begin(), fIt++ );
10118 else if ( nbSharedNodes == maxNbNodes ) {
10122 freeFaceList.erase( fIt++ ); // here fIt++ occurs before erase
10125 if ( freeFaceList.size() > 1 )
10127 // could not choose one face, use another way
10128 // choose a face most close to the bary center of the opposite side
10129 gp_XYZ aBC( 0., 0., 0. );
10130 set <const SMDS_MeshNode*> addedNodes;
10131 TIDSortedElemSet * elemSet2 = elemSetPtr[ 1 - iSide ];
10132 eIt = elemSet2->begin();
10133 for ( eIt = elemSet2->begin(); eIt != elemSet2->end(); eIt++ ) {
10134 SMDS_ElemIteratorPtr nodeIt = (*eIt)->nodesIterator();
10135 while ( nodeIt->more() ) { // loop on free face nodes
10136 const SMDS_MeshNode* n =
10137 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10138 if ( addedNodes.insert( n ).second )
10139 aBC += gp_XYZ( n->X(),n->Y(),n->Z() );
10142 aBC /= addedNodes.size();
10143 double minDist = DBL_MAX;
10144 fIt = freeFaceList.begin();
10145 while ( fIt != freeFaceList.end() ) { // loop on free faces
10147 SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
10148 while ( nodeIt->more() ) { // loop on free face nodes
10149 const SMDS_MeshNode* n =
10150 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10151 gp_XYZ p( n->X(),n->Y(),n->Z() );
10152 dist += ( aBC - p ).SquareModulus();
10154 if ( dist < minDist ) {
10156 freeFaceList.erase( freeFaceList.begin(), fIt++ );
10159 fIt = freeFaceList.erase( fIt++ );
10162 } // choose one of several free faces of a volume
10164 if ( freeFaceList.size() == 1 ) {
10165 const SMDS_MeshElement* aFreeFace = freeFaceList.front();
10166 faceSet->insert( aFreeFace );
10167 // complete a node set with nodes of a found free face
10168 // for ( iNode = 0; iNode < ; iNode++ )
10169 // nodeSet->insert( fNodes[ iNode ] );
10172 } // loop on volumes of a side
10174 // // complete a set of faces if new nodes in a nodeSet appeared
10175 // // ----------------------------------------------------------
10176 // if ( nodeSetSize != nodeSet->size() ) {
10177 // for ( ; nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
10178 // SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
10179 // while ( fIt->more() ) { // loop on faces sharing a node
10180 // const SMDS_MeshElement* f = fIt->next();
10181 // if ( faceSet->find( f ) == faceSet->end() ) {
10182 // // check if all nodes are in nodeSet and
10183 // // complete setOfFaceNodeSet if they are
10184 // set <const SMDS_MeshNode*> faceNodeSet;
10185 // SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
10186 // bool allInSet = true;
10187 // while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
10188 // const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10189 // if ( nodeSet->find( n ) == nodeSet->end() )
10190 // allInSet = false;
10192 // faceNodeSet.insert( n );
10194 // if ( allInSet ) {
10195 // faceSet->insert( f );
10196 // setOfFaceNodeSet.insert( faceNodeSet );
10202 } // Create temporary faces, if there are volumes given
10205 if ( faceSet1.size() != faceSet2.size() ) {
10206 // delete temporary faces: they are in reverseElements of actual nodes
10207 // SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
10208 // while ( tmpFaceIt->more() )
10209 // aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
10210 // list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
10211 // for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
10212 // aMesh->RemoveElement(*tmpFaceIt);
10213 MESSAGE("Diff nb of faces");
10214 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10217 // ============================================================
10218 // 2. Find nodes to merge:
10219 // bind a node to remove to a node to put instead
10220 // ============================================================
10222 TNodeNodeMap nReplaceMap; // bind a node to remove to a node to put instead
10223 if ( theFirstNode1 != theFirstNode2 )
10224 nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
10225 if ( theSecondNode1 != theSecondNode2 )
10226 nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
10228 LinkID_Gen aLinkID_Gen( GetMeshDS() );
10229 set< long > linkIdSet; // links to process
10230 linkIdSet.insert( aLinkID_Gen.GetLinkID( theFirstNode1, theSecondNode1 ));
10232 typedef pair< const SMDS_MeshNode*, const SMDS_MeshNode* > NLink;
10233 list< NLink > linkList[2];
10234 linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
10235 linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
10236 // loop on links in linkList; find faces by links and append links
10237 // of the found faces to linkList
10238 list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
10239 for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ )
10241 NLink link[] = { *linkIt[0], *linkIt[1] };
10242 long linkID = aLinkID_Gen.GetLinkID( link[0].first, link[0].second );
10243 if ( !linkIdSet.count( linkID ) )
10246 // by links, find faces in the face sets,
10247 // and find indices of link nodes in the found faces;
10248 // in a face set, there is only one or no face sharing a link
10249 // ---------------------------------------------------------------
10251 const SMDS_MeshElement* face[] = { 0, 0 };
10252 vector<const SMDS_MeshNode*> fnodes[2];
10253 int iLinkNode[2][2];
10254 TIDSortedElemSet avoidSet;
10255 for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
10256 const SMDS_MeshNode* n1 = link[iSide].first;
10257 const SMDS_MeshNode* n2 = link[iSide].second;
10258 //cout << "Side " << iSide << " ";
10259 //cout << "L( " << n1->GetID() << ", " << n2->GetID() << " ) " << endl;
10260 // find a face by two link nodes
10261 face[ iSide ] = SMESH_MeshAlgos::FindFaceInSet( n1, n2,
10262 *faceSetPtr[ iSide ], avoidSet,
10263 &iLinkNode[iSide][0],
10264 &iLinkNode[iSide][1] );
10265 if ( face[ iSide ])
10267 //cout << " F " << face[ iSide]->GetID() <<endl;
10268 faceSetPtr[ iSide ]->erase( face[ iSide ]);
10269 // put face nodes to fnodes
10270 if ( face[ iSide ]->IsQuadratic() )
10272 // use interlaced nodes iterator
10273 const SMDS_VtkFace* F = dynamic_cast<const SMDS_VtkFace*>( face[ iSide ]);
10274 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
10275 SMDS_ElemIteratorPtr nIter = F->interlacedNodesElemIterator();
10276 while ( nIter->more() )
10277 fnodes[ iSide ].push_back( cast2Node( nIter->next() ));
10281 fnodes[ iSide ].assign( face[ iSide ]->begin_nodes(),
10282 face[ iSide ]->end_nodes() );
10284 fnodes[ iSide ].push_back( fnodes[ iSide ].front());
10288 // check similarity of elements of the sides
10289 if (aResult == SEW_OK && (( face[0] && !face[1] ) || ( !face[0] && face[1] ))) {
10290 MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
10291 if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
10292 aResult = ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10295 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10297 break; // do not return because it's necessary to remove tmp faces
10300 // set nodes to merge
10301 // -------------------
10303 if ( face[0] && face[1] ) {
10304 const int nbNodes = face[0]->NbNodes();
10305 if ( nbNodes != face[1]->NbNodes() ) {
10306 MESSAGE("Diff nb of face nodes");
10307 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10308 break; // do not return because it s necessary to remove tmp faces
10310 bool reverse[] = { false, false }; // order of nodes in the link
10311 for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
10312 // analyse link orientation in faces
10313 int i1 = iLinkNode[ iSide ][ 0 ];
10314 int i2 = iLinkNode[ iSide ][ 1 ];
10315 reverse[ iSide ] = Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1;
10317 int di1 = reverse[0] ? -1 : +1, i1 = iLinkNode[0][1] + di1;
10318 int di2 = reverse[1] ? -1 : +1, i2 = iLinkNode[1][1] + di2;
10319 for ( int i = nbNodes - 2; i > 0; --i, i1 += di1, i2 += di2 )
10321 nReplaceMap.insert ( make_pair ( fnodes[0][ ( i1 + nbNodes ) % nbNodes ],
10322 fnodes[1][ ( i2 + nbNodes ) % nbNodes ]));
10325 // add other links of the faces to linkList
10326 // -----------------------------------------
10328 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
10329 linkID = aLinkID_Gen.GetLinkID( fnodes[0][iNode], fnodes[0][iNode+1] );
10330 pair< set<long>::iterator, bool > iter_isnew = linkIdSet.insert( linkID );
10331 if ( !iter_isnew.second ) { // already in a set: no need to process
10332 linkIdSet.erase( iter_isnew.first );
10334 else // new in set == encountered for the first time: add
10336 const SMDS_MeshNode* n1 = fnodes[0][ iNode ];
10337 const SMDS_MeshNode* n2 = fnodes[0][ iNode + 1];
10338 linkList[0].push_back ( NLink( n1, n2 ));
10339 linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10344 if ( faceSetPtr[0]->empty() || faceSetPtr[1]->empty() )
10347 } // loop on link lists
10349 if ( aResult == SEW_OK &&
10350 ( //linkIt[0] != linkList[0].end() ||
10351 !faceSetPtr[0]->empty() || !faceSetPtr[1]->empty() )) {
10352 MESSAGE( (linkIt[0] != linkList[0].end()) <<" "<< (faceSetPtr[0]->empty()) <<
10353 " " << (faceSetPtr[1]->empty()));
10354 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10357 // ====================================================================
10358 // 3. Replace nodes in elements of the side 1 and remove replaced nodes
10359 // ====================================================================
10361 // delete temporary faces
10362 // SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
10363 // while ( tmpFaceIt->more() )
10364 // aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
10365 list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
10366 for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
10367 aMesh->RemoveElement(*tmpFaceIt);
10369 if ( aResult != SEW_OK)
10372 list< int > nodeIDsToRemove;
10373 vector< const SMDS_MeshNode*> nodes;
10374 ElemFeatures elemType;
10376 // loop on nodes replacement map
10377 TNodeNodeMap::iterator nReplaceMapIt = nReplaceMap.begin(), nnIt;
10378 for ( ; nReplaceMapIt != nReplaceMap.end(); nReplaceMapIt++ )
10379 if ( (*nReplaceMapIt).first != (*nReplaceMapIt).second )
10381 const SMDS_MeshNode* nToRemove = (*nReplaceMapIt).first;
10382 nodeIDsToRemove.push_back( nToRemove->GetID() );
10383 // loop on elements sharing nToRemove
10384 SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
10385 while ( invElemIt->more() ) {
10386 const SMDS_MeshElement* e = invElemIt->next();
10387 // get a new suite of nodes: make replacement
10388 int nbReplaced = 0, i = 0, nbNodes = e->NbNodes();
10389 nodes.resize( nbNodes );
10390 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
10391 while ( nIt->more() ) {
10392 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nIt->next() );
10393 nnIt = nReplaceMap.find( n );
10394 if ( nnIt != nReplaceMap.end() ) {
10396 n = (*nnIt).second;
10400 // if ( nbReplaced == nbNodes && e->GetType() == SMDSAbs_Face )
10401 // elemIDsToRemove.push_back( e->GetID() );
10405 elemType.Init( e, /*basicOnly=*/false ).SetID( e->GetID() );
10406 aMesh->RemoveElement( e );
10408 if ( SMDS_MeshElement* newElem = this->AddElement( nodes, elemType ))
10410 AddToSameGroups( newElem, e, aMesh );
10411 if ( int aShapeId = e->getshapeId() )
10412 aMesh->SetMeshElementOnShape( newElem, aShapeId );
10418 Remove( nodeIDsToRemove, true );
10423 //================================================================================
10425 * \brief Find corresponding nodes in two sets of faces
10426 * \param theSide1 - first face set
10427 * \param theSide2 - second first face
10428 * \param theFirstNode1 - a boundary node of set 1
10429 * \param theFirstNode2 - a node of set 2 corresponding to theFirstNode1
10430 * \param theSecondNode1 - a boundary node of set 1 linked with theFirstNode1
10431 * \param theSecondNode2 - a node of set 2 corresponding to theSecondNode1
10432 * \param nReplaceMap - output map of corresponding nodes
10433 * \return bool - is a success or not
10435 //================================================================================
10438 //#define DEBUG_MATCHING_NODES
10441 SMESH_MeshEditor::Sew_Error
10442 SMESH_MeshEditor::FindMatchingNodes(set<const SMDS_MeshElement*>& theSide1,
10443 set<const SMDS_MeshElement*>& theSide2,
10444 const SMDS_MeshNode* theFirstNode1,
10445 const SMDS_MeshNode* theFirstNode2,
10446 const SMDS_MeshNode* theSecondNode1,
10447 const SMDS_MeshNode* theSecondNode2,
10448 TNodeNodeMap & nReplaceMap)
10450 set<const SMDS_MeshElement*> * faceSetPtr[] = { &theSide1, &theSide2 };
10452 nReplaceMap.clear();
10453 if ( theFirstNode1 != theFirstNode2 )
10454 nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
10455 if ( theSecondNode1 != theSecondNode2 )
10456 nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
10458 set< SMESH_TLink > linkSet; // set of nodes where order of nodes is ignored
10459 linkSet.insert( SMESH_TLink( theFirstNode1, theSecondNode1 ));
10461 list< NLink > linkList[2];
10462 linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
10463 linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
10465 // loop on links in linkList; find faces by links and append links
10466 // of the found faces to linkList
10467 list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
10468 for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ ) {
10469 NLink link[] = { *linkIt[0], *linkIt[1] };
10470 if ( linkSet.find( link[0] ) == linkSet.end() )
10473 // by links, find faces in the face sets,
10474 // and find indices of link nodes in the found faces;
10475 // in a face set, there is only one or no face sharing a link
10476 // ---------------------------------------------------------------
10478 const SMDS_MeshElement* face[] = { 0, 0 };
10479 list<const SMDS_MeshNode*> notLinkNodes[2];
10480 //bool reverse[] = { false, false }; // order of notLinkNodes
10482 for ( int iSide = 0; iSide < 2; iSide++ ) // loop on 2 sides
10484 const SMDS_MeshNode* n1 = link[iSide].first;
10485 const SMDS_MeshNode* n2 = link[iSide].second;
10486 set<const SMDS_MeshElement*> * faceSet = faceSetPtr[ iSide ];
10487 set< const SMDS_MeshElement* > facesOfNode1;
10488 for ( int iNode = 0; iNode < 2; iNode++ ) // loop on 2 nodes of a link
10490 // during a loop of the first node, we find all faces around n1,
10491 // during a loop of the second node, we find one face sharing both n1 and n2
10492 const SMDS_MeshNode* n = iNode ? n1 : n2; // a node of a link
10493 SMDS_ElemIteratorPtr fIt = n->GetInverseElementIterator(SMDSAbs_Face);
10494 while ( fIt->more() ) { // loop on faces sharing a node
10495 const SMDS_MeshElement* f = fIt->next();
10496 if (faceSet->find( f ) != faceSet->end() && // f is in face set
10497 ! facesOfNode1.insert( f ).second ) // f encounters twice
10499 if ( face[ iSide ] ) {
10500 MESSAGE( "2 faces per link " );
10501 return ( iSide ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10504 faceSet->erase( f );
10506 // get not link nodes
10507 int nbN = f->NbNodes();
10508 if ( f->IsQuadratic() )
10510 nbNodes[ iSide ] = nbN;
10511 list< const SMDS_MeshNode* > & nodes = notLinkNodes[ iSide ];
10512 int i1 = f->GetNodeIndex( n1 );
10513 int i2 = f->GetNodeIndex( n2 );
10514 int iEnd = nbN, iBeg = -1, iDelta = 1;
10515 bool reverse = ( Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1 );
10517 std::swap( iEnd, iBeg ); iDelta = -1;
10522 if ( i == iEnd ) i = iBeg + iDelta;
10523 if ( i == i1 ) break;
10524 nodes.push_back ( f->GetNode( i ) );
10530 // check similarity of elements of the sides
10531 if (( face[0] && !face[1] ) || ( !face[0] && face[1] )) {
10532 MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
10533 if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
10534 return ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10537 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10541 // set nodes to merge
10542 // -------------------
10544 if ( face[0] && face[1] ) {
10545 if ( nbNodes[0] != nbNodes[1] ) {
10546 MESSAGE("Diff nb of face nodes");
10547 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10549 #ifdef DEBUG_MATCHING_NODES
10550 MESSAGE ( " Link 1: " << link[0].first->GetID() <<" "<< link[0].second->GetID()
10551 << " F 1: " << face[0] << "| Link 2: " << link[1].first->GetID() <<" "
10552 << link[1].second->GetID() << " F 2: " << face[1] << " | Bind: " ) ;
10554 int nbN = nbNodes[0];
10556 list<const SMDS_MeshNode*>::iterator n1 = notLinkNodes[0].begin();
10557 list<const SMDS_MeshNode*>::iterator n2 = notLinkNodes[1].begin();
10558 for ( int i = 0 ; i < nbN - 2; ++i ) {
10559 #ifdef DEBUG_MATCHING_NODES
10560 MESSAGE ( (*n1)->GetID() << " to " << (*n2)->GetID() );
10562 nReplaceMap.insert( make_pair( *(n1++), *(n2++) ));
10566 // add other links of the face 1 to linkList
10567 // -----------------------------------------
10569 const SMDS_MeshElement* f0 = face[0];
10570 const SMDS_MeshNode* n1 = f0->GetNode( nbN - 1 );
10571 for ( int i = 0; i < nbN; i++ )
10573 const SMDS_MeshNode* n2 = f0->GetNode( i );
10574 pair< set< SMESH_TLink >::iterator, bool > iter_isnew =
10575 linkSet.insert( SMESH_TLink( n1, n2 ));
10576 if ( !iter_isnew.second ) { // already in a set: no need to process
10577 linkSet.erase( iter_isnew.first );
10579 else // new in set == encountered for the first time: add
10581 #ifdef DEBUG_MATCHING_NODES
10582 MESSAGE ( "Add link 1: " << n1->GetID() << " " << n2->GetID() << " "
10583 << " | link 2: " << nReplaceMap[n1]->GetID() << " " << nReplaceMap[n2]->GetID() << " " );
10585 linkList[0].push_back ( NLink( n1, n2 ));
10586 linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10591 } // loop on link lists
10596 namespace // automatically find theAffectedElems for DoubleNodes()
10598 bool isOut( const SMDS_MeshNode* n, const gp_XYZ& norm, const SMDS_MeshElement* elem );
10600 //--------------------------------------------------------------------------------
10601 // Nodes shared by adjacent FissureBorder's.
10602 // 1 node if FissureBorder separates faces
10603 // 2 nodes if FissureBorder separates volumes
10606 const SMDS_MeshNode* _nodes[2];
10609 SubBorder( const SMDS_MeshNode* n1, const SMDS_MeshNode* n2 = 0 )
10613 _nbNodes = bool( n1 ) + bool( n2 );
10614 if ( _nbNodes == 2 && n1 > n2 )
10615 std::swap( _nodes[0], _nodes[1] );
10617 bool operator<( const SubBorder& other ) const
10619 for ( int i = 0; i < _nbNodes; ++i )
10621 if ( _nodes[i] < other._nodes[i] ) return true;
10622 if ( _nodes[i] > other._nodes[i] ) return false;
10628 //--------------------------------------------------------------------------------
10629 // Map a SubBorder to all FissureBorder it bounds
10630 struct FissureBorder;
10631 typedef std::map< SubBorder, std::vector< FissureBorder* > > TBorderLinks;
10632 typedef TBorderLinks::iterator TMappedSub;
10634 //--------------------------------------------------------------------------------
10636 * \brief Element border (volume facet or face edge) at a fissure
10638 struct FissureBorder
10640 std::vector< const SMDS_MeshNode* > _nodes; // border nodes
10641 const SMDS_MeshElement* _elems[2]; // volume or face adjacent to fissure
10643 std::vector< TMappedSub > _mappedSubs; // Sub() in TBorderLinks map
10644 std::vector< const SMDS_MeshNode* > _sortedNodes; // to compare FissureBorder's
10646 FissureBorder( FissureBorder && from ) // move constructor
10648 std::swap( _nodes, from._nodes );
10649 std::swap( _sortedNodes, from._sortedNodes );
10650 _elems[0] = from._elems[0];
10651 _elems[1] = from._elems[1];
10654 FissureBorder( const SMDS_MeshElement* elemToDuplicate,
10655 std::vector< const SMDS_MeshElement* > & adjElems)
10656 : _nodes( elemToDuplicate->NbCornerNodes() )
10658 for ( size_t i = 0; i < _nodes.size(); ++i )
10659 _nodes[i] = elemToDuplicate->GetNode( i );
10661 SMDSAbs_ElementType type = SMDSAbs_ElementType( elemToDuplicate->GetType() + 1 );
10662 findAdjacent( type, adjElems );
10665 FissureBorder( const SMDS_MeshNode** nodes,
10666 const size_t nbNodes,
10667 const SMDSAbs_ElementType adjElemsType,
10668 std::vector< const SMDS_MeshElement* > & adjElems)
10669 : _nodes( nodes, nodes + nbNodes )
10671 findAdjacent( adjElemsType, adjElems );
10674 void findAdjacent( const SMDSAbs_ElementType adjElemsType,
10675 std::vector< const SMDS_MeshElement* > & adjElems)
10677 _elems[0] = _elems[1] = 0;
10679 if ( SMDS_Mesh::GetElementsByNodes( _nodes, adjElems, adjElemsType ))
10680 for ( size_t i = 0; i < adjElems.size() && i < 2; ++i )
10681 _elems[i] = adjElems[i];
10684 bool operator<( const FissureBorder& other ) const
10686 return GetSortedNodes() < other.GetSortedNodes();
10689 const std::vector< const SMDS_MeshNode* >& GetSortedNodes() const
10691 if ( _sortedNodes.empty() && !_nodes.empty() )
10693 FissureBorder* me = const_cast<FissureBorder*>( this );
10694 me->_sortedNodes = me->_nodes;
10695 std::sort( me->_sortedNodes.begin(), me->_sortedNodes.end() );
10697 return _sortedNodes;
10700 size_t NbSub() const
10702 return _nodes.size();
10705 SubBorder Sub(size_t i) const
10707 return SubBorder( _nodes[i], NbSub() > 2 ? _nodes[ (i+1)%NbSub() ] : 0 );
10710 void AddSelfTo( TBorderLinks& borderLinks )
10712 _mappedSubs.resize( NbSub() );
10713 for ( size_t i = 0; i < NbSub(); ++i )
10715 TBorderLinks::iterator s2b =
10716 borderLinks.insert( std::make_pair( Sub(i), TBorderLinks::mapped_type() )).first;
10717 s2b->second.push_back( this );
10718 _mappedSubs[ i ] = s2b;
10727 const SMDS_MeshElement* GetMarkedElem() const
10729 if ( _nodes.empty() ) return 0; // cleared
10730 if ( _elems[0] && _elems[0]->isMarked() ) return _elems[0];
10731 if ( _elems[1] && _elems[1]->isMarked() ) return _elems[1];
10735 gp_XYZ GetNorm() const // normal to the border
10738 if ( _nodes.size() == 2 )
10740 gp_XYZ avgNorm( 0,0,0 ); // sum of normals of adjacent faces
10741 if ( SMESH_MeshAlgos::FaceNormal( _elems[0], norm ))
10743 if ( SMESH_MeshAlgos::FaceNormal( _elems[1], norm ))
10746 gp_XYZ bordDir( SMESH_NodeXYZ( _nodes[0] ) - SMESH_NodeXYZ( _nodes[1] ));
10747 norm = bordDir ^ avgNorm;
10751 SMESH_NodeXYZ p0( _nodes[0] );
10752 SMESH_NodeXYZ p1( _nodes[1] );
10753 SMESH_NodeXYZ p2( _nodes[2] );
10754 norm = ( p0 - p1 ) ^ ( p2 - p1 );
10756 if ( isOut( _nodes[0], norm, GetMarkedElem() ))
10762 void ChooseSide() // mark an _elem located at positive side of fissure
10764 _elems[0]->setIsMarked( true );
10765 gp_XYZ norm = GetNorm();
10766 double maxX = norm.Coord(1);
10767 if ( Abs( maxX ) < Abs( norm.Coord(2)) ) maxX = norm.Coord(2);
10768 if ( Abs( maxX ) < Abs( norm.Coord(3)) ) maxX = norm.Coord(3);
10771 _elems[0]->setIsMarked( false );
10772 _elems[1]->setIsMarked( true );
10776 }; // struct FissureBorder
10778 //--------------------------------------------------------------------------------
10780 * \brief Classifier of elements at fissure edge
10782 class FissureNormal
10784 std::vector< gp_XYZ > _normals;
10788 void Add( const SMDS_MeshNode* n, const FissureBorder& bord )
10791 _normals.reserve(2);
10792 _normals.push_back( bord.GetNorm() );
10793 if ( _normals.size() == 2 )
10794 _bothIn = !isOut( n, _normals[0], bord.GetMarkedElem() );
10797 bool IsIn( const SMDS_MeshNode* n, const SMDS_MeshElement* elem ) const
10800 switch ( _normals.size() ) {
10803 isIn = !isOut( n, _normals[0], elem );
10808 bool in1 = !isOut( n, _normals[0], elem );
10809 bool in2 = !isOut( n, _normals[1], elem );
10810 isIn = _bothIn ? ( in1 && in2 ) : ( in1 || in2 );
10817 //================================================================================
10819 * \brief Classify an element by a plane passing through a node
10821 //================================================================================
10823 bool isOut( const SMDS_MeshNode* n, const gp_XYZ& norm, const SMDS_MeshElement* elem )
10825 SMESH_NodeXYZ p = n;
10827 for ( int i = 0, nb = elem->NbCornerNodes(); i < nb; ++i )
10829 SMESH_NodeXYZ pi = elem->GetNode( i );
10830 sumDot += norm * ( pi - p );
10832 return sumDot < -1e-100;
10835 //================================================================================
10837 * \brief Find FissureBorder's by nodes to duplicate
10839 //================================================================================
10841 void findFissureBorders( const TIDSortedElemSet& theNodes,
10842 std::vector< FissureBorder > & theFissureBorders )
10844 TIDSortedElemSet::const_iterator nIt = theNodes.begin();
10845 const SMDS_MeshNode* n = dynamic_cast< const SMDS_MeshNode*>( *nIt );
10847 SMDSAbs_ElementType elemType = SMDSAbs_Volume;
10848 if ( n->NbInverseElements( elemType ) == 0 )
10850 elemType = SMDSAbs_Face;
10851 if ( n->NbInverseElements( elemType ) == 0 )
10854 // unmark elements touching the fissure
10855 for ( ; nIt != theNodes.end(); ++nIt )
10856 SMESH_MeshAlgos::MarkElems( cast2Node(*nIt)->GetInverseElementIterator(), false );
10858 // loop on elements touching the fissure to get their borders belonging to the fissure
10859 std::set< FissureBorder > fissureBorders;
10860 std::vector< const SMDS_MeshElement* > adjElems;
10861 std::vector< const SMDS_MeshNode* > nodes;
10862 SMDS_VolumeTool volTool;
10863 for ( nIt = theNodes.begin(); nIt != theNodes.end(); ++nIt )
10865 SMDS_ElemIteratorPtr invIt = cast2Node(*nIt)->GetInverseElementIterator( elemType );
10866 while ( invIt->more() )
10868 const SMDS_MeshElement* eInv = invIt->next();
10869 if ( eInv->isMarked() ) continue;
10870 eInv->setIsMarked( true );
10872 if ( elemType == SMDSAbs_Volume )
10874 volTool.Set( eInv );
10875 int iQuad = eInv->IsQuadratic() ? 2 : 1;
10876 for ( int iF = 0, nbF = volTool.NbFaces(); iF < nbF; ++iF )
10878 const SMDS_MeshNode** nn = volTool.GetFaceNodes( iF );
10879 int nbN = volTool.NbFaceNodes( iF ) / iQuad;
10881 bool allOnFissure = true;
10882 for ( int iN = 0; iN < nbN && allOnFissure; iN += iQuad )
10883 if (( allOnFissure = theNodes.count( nn[ iN ])))
10884 nodes.push_back( nn[ iN ]);
10885 if ( allOnFissure )
10886 fissureBorders.insert( std::move( FissureBorder( &nodes[0], nodes.size(),
10887 elemType, adjElems )));
10890 else // elemType == SMDSAbs_Face
10892 const SMDS_MeshNode* nn[2] = { eInv->GetNode( eInv->NbCornerNodes()-1 ), 0 };
10893 bool onFissure0 = theNodes.count( nn[0] ), onFissure1;
10894 for ( int iN = 0, nbN = eInv->NbCornerNodes(); iN < nbN; ++iN )
10896 nn[1] = eInv->GetNode( iN );
10897 onFissure1 = theNodes.count( nn[1] );
10898 if ( onFissure0 && onFissure1 )
10899 fissureBorders.insert( std::move( FissureBorder( nn, 2, elemType, adjElems )));
10901 onFissure0 = onFissure1;
10907 theFissureBorders.reserve( theFissureBorders.size() + fissureBorders.size());
10908 std::set< FissureBorder >::iterator bord = fissureBorders.begin();
10909 for ( ; bord != fissureBorders.end(); ++bord )
10911 theFissureBorders.push_back( std::move( const_cast<FissureBorder&>( *bord ) ));
10914 } // findFissureBorders()
10916 //================================================================================
10918 * \brief Find elements on one side of a fissure defined by elements or nodes to duplicate
10919 * \param [in] theElemsOrNodes - elements or nodes to duplicate
10920 * \param [in] theNodesNot - nodes not to duplicate
10921 * \param [out] theAffectedElems - the found elements
10923 //================================================================================
10925 void findAffectedElems( const TIDSortedElemSet& theElemsOrNodes,
10926 TIDSortedElemSet& theAffectedElems)
10928 if ( theElemsOrNodes.empty() ) return;
10930 // find FissureBorder's
10932 std::vector< FissureBorder > fissure;
10933 std::vector< const SMDS_MeshElement* > elemsByFacet;
10935 TIDSortedElemSet::const_iterator elIt = theElemsOrNodes.begin();
10936 if ( (*elIt)->GetType() == SMDSAbs_Node )
10938 findFissureBorders( theElemsOrNodes, fissure );
10942 fissure.reserve( theElemsOrNodes.size() );
10943 for ( ; elIt != theElemsOrNodes.end(); ++elIt )
10944 fissure.push_back( std::move( FissureBorder( *elIt, elemsByFacet )));
10946 if ( fissure.empty() )
10949 // fill borderLinks
10951 TBorderLinks borderLinks;
10953 for ( size_t i = 0; i < fissure.size(); ++i )
10955 fissure[i].AddSelfTo( borderLinks );
10958 // get theAffectedElems
10960 // unmark elements having nodes on the fissure, theAffectedElems elements will be marked
10961 for ( size_t i = 0; i < fissure.size(); ++i )
10962 for ( size_t j = 0; j < fissure[i]._nodes.size(); ++j )
10964 SMESH_MeshAlgos::MarkElemNodes( fissure[i]._nodes[j]->GetInverseElementIterator(),
10965 false, /*markElem=*/true );
10968 std::vector<const SMDS_MeshNode *> facetNodes;
10969 std::map< const SMDS_MeshNode*, FissureNormal > fissEdgeNodes2Norm;
10970 boost::container::flat_set< const SMDS_MeshNode* > fissureNodes;
10972 // choose a side of fissure
10973 fissure[0].ChooseSide();
10974 theAffectedElems.insert( fissure[0].GetMarkedElem() );
10976 size_t nbCheckedBorders = 0;
10977 while ( nbCheckedBorders < fissure.size() )
10979 // find a FissureBorder to treat
10980 FissureBorder* bord = 0;
10981 for ( size_t i = 0; i < fissure.size() && !bord; ++i )
10982 if ( fissure[i].GetMarkedElem() )
10983 bord = & fissure[i];
10984 for ( size_t i = 0; i < fissure.size() && !bord; ++i )
10985 if ( fissure[i].NbSub() > 0 && fissure[i]._elems[0] )
10987 bord = & fissure[i];
10988 bord->ChooseSide();
10989 theAffectedElems.insert( bord->GetMarkedElem() );
10991 if ( !bord ) return;
10992 ++nbCheckedBorders;
10994 // treat FissureBorder's linked to bord
10995 fissureNodes.clear();
10996 fissureNodes.insert( bord->_nodes.begin(), bord->_nodes.end() );
10997 for ( size_t i = 0; i < bord->NbSub(); ++i )
10999 TBorderLinks::iterator l2b = bord->_mappedSubs[ i ];
11000 if ( l2b == borderLinks.end() || l2b->second.empty() ) continue;
11001 std::vector< FissureBorder* >& linkedBorders = l2b->second;
11002 const SubBorder& sb = l2b->first;
11003 const SMDS_MeshElement* bordElem = bord->GetMarkedElem();
11005 if ( linkedBorders.size() == 1 ) // fissure edge reached, fill fissEdgeNodes2Norm
11007 for ( int j = 0; j < sb._nbNodes; ++j )
11008 fissEdgeNodes2Norm[ sb._nodes[j] ].Add( sb._nodes[j], *bord );
11012 // add to theAffectedElems elems sharing nodes of a SubBorder and a node of bordElem
11013 // until an elem adjacent to a neighbour FissureBorder is found
11014 facetNodes.clear();
11015 facetNodes.insert( facetNodes.end(), sb._nodes, sb._nodes + sb._nbNodes );
11016 facetNodes.resize( sb._nbNodes + 1 );
11020 // check if bordElem is adjacent to a neighbour FissureBorder
11021 for ( size_t j = 0; j < linkedBorders.size(); ++j )
11023 FissureBorder* bord2 = linkedBorders[j];
11024 if ( bord2 == bord ) continue;
11025 if ( bordElem == bord2->_elems[0] || bordElem == bord2->_elems[1] )
11028 fissureNodes.insert( bord2->_nodes.begin(), bord2->_nodes.end() );
11033 // find the next bordElem
11034 const SMDS_MeshElement* nextBordElem = 0;
11035 for ( int iN = 0, nbN = bordElem->NbCornerNodes(); iN < nbN && !nextBordElem; ++iN )
11037 const SMDS_MeshNode* n = bordElem->GetNode( iN );
11038 if ( fissureNodes.count( n )) continue;
11040 facetNodes[ sb._nbNodes ] = n;
11041 elemsByFacet.clear();
11042 if ( SMDS_Mesh::GetElementsByNodes( facetNodes, elemsByFacet ) > 1 )
11044 for ( size_t iE = 0; iE < elemsByFacet.size(); ++iE )
11045 if ( elemsByFacet[ iE ] != bordElem &&
11046 !elemsByFacet[ iE ]->isMarked() )
11048 theAffectedElems.insert( elemsByFacet[ iE ]);
11049 elemsByFacet[ iE ]->setIsMarked( true );
11050 if ( elemsByFacet[ iE ]->GetType() == bordElem->GetType() )
11051 nextBordElem = elemsByFacet[ iE ];
11055 bordElem = nextBordElem;
11057 } // while ( bordElem )
11059 linkedBorders.clear(); // not to treat this link any more
11061 } // loop on SubBorder's of a FissureBorder
11065 } // loop on FissureBorder's
11068 // add elements sharing only one node of the fissure, except those sharing fissure edge nodes
11070 // mark nodes of theAffectedElems
11071 SMESH_MeshAlgos::MarkElemNodes( theAffectedElems.begin(), theAffectedElems.end(), true );
11073 // unmark nodes of the fissure
11074 elIt = theElemsOrNodes.begin();
11075 if ( (*elIt)->GetType() == SMDSAbs_Node )
11076 SMESH_MeshAlgos::MarkElems( elIt, theElemsOrNodes.end(), false );
11078 SMESH_MeshAlgos::MarkElemNodes( elIt, theElemsOrNodes.end(), false );
11080 std::vector< gp_XYZ > normVec;
11082 // loop on nodes of the fissure, add elements having marked nodes
11083 for ( elIt = theElemsOrNodes.begin(); elIt != theElemsOrNodes.end(); ++elIt )
11085 const SMDS_MeshElement* e = (*elIt);
11086 if ( e->GetType() != SMDSAbs_Node )
11087 e->setIsMarked( true ); // avoid adding a fissure element
11089 for ( int iN = 0, nbN = e->NbCornerNodes(); iN < nbN; ++iN )
11091 const SMDS_MeshNode* n = e->GetNode( iN );
11092 if ( fissEdgeNodes2Norm.count( n ))
11095 SMDS_ElemIteratorPtr invIt = n->GetInverseElementIterator();
11096 while ( invIt->more() )
11098 const SMDS_MeshElement* eInv = invIt->next();
11099 if ( eInv->isMarked() ) continue;
11100 eInv->setIsMarked( true );
11102 SMDS_ElemIteratorPtr nIt = eInv->nodesIterator();
11103 while( nIt->more() )
11104 if ( nIt->next()->isMarked())
11106 theAffectedElems.insert( eInv );
11107 SMESH_MeshAlgos::MarkElems( eInv->nodesIterator(), true );
11108 n->setIsMarked( false );
11115 // add elements on the fissure edge
11116 std::map< const SMDS_MeshNode*, FissureNormal >::iterator n2N;
11117 for ( n2N = fissEdgeNodes2Norm.begin(); n2N != fissEdgeNodes2Norm.end(); ++n2N )
11119 const SMDS_MeshNode* edgeNode = n2N->first;
11120 const FissureNormal & normals = n2N->second;
11122 SMDS_ElemIteratorPtr invIt = edgeNode->GetInverseElementIterator();
11123 while ( invIt->more() )
11125 const SMDS_MeshElement* eInv = invIt->next();
11126 if ( eInv->isMarked() ) continue;
11127 eInv->setIsMarked( true );
11129 // classify eInv using normals
11130 bool toAdd = normals.IsIn( edgeNode, eInv );
11131 if ( toAdd ) // check if all nodes lie on the fissure edge
11133 bool notOnEdge = false;
11134 for ( int iN = 0, nbN = eInv->NbCornerNodes(); iN < nbN && !notOnEdge; ++iN )
11135 notOnEdge = !fissEdgeNodes2Norm.count( eInv->GetNode( iN ));
11140 theAffectedElems.insert( eInv );
11146 } // findAffectedElems()
11149 //================================================================================
11151 * \brief Create elements equal (on same nodes) to given ones
11152 * \param [in] theElements - a set of elems to duplicate. If it is empty, all
11153 * elements of the uppest dimension are duplicated.
11155 //================================================================================
11157 void SMESH_MeshEditor::DoubleElements( const TIDSortedElemSet& theElements )
11159 ClearLastCreated();
11160 SMESHDS_Mesh* mesh = GetMeshDS();
11162 // get an element type and an iterator over elements
11164 SMDSAbs_ElementType type = SMDSAbs_All;
11165 SMDS_ElemIteratorPtr elemIt;
11166 if ( theElements.empty() )
11168 if ( mesh->NbNodes() == 0 )
11170 // get most complex type
11171 SMDSAbs_ElementType types[SMDSAbs_NbElementTypes] = {
11172 SMDSAbs_Volume, SMDSAbs_Face, SMDSAbs_Edge,
11173 SMDSAbs_0DElement, SMDSAbs_Ball, SMDSAbs_Node
11175 for ( int i = 0; i < SMDSAbs_NbElementTypes; ++i )
11176 if ( mesh->GetMeshInfo().NbElements( types[i] ))
11181 elemIt = mesh->elementsIterator( type );
11185 type = (*theElements.begin())->GetType();
11186 elemIt = elemSetIterator( theElements );
11189 // duplicate elements
11191 ElemFeatures elemType;
11193 vector< const SMDS_MeshNode* > nodes;
11194 while ( elemIt->more() )
11196 const SMDS_MeshElement* elem = elemIt->next();
11197 if ( elem->GetType() != type )
11200 elemType.Init( elem, /*basicOnly=*/false );
11201 nodes.assign( elem->begin_nodes(), elem->end_nodes() );
11203 AddElement( nodes, elemType );
11207 //================================================================================
11209 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
11210 \param theElems - the list of elements (edges or faces) to be replicated
11211 The nodes for duplication could be found from these elements
11212 \param theNodesNot - list of nodes to NOT replicate
11213 \param theAffectedElems - the list of elements (cells and edges) to which the
11214 replicated nodes should be associated to.
11215 \return TRUE if operation has been completed successfully, FALSE otherwise
11217 //================================================================================
11219 bool SMESH_MeshEditor::DoubleNodes( const TIDSortedElemSet& theElems,
11220 const TIDSortedElemSet& theNodesNot,
11221 const TIDSortedElemSet& theAffectedElems )
11223 ClearLastCreated();
11225 if ( theElems.size() == 0 )
11228 SMESHDS_Mesh* aMeshDS = GetMeshDS();
11233 TNodeNodeMap anOldNodeToNewNode;
11234 // duplicate elements and nodes
11235 res = doubleNodes( aMeshDS, theElems, theNodesNot, anOldNodeToNewNode, true );
11236 // replce nodes by duplications
11237 res = doubleNodes( aMeshDS, theAffectedElems, theNodesNot, anOldNodeToNewNode, false );
11241 //================================================================================
11243 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
11244 \param theMeshDS - mesh instance
11245 \param theElems - the elements replicated or modified (nodes should be changed)
11246 \param theNodesNot - nodes to NOT replicate
11247 \param theNodeNodeMap - relation of old node to new created node
11248 \param theIsDoubleElem - flag os to replicate element or modify
11249 \return TRUE if operation has been completed successfully, FALSE otherwise
11251 //================================================================================
11253 bool SMESH_MeshEditor::doubleNodes(SMESHDS_Mesh* theMeshDS,
11254 const TIDSortedElemSet& theElems,
11255 const TIDSortedElemSet& theNodesNot,
11256 TNodeNodeMap& theNodeNodeMap,
11257 const bool theIsDoubleElem )
11259 // iterate through element and duplicate them (by nodes duplication)
11261 std::vector<const SMDS_MeshNode*> newNodes;
11262 ElemFeatures elemType;
11264 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
11265 for ( ; elemItr != theElems.end(); ++elemItr )
11267 const SMDS_MeshElement* anElem = *elemItr;
11271 // duplicate nodes to duplicate element
11272 bool isDuplicate = false;
11273 newNodes.resize( anElem->NbNodes() );
11274 SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
11276 while ( anIter->more() )
11278 const SMDS_MeshNode* aCurrNode = static_cast<const SMDS_MeshNode*>( anIter->next() );
11279 const SMDS_MeshNode* aNewNode = aCurrNode;
11280 TNodeNodeMap::iterator n2n = theNodeNodeMap.find( aCurrNode );
11281 if ( n2n != theNodeNodeMap.end() )
11283 aNewNode = n2n->second;
11285 else if ( theIsDoubleElem && !theNodesNot.count( aCurrNode ))
11288 aNewNode = theMeshDS->AddNode( aCurrNode->X(), aCurrNode->Y(), aCurrNode->Z() );
11289 copyPosition( aCurrNode, aNewNode );
11290 theNodeNodeMap[ aCurrNode ] = aNewNode;
11291 myLastCreatedNodes.Append( aNewNode );
11293 isDuplicate |= (aCurrNode != aNewNode);
11294 newNodes[ ind++ ] = aNewNode;
11296 if ( !isDuplicate )
11299 if ( theIsDoubleElem )
11300 AddElement( newNodes, elemType.Init( anElem, /*basicOnly=*/false ));
11302 theMeshDS->ChangeElementNodes( anElem, &newNodes[ 0 ], newNodes.size() );
11309 //================================================================================
11311 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
11312 \param theNodes - identifiers of nodes to be doubled
11313 \param theModifiedElems - identifiers of elements to be updated by the new (doubled)
11314 nodes. If list of element identifiers is empty then nodes are doubled but
11315 they not assigned to elements
11316 \return TRUE if operation has been completed successfully, FALSE otherwise
11318 //================================================================================
11320 bool SMESH_MeshEditor::DoubleNodes( const std::list< int >& theListOfNodes,
11321 const std::list< int >& theListOfModifiedElems )
11323 ClearLastCreated();
11325 if ( theListOfNodes.size() == 0 )
11328 SMESHDS_Mesh* aMeshDS = GetMeshDS();
11332 // iterate through nodes and duplicate them
11334 std::map< const SMDS_MeshNode*, const SMDS_MeshNode* > anOldNodeToNewNode;
11336 std::list< int >::const_iterator aNodeIter;
11337 for ( aNodeIter = theListOfNodes.begin(); aNodeIter != theListOfNodes.end(); ++aNodeIter )
11339 const SMDS_MeshNode* aNode = aMeshDS->FindNode( *aNodeIter );
11345 const SMDS_MeshNode* aNewNode = aMeshDS->AddNode( aNode->X(), aNode->Y(), aNode->Z() );
11348 copyPosition( aNode, aNewNode );
11349 anOldNodeToNewNode[ aNode ] = aNewNode;
11350 myLastCreatedNodes.Append( aNewNode );
11354 // Change nodes of elements
11356 std::vector<const SMDS_MeshNode*> aNodeArr;
11358 std::list< int >::const_iterator anElemIter;
11359 for ( anElemIter = theListOfModifiedElems.begin();
11360 anElemIter != theListOfModifiedElems.end();
11363 const SMDS_MeshElement* anElem = aMeshDS->FindElement( *anElemIter );
11367 aNodeArr.assign( anElem->begin_nodes(), anElem->end_nodes() );
11368 for( size_t i = 0; i < aNodeArr.size(); ++i )
11370 std::map< const SMDS_MeshNode*, const SMDS_MeshNode* >::iterator n2n =
11371 anOldNodeToNewNode.find( aNodeArr[ i ]);
11372 if ( n2n != anOldNodeToNewNode.end() )
11373 aNodeArr[ i ] = n2n->second;
11375 aMeshDS->ChangeElementNodes( anElem, &aNodeArr[ 0 ], aNodeArr.size() );
11383 //================================================================================
11385 \brief Check if element located inside shape
11386 \return TRUE if IN or ON shape, FALSE otherwise
11388 //================================================================================
11390 template<class Classifier>
11391 bool isInside(const SMDS_MeshElement* theElem,
11392 Classifier& theClassifier,
11393 const double theTol)
11395 gp_XYZ centerXYZ (0, 0, 0);
11396 SMDS_ElemIteratorPtr aNodeItr = theElem->nodesIterator();
11397 while ( aNodeItr->more() )
11398 centerXYZ += SMESH_NodeXYZ( aNodeItr->next() );
11400 gp_Pnt aPnt = centerXYZ / theElem->NbNodes();
11401 theClassifier.Perform(aPnt, theTol);
11402 TopAbs_State aState = theClassifier.State();
11403 return (aState == TopAbs_IN || aState == TopAbs_ON );
11406 //================================================================================
11408 * \brief Classifier of the 3D point on the TopoDS_Face
11409 * with interaface suitable for isInside()
11411 //================================================================================
11413 struct _FaceClassifier
11415 Extrema_ExtPS _extremum;
11416 BRepAdaptor_Surface _surface;
11417 TopAbs_State _state;
11419 _FaceClassifier(const TopoDS_Face& face):_extremum(),_surface(face),_state(TopAbs_OUT)
11421 _extremum.Initialize( _surface,
11422 _surface.FirstUParameter(), _surface.LastUParameter(),
11423 _surface.FirstVParameter(), _surface.LastVParameter(),
11424 _surface.Tolerance(), _surface.Tolerance() );
11426 void Perform(const gp_Pnt& aPnt, double theTol)
11429 _state = TopAbs_OUT;
11430 _extremum.Perform(aPnt);
11431 if ( _extremum.IsDone() )
11432 for ( int iSol = 1; iSol <= _extremum.NbExt() && _state == TopAbs_OUT; ++iSol)
11433 _state = ( _extremum.SquareDistance(iSol) <= theTol ? TopAbs_IN : TopAbs_OUT );
11435 TopAbs_State State() const
11442 //================================================================================
11444 \brief Identify the elements that will be affected by node duplication (actual duplication is not performed).
11445 This method is the first step of DoubleNodeElemGroupsInRegion.
11446 \param theElems - list of groups of elements (edges or faces) to be replicated
11447 \param theNodesNot - list of groups of nodes not to replicated
11448 \param theShape - shape to detect affected elements (element which geometric center
11449 located on or inside shape). If the shape is null, detection is done on faces orientations
11450 (select elements with a gravity center on the side given by faces normals).
11451 This mode (null shape) is faster, but works only when theElems are faces, with coherents orientations.
11452 The replicated nodes should be associated to affected elements.
11454 \sa DoubleNodeElemGroupsInRegion()
11456 //================================================================================
11458 bool SMESH_MeshEditor::AffectedElemGroupsInRegion( const TIDSortedElemSet& theElems,
11459 const TIDSortedElemSet& theNodesNot,
11460 const TopoDS_Shape& theShape,
11461 TIDSortedElemSet& theAffectedElems)
11463 if ( theShape.IsNull() )
11465 findAffectedElems( theElems, theAffectedElems );
11469 const double aTol = Precision::Confusion();
11470 auto_ptr< BRepClass3d_SolidClassifier> bsc3d;
11471 auto_ptr<_FaceClassifier> aFaceClassifier;
11472 if ( theShape.ShapeType() == TopAbs_SOLID )
11474 bsc3d.reset( new BRepClass3d_SolidClassifier(theShape));;
11475 bsc3d->PerformInfinitePoint(aTol);
11477 else if (theShape.ShapeType() == TopAbs_FACE )
11479 aFaceClassifier.reset( new _FaceClassifier(TopoDS::Face(theShape)));
11482 // iterates on indicated elements and get elements by back references from their nodes
11483 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
11484 for ( ; elemItr != theElems.end(); ++elemItr )
11486 SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
11487 SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
11488 while ( nodeItr->more() )
11490 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
11491 if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
11493 SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
11494 while ( backElemItr->more() )
11496 const SMDS_MeshElement* curElem = backElemItr->next();
11497 if ( curElem && theElems.find(curElem) == theElems.end() &&
11499 isInside( curElem, *bsc3d, aTol ) :
11500 isInside( curElem, *aFaceClassifier, aTol )))
11501 theAffectedElems.insert( curElem );
11509 //================================================================================
11511 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
11512 \param theElems - group of of elements (edges or faces) to be replicated
11513 \param theNodesNot - group of nodes not to replicate
11514 \param theShape - shape to detect affected elements (element which geometric center
11515 located on or inside shape).
11516 The replicated nodes should be associated to affected elements.
11517 \return TRUE if operation has been completed successfully, FALSE otherwise
11519 //================================================================================
11521 bool SMESH_MeshEditor::DoubleNodesInRegion( const TIDSortedElemSet& theElems,
11522 const TIDSortedElemSet& theNodesNot,
11523 const TopoDS_Shape& theShape )
11525 if ( theShape.IsNull() )
11528 const double aTol = Precision::Confusion();
11529 SMESHUtils::Deleter< BRepClass3d_SolidClassifier> bsc3d;
11530 SMESHUtils::Deleter<_FaceClassifier> aFaceClassifier;
11531 if ( theShape.ShapeType() == TopAbs_SOLID )
11533 bsc3d._obj = new BRepClass3d_SolidClassifier( theShape );
11534 bsc3d->PerformInfinitePoint(aTol);
11536 else if (theShape.ShapeType() == TopAbs_FACE )
11538 aFaceClassifier._obj = new _FaceClassifier( TopoDS::Face( theShape ));
11541 // iterates on indicated elements and get elements by back references from their nodes
11542 TIDSortedElemSet anAffected;
11543 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
11544 for ( ; elemItr != theElems.end(); ++elemItr )
11546 SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
11550 SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
11551 while ( nodeItr->more() )
11553 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
11554 if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
11556 SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
11557 while ( backElemItr->more() )
11559 const SMDS_MeshElement* curElem = backElemItr->next();
11560 if ( curElem && theElems.find(curElem) == theElems.end() &&
11562 isInside( curElem, *bsc3d, aTol ) :
11563 isInside( curElem, *aFaceClassifier, aTol )))
11564 anAffected.insert( curElem );
11568 return DoubleNodes( theElems, theNodesNot, anAffected );
11572 * \brief compute an oriented angle between two planes defined by four points.
11573 * The vector (p0,p1) defines the intersection of the 2 planes (p0,p1,g1) and (p0,p1,g2)
11574 * @param p0 base of the rotation axe
11575 * @param p1 extremity of the rotation axe
11576 * @param g1 belongs to the first plane
11577 * @param g2 belongs to the second plane
11579 double SMESH_MeshEditor::OrientedAngle(const gp_Pnt& p0, const gp_Pnt& p1, const gp_Pnt& g1, const gp_Pnt& g2)
11581 gp_Vec vref(p0, p1);
11584 gp_Vec n1 = vref.Crossed(v1);
11585 gp_Vec n2 = vref.Crossed(v2);
11587 return n2.AngleWithRef(n1, vref);
11589 catch ( Standard_Failure ) {
11591 return Max( v1.Magnitude(), v2.Magnitude() );
11595 * \brief Double nodes on shared faces between groups of volumes and create flat elements on demand.
11596 * The list of groups must contain at least two groups. The groups have to be disjoint: no common element into two different groups.
11597 * The nodes of the internal faces at the boundaries of the groups are doubled. Optionally, the internal faces are replaced by flat elements.
11598 * Triangles are transformed into prisms, and quadrangles into hexahedrons.
11599 * The flat elements are stored in groups of volumes. These groups are named according to the position of the group in the list:
11600 * 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.
11601 * 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.
11602 * All the flat elements are gathered into the group named "joints3D" (or "joints2D" in 2D situation).
11603 * The flat element of the multiple junctions between the simple junction are stored in a group named "jointsMultiples".
11604 * \param theElems - list of groups of volumes, where a group of volume is a set of
11605 * SMDS_MeshElements sorted by Id.
11606 * \param createJointElems - if TRUE, create the elements
11607 * \param onAllBoundaries - if TRUE, the nodes and elements are also created on
11608 * the boundary between \a theDomains and the rest mesh
11609 * \return TRUE if operation has been completed successfully, FALSE otherwise
11611 bool SMESH_MeshEditor::DoubleNodesOnGroupBoundaries( const std::vector<TIDSortedElemSet>& theElems,
11612 bool createJointElems,
11613 bool onAllBoundaries)
11615 // MESSAGE("----------------------------------------------");
11616 // MESSAGE("SMESH_MeshEditor::doubleNodesOnGroupBoundaries");
11617 // MESSAGE("----------------------------------------------");
11619 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11620 meshDS->BuildDownWardConnectivity(true);
11622 SMDS_UnstructuredGrid *grid = meshDS->getGrid();
11624 // --- build the list of faces shared by 2 domains (group of elements), with their domain and volume indexes
11625 // build the list of cells with only a node or an edge on the border, with their domain and volume indexes
11626 // build the list of nodes shared by 2 or more domains, with their domain indexes
11628 std::map<DownIdType, std::map<int,int>, DownIdCompare> faceDomains; // face --> (id domain --> id volume)
11629 std::map<int,int>celldom; // cell vtkId --> domain
11630 std::map<DownIdType, std::map<int,int>, DownIdCompare> cellDomains; // oldNode --> (id domain --> id cell)
11631 std::map<int, std::map<int,int> > nodeDomains; // oldId --> (domainId --> newId)
11632 faceDomains.clear();
11634 cellDomains.clear();
11635 nodeDomains.clear();
11636 std::map<int,int> emptyMap;
11637 std::set<int> emptySet;
11640 //MESSAGE(".. Number of domains :"<<theElems.size());
11642 TIDSortedElemSet theRestDomElems;
11643 const int iRestDom = -1;
11644 const int idom0 = onAllBoundaries ? iRestDom : 0;
11645 const int nbDomains = theElems.size();
11647 // Check if the domains do not share an element
11648 for (int idom = 0; idom < nbDomains-1; idom++)
11650 // MESSAGE("... Check of domain #" << idom);
11651 const TIDSortedElemSet& domain = theElems[idom];
11652 TIDSortedElemSet::const_iterator elemItr = domain.begin();
11653 for (; elemItr != domain.end(); ++elemItr)
11655 const SMDS_MeshElement* anElem = *elemItr;
11656 int idombisdeb = idom + 1 ;
11657 // check if the element belongs to a domain further in the list
11658 for ( size_t idombis = idombisdeb; idombis < theElems.size(); idombis++ )
11660 const TIDSortedElemSet& domainbis = theElems[idombis];
11661 if ( domainbis.count( anElem ))
11663 MESSAGE(".... Domain #" << idom);
11664 MESSAGE(".... Domain #" << idombis);
11665 throw SALOME_Exception("The domains are not disjoint.");
11672 for (int idom = 0; idom < nbDomains; idom++)
11675 // --- build a map (face to duplicate --> volume to modify)
11676 // with all the faces shared by 2 domains (group of elements)
11677 // and corresponding volume of this domain, for each shared face.
11678 // a volume has a face shared by 2 domains if it has a neighbor which is not in his domain.
11680 //MESSAGE("... Neighbors of domain #" << idom);
11681 const TIDSortedElemSet& domain = theElems[idom];
11682 TIDSortedElemSet::const_iterator elemItr = domain.begin();
11683 for (; elemItr != domain.end(); ++elemItr)
11685 const SMDS_MeshElement* anElem = *elemItr;
11688 int vtkId = anElem->getVtkId();
11689 //MESSAGE(" vtkId " << vtkId << " smdsId " << anElem->GetID());
11690 int neighborsVtkIds[NBMAXNEIGHBORS];
11691 int downIds[NBMAXNEIGHBORS];
11692 unsigned char downTypes[NBMAXNEIGHBORS];
11693 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
11694 for (int n = 0; n < nbNeighbors; n++)
11696 int smdsId = meshDS->fromVtkToSmds(neighborsVtkIds[n]);
11697 const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
11698 if (elem && ! domain.count(elem)) // neighbor is in another domain : face is shared
11701 for ( size_t idombis = 0; idombis < theElems.size() && !ok; idombis++) // check if the neighbor belongs to another domain of the list
11703 // MESSAGE("Domain " << idombis);
11704 const TIDSortedElemSet& domainbis = theElems[idombis];
11705 if ( domainbis.count(elem)) ok = true ; // neighbor is in a correct domain : face is kept
11707 if ( ok || onAllBoundaries ) // the characteristics of the face is stored
11709 DownIdType face(downIds[n], downTypes[n]);
11710 if (!faceDomains[face].count(idom))
11712 faceDomains[face][idom] = vtkId; // volume associated to face in this domain
11713 celldom[vtkId] = idom;
11714 //MESSAGE(" cell with a border " << vtkId << " domain " << idom);
11718 theRestDomElems.insert( elem );
11719 faceDomains[face][iRestDom] = neighborsVtkIds[n];
11720 celldom[neighborsVtkIds[n]] = iRestDom;
11728 //MESSAGE("Number of shared faces " << faceDomains.size());
11729 std::map<DownIdType, std::map<int, int>, DownIdCompare>::iterator itface;
11731 // --- explore the shared faces domain by domain,
11732 // explore the nodes of the face and see if they belong to a cell in the domain,
11733 // which has only a node or an edge on the border (not a shared face)
11735 for (int idomain = idom0; idomain < nbDomains; idomain++)
11737 //MESSAGE("Domain " << idomain);
11738 const TIDSortedElemSet& domain = (idomain == iRestDom) ? theRestDomElems : theElems[idomain];
11739 itface = faceDomains.begin();
11740 for (; itface != faceDomains.end(); ++itface)
11742 const std::map<int, int>& domvol = itface->second;
11743 if (!domvol.count(idomain))
11745 DownIdType face = itface->first;
11746 //MESSAGE(" --- face " << face.cellId);
11747 std::set<int> oldNodes;
11749 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11750 std::set<int>::iterator itn = oldNodes.begin();
11751 for (; itn != oldNodes.end(); ++itn)
11754 //MESSAGE(" node " << oldId);
11755 vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
11756 for (int i=0; i<l.ncells; i++)
11758 int vtkId = l.cells[i];
11759 const SMDS_MeshElement* anElem = GetMeshDS()->FindElement(GetMeshDS()->fromVtkToSmds(vtkId));
11760 if (!domain.count(anElem))
11762 int vtkType = grid->GetCellType(vtkId);
11763 int downId = grid->CellIdToDownId(vtkId);
11766 MESSAGE("doubleNodesOnGroupBoundaries: internal algorithm problem");
11767 continue; // not OK at this stage of the algorithm:
11768 //no cells created after BuildDownWardConnectivity
11770 DownIdType aCell(downId, vtkType);
11771 cellDomains[aCell][idomain] = vtkId;
11772 celldom[vtkId] = idomain;
11773 //MESSAGE(" cell " << vtkId << " domain " << idomain);
11779 // --- explore the shared faces domain by domain, to duplicate the nodes in a coherent way
11780 // for each shared face, get the nodes
11781 // for each node, for each domain of the face, create a clone of the node
11783 // --- edges at the intersection of 3 or 4 domains, with the order of domains to build
11784 // junction elements of type prism or hexa. the key is the pair of nodesId (lower first)
11785 // the value is the ordered domain ids. (more than 4 domains not taken into account)
11787 std::map<std::vector<int>, std::vector<int> > edgesMultiDomains; // nodes of edge --> ordered domains
11788 std::map<int, std::vector<int> > mutipleNodes; // nodes multi domains with domain order
11789 std::map<int, std::vector<int> > mutipleNodesToFace; // nodes multi domains with domain order to transform in Face (junction between 3 or more 2D domains)
11791 //MESSAGE(".. Duplication of the nodes");
11792 for (int idomain = idom0; idomain < nbDomains; idomain++)
11794 itface = faceDomains.begin();
11795 for (; itface != faceDomains.end(); ++itface)
11797 const std::map<int, int>& domvol = itface->second;
11798 if (!domvol.count(idomain))
11800 DownIdType face = itface->first;
11801 //MESSAGE(" --- face " << face.cellId);
11802 std::set<int> oldNodes;
11804 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11805 std::set<int>::iterator itn = oldNodes.begin();
11806 for (; itn != oldNodes.end(); ++itn)
11809 if (nodeDomains[oldId].empty())
11811 nodeDomains[oldId][idomain] = oldId; // keep the old node in the first domain
11812 //MESSAGE("-+-+-b oldNode " << oldId << " domain " << idomain);
11814 std::map<int, int>::const_iterator itdom = domvol.begin();
11815 for (; itdom != domvol.end(); ++itdom)
11817 int idom = itdom->first;
11818 //MESSAGE(" domain " << idom);
11819 if (!nodeDomains[oldId].count(idom)) // --- node to clone
11821 if (nodeDomains[oldId].size() >= 2) // a multiple node
11823 vector<int> orderedDoms;
11824 //MESSAGE("multiple node " << oldId);
11825 if (mutipleNodes.count(oldId))
11826 orderedDoms = mutipleNodes[oldId];
11829 map<int,int>::iterator it = nodeDomains[oldId].begin();
11830 for (; it != nodeDomains[oldId].end(); ++it)
11831 orderedDoms.push_back(it->first);
11833 orderedDoms.push_back(idom); // TODO order ==> push_front or back
11834 //stringstream txt;
11835 //for (int i=0; i<orderedDoms.size(); i++)
11836 // txt << orderedDoms[i] << " ";
11837 //MESSAGE("orderedDoms " << txt.str());
11838 mutipleNodes[oldId] = orderedDoms;
11840 double *coords = grid->GetPoint(oldId);
11841 SMDS_MeshNode *newNode = meshDS->AddNode(coords[0], coords[1], coords[2]);
11842 copyPosition( meshDS->FindNodeVtk( oldId ), newNode );
11843 int newId = newNode->getVtkId();
11844 nodeDomains[oldId][idom] = newId; // cloned node for other domains
11845 //MESSAGE("-+-+-c oldNode " << oldId << " domain " << idomain << " newNode " << newId << " domain " << idom << " size=" <<nodeDomains[oldId].size());
11852 //MESSAGE(".. Creation of elements");
11853 for (int idomain = idom0; idomain < nbDomains; idomain++)
11855 itface = faceDomains.begin();
11856 for (; itface != faceDomains.end(); ++itface)
11858 std::map<int, int> domvol = itface->second;
11859 if (!domvol.count(idomain))
11861 DownIdType face = itface->first;
11862 //MESSAGE(" --- face " << face.cellId);
11863 std::set<int> oldNodes;
11865 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11866 int nbMultipleNodes = 0;
11867 std::set<int>::iterator itn = oldNodes.begin();
11868 for (; itn != oldNodes.end(); ++itn)
11871 if (mutipleNodes.count(oldId))
11874 if (nbMultipleNodes > 1) // check if an edge of the face is shared between 3 or more domains
11876 //MESSAGE("multiple Nodes detected on a shared face");
11877 int downId = itface->first.cellId;
11878 unsigned char cellType = itface->first.cellType;
11879 // --- shared edge or shared face ?
11880 if ((cellType == VTK_LINE) || (cellType == VTK_QUADRATIC_EDGE)) // shared edge (between two faces)
11883 int nbNodes = grid->getDownArray(cellType)->getNodes(downId, nodes);
11884 for (int i=0; i< nbNodes; i=i+nbNodes-1) // i=0 , i=nbNodes-1
11885 if (mutipleNodes.count(nodes[i]))
11886 if (!mutipleNodesToFace.count(nodes[i]))
11887 mutipleNodesToFace[nodes[i]] = mutipleNodes[nodes[i]];
11889 else // shared face (between two volumes)
11891 int nbEdges = grid->getDownArray(cellType)->getNumberOfDownCells(downId);
11892 const int* downEdgeIds = grid->getDownArray(cellType)->getDownCells(downId);
11893 const unsigned char* edgeType = grid->getDownArray(cellType)->getDownTypes(downId);
11894 for (int ie =0; ie < nbEdges; ie++)
11897 int nbNodes = grid->getDownArray(edgeType[ie])->getNodes(downEdgeIds[ie], nodes);
11898 if ( mutipleNodes.count(nodes[0]) && mutipleNodes.count( nodes[ nbNodes-1 ]))
11900 vector<int> vn0 = mutipleNodes[nodes[0]];
11901 vector<int> vn1 = mutipleNodes[nodes[nbNodes - 1]];
11903 for ( size_t i0 = 0; i0 < vn0.size(); i0++ )
11904 for ( size_t i1 = 0; i1 < vn1.size(); i1++ )
11905 if ( vn0[i0] == vn1[i1] )
11906 doms.push_back( vn0[ i0 ]);
11907 if ( doms.size() > 2 )
11909 //MESSAGE(" detect edgesMultiDomains " << nodes[0] << " " << nodes[nbNodes - 1]);
11910 double *coords = grid->GetPoint(nodes[0]);
11911 gp_Pnt p0(coords[0], coords[1], coords[2]);
11912 coords = grid->GetPoint(nodes[nbNodes - 1]);
11913 gp_Pnt p1(coords[0], coords[1], coords[2]);
11915 int vtkVolIds[1000]; // an edge can belong to a lot of volumes
11916 map<int, SMDS_VtkVolume*> domvol; // domain --> a volume with the edge
11917 map<int, double> angleDom; // oriented angles between planes defined by edge and volume centers
11918 int nbvol = grid->GetParentVolumes(vtkVolIds, downEdgeIds[ie], edgeType[ie]);
11919 for ( size_t id = 0; id < doms.size(); id++ )
11921 int idom = doms[id];
11922 const TIDSortedElemSet& domain = (idom == iRestDom) ? theRestDomElems : theElems[idom];
11923 for ( int ivol = 0; ivol < nbvol; ivol++ )
11925 int smdsId = meshDS->fromVtkToSmds(vtkVolIds[ivol]);
11926 SMDS_MeshElement* elem = (SMDS_MeshElement*)meshDS->FindElement(smdsId);
11927 if (domain.count(elem))
11929 SMDS_VtkVolume* svol = dynamic_cast<SMDS_VtkVolume*>(elem);
11930 domvol[idom] = svol;
11931 //MESSAGE(" domain " << idom << " volume " << elem->GetID());
11933 vtkIdType npts = 0;
11934 vtkIdType* pts = 0;
11935 grid->GetCellPoints(vtkVolIds[ivol], npts, pts);
11936 SMDS_VtkVolume::gravityCenter(grid, pts, npts, values);
11939 gref.SetXYZ(gp_XYZ(values[0], values[1], values[2]));
11940 angleDom[idom] = 0;
11944 gp_Pnt g(values[0], values[1], values[2]);
11945 angleDom[idom] = OrientedAngle(p0, p1, gref, g); // -pi<angle<+pi
11946 //MESSAGE(" angle=" << angleDom[idom]);
11952 map<double, int> sortedDom; // sort domains by angle
11953 for (map<int, double>::iterator ia = angleDom.begin(); ia != angleDom.end(); ++ia)
11954 sortedDom[ia->second] = ia->first;
11955 vector<int> vnodes;
11957 for (map<double, int>::iterator ib = sortedDom.begin(); ib != sortedDom.end(); ++ib)
11959 vdom.push_back(ib->second);
11960 //MESSAGE(" ordered domain " << ib->second << " angle " << ib->first);
11962 for (int ino = 0; ino < nbNodes; ino++)
11963 vnodes.push_back(nodes[ino]);
11964 edgesMultiDomains[vnodes] = vdom; // nodes vector --> ordered domains
11973 // --- iterate on shared faces (volumes to modify, face to extrude)
11974 // get node id's of the face (id SMDS = id VTK)
11975 // create flat element with old and new nodes if requested
11977 // --- new quad nodes on flat quad elements: oldId --> ((domain1 X domain2) --> newId)
11978 // (domain1 X domain2) = domain1 + MAXINT*domain2
11980 std::map<int, std::map<long,int> > nodeQuadDomains;
11981 std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11983 //MESSAGE(".. Creation of elements: simple junction");
11984 if (createJointElems)
11987 string joints2DName = "joints2D";
11988 mapOfJunctionGroups[joints2DName] = this->myMesh->AddGroup(SMDSAbs_Face, joints2DName.c_str(), idg);
11989 SMESHDS_Group *joints2DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints2DName]->GetGroupDS());
11990 string joints3DName = "joints3D";
11991 mapOfJunctionGroups[joints3DName] = this->myMesh->AddGroup(SMDSAbs_Volume, joints3DName.c_str(), idg);
11992 SMESHDS_Group *joints3DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints3DName]->GetGroupDS());
11994 itface = faceDomains.begin();
11995 for (; itface != faceDomains.end(); ++itface)
11997 DownIdType face = itface->first;
11998 std::set<int> oldNodes;
11999 std::set<int>::iterator itn;
12001 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
12003 std::map<int, int> domvol = itface->second;
12004 std::map<int, int>::iterator itdom = domvol.begin();
12005 int dom1 = itdom->first;
12006 int vtkVolId = itdom->second;
12008 int dom2 = itdom->first;
12009 SMDS_MeshCell *vol = grid->extrudeVolumeFromFace(vtkVolId, dom1, dom2, oldNodes, nodeDomains,
12011 stringstream grpname;
12014 grpname << dom1 << "_" << dom2;
12016 grpname << dom2 << "_" << dom1;
12017 string namegrp = grpname.str();
12018 if (!mapOfJunctionGroups.count(namegrp))
12019 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(vol->GetType(), namegrp.c_str(), idg);
12020 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
12022 sgrp->Add(vol->GetID());
12023 if (vol->GetType() == SMDSAbs_Volume)
12024 joints3DGrp->Add(vol->GetID());
12025 else if (vol->GetType() == SMDSAbs_Face)
12026 joints2DGrp->Add(vol->GetID());
12030 // --- create volumes on multiple domain intersection if requested
12031 // iterate on mutipleNodesToFace
12032 // iterate on edgesMultiDomains
12034 //MESSAGE(".. Creation of elements: multiple junction");
12035 if (createJointElems)
12037 // --- iterate on mutipleNodesToFace
12039 std::map<int, std::vector<int> >::iterator itn = mutipleNodesToFace.begin();
12040 for (; itn != mutipleNodesToFace.end(); ++itn)
12042 int node = itn->first;
12043 vector<int> orderDom = itn->second;
12044 vector<vtkIdType> orderedNodes;
12045 for ( size_t idom = 0; idom < orderDom.size(); idom++ )
12046 orderedNodes.push_back( nodeDomains[ node ][ orderDom[ idom ]]);
12047 SMDS_MeshFace* face = this->GetMeshDS()->AddFaceFromVtkIds(orderedNodes);
12049 stringstream grpname;
12051 grpname << 0 << "_" << 0;
12053 string namegrp = grpname.str();
12054 if (!mapOfJunctionGroups.count(namegrp))
12055 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Face, namegrp.c_str(), idg);
12056 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
12058 sgrp->Add(face->GetID());
12061 // --- iterate on edgesMultiDomains
12063 std::map<std::vector<int>, std::vector<int> >::iterator ite = edgesMultiDomains.begin();
12064 for (; ite != edgesMultiDomains.end(); ++ite)
12066 vector<int> nodes = ite->first;
12067 vector<int> orderDom = ite->second;
12068 vector<vtkIdType> orderedNodes;
12069 if (nodes.size() == 2)
12071 //MESSAGE(" use edgesMultiDomains " << nodes[0] << " " << nodes[1]);
12072 for ( size_t ino = 0; ino < nodes.size(); ino++ )
12073 if ( orderDom.size() == 3 )
12074 for ( size_t idom = 0; idom < orderDom.size(); idom++ )
12075 orderedNodes.push_back( nodeDomains[ nodes[ ino ]][ orderDom[ idom ]]);
12077 for (int idom = orderDom.size()-1; idom >=0; idom--)
12078 orderedNodes.push_back( nodeDomains[ nodes[ ino ]][ orderDom[ idom ]]);
12079 SMDS_MeshVolume* vol = this->GetMeshDS()->AddVolumeFromVtkIds(orderedNodes);
12082 string namegrp = "jointsMultiples";
12083 if (!mapOfJunctionGroups.count(namegrp))
12084 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
12085 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
12087 sgrp->Add(vol->GetID());
12091 //INFOS("Quadratic multiple joints not implemented");
12092 // TODO quadratic nodes
12097 // --- list the explicit faces and edges of the mesh that need to be modified,
12098 // i.e. faces and edges built with one or more duplicated nodes.
12099 // associate these faces or edges to their corresponding domain.
12100 // only the first domain found is kept when a face or edge is shared
12102 std::map<DownIdType, std::map<int,int>, DownIdCompare> faceOrEdgeDom; // cellToModify --> (id domain --> id cell)
12103 std::map<int,int> feDom; // vtk id of cell to modify --> id domain
12104 faceOrEdgeDom.clear();
12107 //MESSAGE(".. Modification of elements");
12108 for (int idomain = idom0; idomain < nbDomains; idomain++)
12110 std::map<int, std::map<int, int> >::const_iterator itnod = nodeDomains.begin();
12111 for (; itnod != nodeDomains.end(); ++itnod)
12113 int oldId = itnod->first;
12114 //MESSAGE(" node " << oldId);
12115 vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
12116 for (int i = 0; i < l.ncells; i++)
12118 int vtkId = l.cells[i];
12119 int vtkType = grid->GetCellType(vtkId);
12120 int downId = grid->CellIdToDownId(vtkId);
12122 continue; // new cells: not to be modified
12123 DownIdType aCell(downId, vtkType);
12124 int volParents[1000];
12125 int nbvol = grid->GetParentVolumes(volParents, vtkId);
12126 for (int j = 0; j < nbvol; j++)
12127 if (celldom.count(volParents[j]) && (celldom[volParents[j]] == idomain))
12128 if (!feDom.count(vtkId))
12130 feDom[vtkId] = idomain;
12131 faceOrEdgeDom[aCell] = emptyMap;
12132 faceOrEdgeDom[aCell][idomain] = vtkId; // affect face or edge to the first domain only
12133 //MESSAGE("affect cell " << this->GetMeshDS()->fromVtkToSmds(vtkId) << " domain " << idomain
12134 // << " type " << vtkType << " downId " << downId);
12140 // --- iterate on shared faces (volumes to modify, face to extrude)
12141 // get node id's of the face
12142 // replace old nodes by new nodes in volumes, and update inverse connectivity
12144 std::map<DownIdType, std::map<int,int>, DownIdCompare>* maps[3] = {&faceDomains, &cellDomains, &faceOrEdgeDom};
12145 for (int m=0; m<3; m++)
12147 std::map<DownIdType, std::map<int,int>, DownIdCompare>* amap = maps[m];
12148 itface = (*amap).begin();
12149 for (; itface != (*amap).end(); ++itface)
12151 DownIdType face = itface->first;
12152 std::set<int> oldNodes;
12153 std::set<int>::iterator itn;
12155 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
12156 //MESSAGE("examine cell, downId " << face.cellId << " type " << int(face.cellType));
12157 std::map<int, int> localClonedNodeIds;
12159 std::map<int, int> domvol = itface->second;
12160 std::map<int, int>::iterator itdom = domvol.begin();
12161 for (; itdom != domvol.end(); ++itdom)
12163 int idom = itdom->first;
12164 int vtkVolId = itdom->second;
12165 //MESSAGE("modify nodes of cell " << this->GetMeshDS()->fromVtkToSmds(vtkVolId) << " domain " << idom);
12166 localClonedNodeIds.clear();
12167 for (itn = oldNodes.begin(); itn != oldNodes.end(); ++itn)
12170 if (nodeDomains[oldId].count(idom))
12172 localClonedNodeIds[oldId] = nodeDomains[oldId][idom];
12173 //MESSAGE(" node " << oldId << " --> " << localClonedNodeIds[oldId]);
12176 meshDS->ModifyCellNodes(vtkVolId, localClonedNodeIds);
12181 // Remove empty groups (issue 0022812)
12182 std::map<std::string, SMESH_Group*>::iterator name_group = mapOfJunctionGroups.begin();
12183 for ( ; name_group != mapOfJunctionGroups.end(); ++name_group )
12185 if ( name_group->second && name_group->second->GetGroupDS()->IsEmpty() )
12186 myMesh->RemoveGroup( name_group->second->GetGroupDS()->GetID() );
12189 meshDS->CleanDownWardConnectivity(); // Mesh has been modified, downward connectivity is no more usable, free memory
12190 grid->DeleteLinks();
12198 * \brief Double nodes on some external faces and create flat elements.
12199 * Flat elements are mainly used by some types of mechanic calculations.
12201 * Each group of the list must be constituted of faces.
12202 * Triangles are transformed in prisms, and quadrangles in hexahedrons.
12203 * @param theElems - list of groups of faces, where a group of faces is a set of
12204 * SMDS_MeshElements sorted by Id.
12205 * @return TRUE if operation has been completed successfully, FALSE otherwise
12207 bool SMESH_MeshEditor::CreateFlatElementsOnFacesGroups(const std::vector<TIDSortedElemSet>& theElems)
12209 // MESSAGE("-------------------------------------------------");
12210 // MESSAGE("SMESH_MeshEditor::CreateFlatElementsOnFacesGroups");
12211 // MESSAGE("-------------------------------------------------");
12213 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
12215 // --- For each group of faces
12216 // duplicate the nodes, create a flat element based on the face
12217 // replace the nodes of the faces by their clones
12219 std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> clonedNodes;
12220 std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> intermediateNodes;
12221 clonedNodes.clear();
12222 intermediateNodes.clear();
12223 std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
12224 mapOfJunctionGroups.clear();
12226 for ( size_t idom = 0; idom < theElems.size(); idom++ )
12228 const TIDSortedElemSet& domain = theElems[idom];
12229 TIDSortedElemSet::const_iterator elemItr = domain.begin();
12230 for ( ; elemItr != domain.end(); ++elemItr )
12232 SMDS_MeshElement* anElem = (SMDS_MeshElement*) *elemItr;
12233 SMDS_MeshFace* aFace = dynamic_cast<SMDS_MeshFace*> (anElem);
12236 // MESSAGE("aFace=" << aFace->GetID());
12237 bool isQuad = aFace->IsQuadratic();
12238 vector<const SMDS_MeshNode*> ln0, ln1, ln2, ln3, ln4;
12240 // --- clone the nodes, create intermediate nodes for non medium nodes of a quad face
12242 SMDS_ElemIteratorPtr nodeIt = aFace->nodesIterator();
12243 while (nodeIt->more())
12245 const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*> (nodeIt->next());
12246 bool isMedium = isQuad && (aFace->IsMediumNode(node));
12248 ln2.push_back(node);
12250 ln0.push_back(node);
12252 const SMDS_MeshNode* clone = 0;
12253 if (!clonedNodes.count(node))
12255 clone = meshDS->AddNode(node->X(), node->Y(), node->Z());
12256 copyPosition( node, clone );
12257 clonedNodes[node] = clone;
12260 clone = clonedNodes[node];
12263 ln3.push_back(clone);
12265 ln1.push_back(clone);
12267 const SMDS_MeshNode* inter = 0;
12268 if (isQuad && (!isMedium))
12270 if (!intermediateNodes.count(node))
12272 inter = meshDS->AddNode(node->X(), node->Y(), node->Z());
12273 copyPosition( node, inter );
12274 intermediateNodes[node] = inter;
12277 inter = intermediateNodes[node];
12278 ln4.push_back(inter);
12282 // --- extrude the face
12284 vector<const SMDS_MeshNode*> ln;
12285 SMDS_MeshVolume* vol = 0;
12286 vtkIdType aType = aFace->GetVtkType();
12290 vol = meshDS->AddVolume(ln0[2], ln0[1], ln0[0], ln1[2], ln1[1], ln1[0]);
12291 // MESSAGE("vol prism " << vol->GetID());
12292 ln.push_back(ln1[0]);
12293 ln.push_back(ln1[1]);
12294 ln.push_back(ln1[2]);
12297 vol = meshDS->AddVolume(ln0[3], ln0[2], ln0[1], ln0[0], ln1[3], ln1[2], ln1[1], ln1[0]);
12298 // MESSAGE("vol hexa " << vol->GetID());
12299 ln.push_back(ln1[0]);
12300 ln.push_back(ln1[1]);
12301 ln.push_back(ln1[2]);
12302 ln.push_back(ln1[3]);
12304 case VTK_QUADRATIC_TRIANGLE:
12305 vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln0[0], ln0[1], ln0[2], ln3[0], ln3[1], ln3[2],
12306 ln2[0], ln2[1], ln2[2], ln4[0], ln4[1], ln4[2]);
12307 // MESSAGE("vol quad prism " << vol->GetID());
12308 ln.push_back(ln1[0]);
12309 ln.push_back(ln1[1]);
12310 ln.push_back(ln1[2]);
12311 ln.push_back(ln3[0]);
12312 ln.push_back(ln3[1]);
12313 ln.push_back(ln3[2]);
12315 case VTK_QUADRATIC_QUAD:
12316 // vol = meshDS->AddVolume(ln0[0], ln0[1], ln0[2], ln0[3], ln1[0], ln1[1], ln1[2], ln1[3],
12317 // ln2[0], ln2[1], ln2[2], ln2[3], ln3[0], ln3[1], ln3[2], ln3[3],
12318 // ln4[0], ln4[1], ln4[2], ln4[3]);
12319 vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln1[3], ln0[0], ln0[1], ln0[2], ln0[3],
12320 ln3[0], ln3[1], ln3[2], ln3[3], ln2[0], ln2[1], ln2[2], ln2[3],
12321 ln4[0], ln4[1], ln4[2], ln4[3]);
12322 // MESSAGE("vol quad hexa " << vol->GetID());
12323 ln.push_back(ln1[0]);
12324 ln.push_back(ln1[1]);
12325 ln.push_back(ln1[2]);
12326 ln.push_back(ln1[3]);
12327 ln.push_back(ln3[0]);
12328 ln.push_back(ln3[1]);
12329 ln.push_back(ln3[2]);
12330 ln.push_back(ln3[3]);
12340 stringstream grpname;
12344 string namegrp = grpname.str();
12345 if (!mapOfJunctionGroups.count(namegrp))
12346 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
12347 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
12349 sgrp->Add(vol->GetID());
12352 // --- modify the face
12354 aFace->ChangeNodes(&ln[0], ln.size());
12361 * \brief identify all the elements around a geom shape, get the faces delimiting the hole
12362 * Build groups of volume to remove, groups of faces to replace on the skin of the object,
12363 * groups of faces to remove inside the object, (idem edges).
12364 * Build ordered list of nodes at the border of each group of faces to replace (to be used to build a geom subshape)
12366 void SMESH_MeshEditor::CreateHoleSkin(double radius,
12367 const TopoDS_Shape& theShape,
12368 SMESH_NodeSearcher* theNodeSearcher,
12369 const char* groupName,
12370 std::vector<double>& nodesCoords,
12371 std::vector<std::vector<int> >& listOfListOfNodes)
12373 // MESSAGE("--------------------------------");
12374 // MESSAGE("SMESH_MeshEditor::CreateHoleSkin");
12375 // MESSAGE("--------------------------------");
12377 // --- zone of volumes to remove is given :
12378 // 1 either by a geom shape (one or more vertices) and a radius,
12379 // 2 either by a group of nodes (representative of the shape)to use with the radius,
12380 // 3 either by a group of nodes where all the elements build on one of this nodes are to remove,
12381 // In the case 2, the group of nodes is an external group of nodes from another mesh,
12382 // In the case 3, the group of nodes is an internal group of the mesh (obtained for instance by a filter),
12383 // defined by it's name.
12385 SMESHDS_GroupBase* groupDS = 0;
12386 SMESH_Mesh::GroupIteratorPtr groupIt = this->myMesh->GetGroups();
12387 while ( groupIt->more() )
12390 SMESH_Group * group = groupIt->next();
12391 if ( !group ) continue;
12392 groupDS = group->GetGroupDS();
12393 if ( !groupDS || groupDS->IsEmpty() ) continue;
12394 std::string grpName = group->GetName();
12395 //MESSAGE("grpName=" << grpName);
12396 if (grpName == groupName)
12402 bool isNodeGroup = false;
12403 bool isNodeCoords = false;
12406 if (groupDS->GetType() != SMDSAbs_Node)
12408 isNodeGroup = true; // a group of nodes exists and it is in this mesh
12411 if (nodesCoords.size() > 0)
12412 isNodeCoords = true; // a list o nodes given by their coordinates
12413 //MESSAGE("---" << isNodeGroup << " " << isNodeCoords);
12415 // --- define groups to build
12417 int idg; // --- group of SMDS volumes
12418 string grpvName = groupName;
12419 grpvName += "_vol";
12420 SMESH_Group *grp = this->myMesh->AddGroup(SMDSAbs_Volume, grpvName.c_str(), idg);
12423 MESSAGE("group not created " << grpvName);
12426 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(grp->GetGroupDS());
12428 int idgs; // --- group of SMDS faces on the skin
12429 string grpsName = groupName;
12430 grpsName += "_skin";
12431 SMESH_Group *grps = this->myMesh->AddGroup(SMDSAbs_Face, grpsName.c_str(), idgs);
12434 MESSAGE("group not created " << grpsName);
12437 SMESHDS_Group *sgrps = dynamic_cast<SMESHDS_Group*>(grps->GetGroupDS());
12439 int idgi; // --- group of SMDS faces internal (several shapes)
12440 string grpiName = groupName;
12441 grpiName += "_internalFaces";
12442 SMESH_Group *grpi = this->myMesh->AddGroup(SMDSAbs_Face, grpiName.c_str(), idgi);
12445 MESSAGE("group not created " << grpiName);
12448 SMESHDS_Group *sgrpi = dynamic_cast<SMESHDS_Group*>(grpi->GetGroupDS());
12450 int idgei; // --- group of SMDS faces internal (several shapes)
12451 string grpeiName = groupName;
12452 grpeiName += "_internalEdges";
12453 SMESH_Group *grpei = this->myMesh->AddGroup(SMDSAbs_Edge, grpeiName.c_str(), idgei);
12456 MESSAGE("group not created " << grpeiName);
12459 SMESHDS_Group *sgrpei = dynamic_cast<SMESHDS_Group*>(grpei->GetGroupDS());
12461 // --- build downward connectivity
12463 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
12464 meshDS->BuildDownWardConnectivity(true);
12465 SMDS_UnstructuredGrid* grid = meshDS->getGrid();
12467 // --- set of volumes detected inside
12469 std::set<int> setOfInsideVol;
12470 std::set<int> setOfVolToCheck;
12472 std::vector<gp_Pnt> gpnts;
12475 if (isNodeGroup) // --- a group of nodes is provided : find all the volumes using one or more of this nodes
12477 //MESSAGE("group of nodes provided");
12478 SMDS_ElemIteratorPtr elemIt = groupDS->GetElements();
12479 while ( elemIt->more() )
12481 const SMDS_MeshElement* elem = elemIt->next();
12484 const SMDS_MeshNode* node = dynamic_cast<const SMDS_MeshNode*>(elem);
12487 SMDS_MeshElement* vol = 0;
12488 SMDS_ElemIteratorPtr volItr = node->GetInverseElementIterator(SMDSAbs_Volume);
12489 while (volItr->more())
12491 vol = (SMDS_MeshElement*)volItr->next();
12492 setOfInsideVol.insert(vol->getVtkId());
12493 sgrp->Add(vol->GetID());
12497 else if (isNodeCoords)
12499 //MESSAGE("list of nodes coordinates provided");
12502 while ( i < nodesCoords.size()-2 )
12504 double x = nodesCoords[i++];
12505 double y = nodesCoords[i++];
12506 double z = nodesCoords[i++];
12507 gp_Pnt p = gp_Pnt(x, y ,z);
12508 gpnts.push_back(p);
12509 //MESSAGE("TopoDS_Vertex " << k << " " << p.X() << " " << p.Y() << " " << p.Z());
12513 else // --- no group, no coordinates : use the vertices of the geom shape provided, and radius
12515 //MESSAGE("no group of nodes provided, using vertices from geom shape, and radius");
12516 TopTools_IndexedMapOfShape vertexMap;
12517 TopExp::MapShapes( theShape, TopAbs_VERTEX, vertexMap );
12518 gp_Pnt p = gp_Pnt(0,0,0);
12519 if (vertexMap.Extent() < 1)
12522 for ( int i = 1; i <= vertexMap.Extent(); ++i )
12524 const TopoDS_Vertex& vertex = TopoDS::Vertex( vertexMap( i ));
12525 p = BRep_Tool::Pnt(vertex);
12526 gpnts.push_back(p);
12527 //MESSAGE("TopoDS_Vertex " << i << " " << p.X() << " " << p.Y() << " " << p.Z());
12531 if (gpnts.size() > 0)
12533 const SMDS_MeshNode* startNode = theNodeSearcher->FindClosestTo(gpnts[0]);
12534 //MESSAGE("startNode->nodeId " << nodeId);
12536 double radius2 = radius*radius;
12537 //MESSAGE("radius2 " << radius2);
12539 // --- volumes on start node
12541 setOfVolToCheck.clear();
12542 SMDS_MeshElement* startVol = 0;
12543 SMDS_ElemIteratorPtr volItr = startNode->GetInverseElementIterator(SMDSAbs_Volume);
12544 while (volItr->more())
12546 startVol = (SMDS_MeshElement*)volItr->next();
12547 setOfVolToCheck.insert(startVol->getVtkId());
12549 if (setOfVolToCheck.empty())
12551 MESSAGE("No volumes found");
12555 // --- starting with central volumes then their neighbors, check if they are inside
12556 // or outside the domain, until no more new neighbor volume is inside.
12557 // Fill the group of inside volumes
12559 std::map<int, double> mapOfNodeDistance2;
12560 mapOfNodeDistance2.clear();
12561 std::set<int> setOfOutsideVol;
12562 while (!setOfVolToCheck.empty())
12564 std::set<int>::iterator it = setOfVolToCheck.begin();
12566 //MESSAGE("volume to check, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12567 bool volInside = false;
12568 vtkIdType npts = 0;
12569 vtkIdType* pts = 0;
12570 grid->GetCellPoints(vtkId, npts, pts);
12571 for (int i=0; i<npts; i++)
12573 double distance2 = 0;
12574 if (mapOfNodeDistance2.count(pts[i]))
12576 distance2 = mapOfNodeDistance2[pts[i]];
12577 //MESSAGE("point " << pts[i] << " distance2 " << distance2);
12581 double *coords = grid->GetPoint(pts[i]);
12582 gp_Pnt aPoint = gp_Pnt(coords[0], coords[1], coords[2]);
12584 for ( size_t j = 0; j < gpnts.size(); j++ )
12586 double d2 = aPoint.SquareDistance( gpnts[ j ]);
12587 if (d2 < distance2)
12590 if (distance2 < radius2)
12594 mapOfNodeDistance2[pts[i]] = distance2;
12595 //MESSAGE(" point " << pts[i] << " distance2 " << distance2 << " coords " << coords[0] << " " << coords[1] << " " << coords[2]);
12597 if (distance2 < radius2)
12599 volInside = true; // one or more nodes inside the domain
12600 sgrp->Add(meshDS->fromVtkToSmds(vtkId));
12606 setOfInsideVol.insert(vtkId);
12607 //MESSAGE(" volume inside, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12608 int neighborsVtkIds[NBMAXNEIGHBORS];
12609 int downIds[NBMAXNEIGHBORS];
12610 unsigned char downTypes[NBMAXNEIGHBORS];
12611 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12612 for (int n = 0; n < nbNeighbors; n++)
12613 if (!setOfInsideVol.count(neighborsVtkIds[n]) ||setOfOutsideVol.count(neighborsVtkIds[n]))
12614 setOfVolToCheck.insert(neighborsVtkIds[n]);
12618 setOfOutsideVol.insert(vtkId);
12619 //MESSAGE(" volume outside, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12621 setOfVolToCheck.erase(vtkId);
12625 // --- for outside hexahedrons, check if they have more than one neighbor volume inside
12626 // If yes, add the volume to the inside set
12628 bool addedInside = true;
12629 std::set<int> setOfVolToReCheck;
12630 while (addedInside)
12632 //MESSAGE(" --------------------------- re check");
12633 addedInside = false;
12634 std::set<int>::iterator itv = setOfInsideVol.begin();
12635 for (; itv != setOfInsideVol.end(); ++itv)
12638 int neighborsVtkIds[NBMAXNEIGHBORS];
12639 int downIds[NBMAXNEIGHBORS];
12640 unsigned char downTypes[NBMAXNEIGHBORS];
12641 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12642 for (int n = 0; n < nbNeighbors; n++)
12643 if (!setOfInsideVol.count(neighborsVtkIds[n]))
12644 setOfVolToReCheck.insert(neighborsVtkIds[n]);
12646 setOfVolToCheck = setOfVolToReCheck;
12647 setOfVolToReCheck.clear();
12648 while (!setOfVolToCheck.empty())
12650 std::set<int>::iterator it = setOfVolToCheck.begin();
12652 if (grid->GetCellType(vtkId) == VTK_HEXAHEDRON)
12654 //MESSAGE("volume to recheck, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12655 int countInside = 0;
12656 int neighborsVtkIds[NBMAXNEIGHBORS];
12657 int downIds[NBMAXNEIGHBORS];
12658 unsigned char downTypes[NBMAXNEIGHBORS];
12659 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12660 for (int n = 0; n < nbNeighbors; n++)
12661 if (setOfInsideVol.count(neighborsVtkIds[n]))
12663 //MESSAGE("countInside " << countInside);
12664 if (countInside > 1)
12666 //MESSAGE(" volume inside, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12667 setOfInsideVol.insert(vtkId);
12668 sgrp->Add(meshDS->fromVtkToSmds(vtkId));
12669 addedInside = true;
12672 setOfVolToReCheck.insert(vtkId);
12674 setOfVolToCheck.erase(vtkId);
12678 // --- map of Downward faces at the boundary, inside the global volume
12679 // map of Downward faces on the skin of the global volume (equivalent to SMDS faces on the skin)
12680 // fill group of SMDS faces inside the volume (when several volume shapes)
12681 // fill group of SMDS faces on the skin of the global volume (if skin)
12683 std::map<DownIdType, int, DownIdCompare> boundaryFaces; // boundary faces inside the volume --> corresponding cell
12684 std::map<DownIdType, int, DownIdCompare> skinFaces; // faces on the skin of the global volume --> corresponding cell
12685 std::set<int>::iterator it = setOfInsideVol.begin();
12686 for (; it != setOfInsideVol.end(); ++it)
12689 //MESSAGE(" vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12690 int neighborsVtkIds[NBMAXNEIGHBORS];
12691 int downIds[NBMAXNEIGHBORS];
12692 unsigned char downTypes[NBMAXNEIGHBORS];
12693 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId, true);
12694 for (int n = 0; n < nbNeighbors; n++)
12696 int neighborDim = SMDS_Downward::getCellDimension(grid->GetCellType(neighborsVtkIds[n]));
12697 if (neighborDim == 3)
12699 if (! setOfInsideVol.count(neighborsVtkIds[n])) // neighbor volume is not inside : face is boundary
12701 DownIdType face(downIds[n], downTypes[n]);
12702 boundaryFaces[face] = vtkId;
12704 // if the face between to volumes is in the mesh, get it (internal face between shapes)
12705 int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12706 if (vtkFaceId >= 0)
12708 sgrpi->Add(meshDS->fromVtkToSmds(vtkFaceId));
12709 // find also the smds edges on this face
12710 int nbEdges = grid->getDownArray(downTypes[n])->getNumberOfDownCells(downIds[n]);
12711 const int* dEdges = grid->getDownArray(downTypes[n])->getDownCells(downIds[n]);
12712 const unsigned char* dTypes = grid->getDownArray(downTypes[n])->getDownTypes(downIds[n]);
12713 for (int i = 0; i < nbEdges; i++)
12715 int vtkEdgeId = grid->getDownArray(dTypes[i])->getVtkCellId(dEdges[i]);
12716 if (vtkEdgeId >= 0)
12717 sgrpei->Add(meshDS->fromVtkToSmds(vtkEdgeId));
12721 else if (neighborDim == 2) // skin of the volume
12723 DownIdType face(downIds[n], downTypes[n]);
12724 skinFaces[face] = vtkId;
12725 int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12726 if (vtkFaceId >= 0)
12727 sgrps->Add(meshDS->fromVtkToSmds(vtkFaceId));
12732 // --- identify the edges constituting the wire of each subshape on the skin
12733 // define polylines with the nodes of edges, equivalent to wires
12734 // project polylines on subshapes, and partition, to get geom faces
12736 std::map<int, std::set<int> > shapeIdToVtkIdSet; // shapeId --> set of vtkId on skin
12737 std::set<int> emptySet;
12739 std::set<int> shapeIds;
12741 SMDS_ElemIteratorPtr itelem = sgrps->GetElements();
12742 while (itelem->more())
12744 const SMDS_MeshElement *elem = itelem->next();
12745 int shapeId = elem->getshapeId();
12746 int vtkId = elem->getVtkId();
12747 if (!shapeIdToVtkIdSet.count(shapeId))
12749 shapeIdToVtkIdSet[shapeId] = emptySet;
12750 shapeIds.insert(shapeId);
12752 shapeIdToVtkIdSet[shapeId].insert(vtkId);
12755 std::map<int, std::set<DownIdType, DownIdCompare> > shapeIdToEdges; // shapeId --> set of downward edges
12756 std::set<DownIdType, DownIdCompare> emptyEdges;
12757 emptyEdges.clear();
12759 std::map<int, std::set<int> >::iterator itShape = shapeIdToVtkIdSet.begin();
12760 for (; itShape != shapeIdToVtkIdSet.end(); ++itShape)
12762 int shapeId = itShape->first;
12763 //MESSAGE(" --- Shape ID --- "<< shapeId);
12764 shapeIdToEdges[shapeId] = emptyEdges;
12766 std::vector<int> nodesEdges;
12768 std::set<int>::iterator its = itShape->second.begin();
12769 for (; its != itShape->second.end(); ++its)
12772 //MESSAGE(" " << vtkId);
12773 int neighborsVtkIds[NBMAXNEIGHBORS];
12774 int downIds[NBMAXNEIGHBORS];
12775 unsigned char downTypes[NBMAXNEIGHBORS];
12776 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12777 for (int n = 0; n < nbNeighbors; n++)
12779 if (neighborsVtkIds[n]<0) // only smds faces are considered as neighbors here
12781 int smdsId = meshDS->fromVtkToSmds(neighborsVtkIds[n]);
12782 const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
12783 if ( shapeIds.count(elem->getshapeId()) && !sgrps->Contains(elem)) // edge : neighbor in the set of shape, not in the group
12785 DownIdType edge(downIds[n], downTypes[n]);
12786 if (!shapeIdToEdges[shapeId].count(edge))
12788 shapeIdToEdges[shapeId].insert(edge);
12790 int nbNodes = grid->getDownArray(downTypes[n])->getNodes(downIds[n],vtkNodeId);
12791 nodesEdges.push_back(vtkNodeId[0]);
12792 nodesEdges.push_back(vtkNodeId[nbNodes-1]);
12793 //MESSAGE(" --- nodes " << vtkNodeId[0]+1 << " " << vtkNodeId[nbNodes-1]+1);
12799 std::list<int> order;
12801 if (nodesEdges.size() > 0)
12803 order.push_back(nodesEdges[0]); //MESSAGE(" --- back " << order.back()+1); // SMDS id = VTK id + 1;
12804 nodesEdges[0] = -1;
12805 order.push_back(nodesEdges[1]); //MESSAGE(" --- back " << order.back()+1);
12806 nodesEdges[1] = -1; // do not reuse this edge
12810 int nodeTofind = order.back(); // try first to push back
12812 for ( i = 0; i < (int)nodesEdges.size(); i++ )
12813 if (nodesEdges[i] == nodeTofind)
12815 if ( i == (int) nodesEdges.size() )
12816 found = false; // no follower found on back
12819 if (i%2) // odd ==> use the previous one
12820 if (nodesEdges[i-1] < 0)
12824 order.push_back(nodesEdges[i-1]); //MESSAGE(" --- back " << order.back()+1);
12825 nodesEdges[i-1] = -1;
12827 else // even ==> use the next one
12828 if (nodesEdges[i+1] < 0)
12832 order.push_back(nodesEdges[i+1]); //MESSAGE(" --- back " << order.back()+1);
12833 nodesEdges[i+1] = -1;
12838 // try to push front
12840 nodeTofind = order.front(); // try to push front
12841 for ( i = 0; i < (int)nodesEdges.size(); i++ )
12842 if ( nodesEdges[i] == nodeTofind )
12844 if ( i == (int)nodesEdges.size() )
12846 found = false; // no predecessor found on front
12849 if (i%2) // odd ==> use the previous one
12850 if (nodesEdges[i-1] < 0)
12854 order.push_front(nodesEdges[i-1]); //MESSAGE(" --- front " << order.front()+1);
12855 nodesEdges[i-1] = -1;
12857 else // even ==> use the next one
12858 if (nodesEdges[i+1] < 0)
12862 order.push_front(nodesEdges[i+1]); //MESSAGE(" --- front " << order.front()+1);
12863 nodesEdges[i+1] = -1;
12869 std::vector<int> nodes;
12870 nodes.push_back(shapeId);
12871 std::list<int>::iterator itl = order.begin();
12872 for (; itl != order.end(); itl++)
12874 nodes.push_back((*itl) + 1); // SMDS id = VTK id + 1;
12875 //MESSAGE(" ordered node " << nodes[nodes.size()-1]);
12877 listOfListOfNodes.push_back(nodes);
12880 // partition geom faces with blocFissure
12881 // mesh blocFissure and geom faces of the skin (external wires given, triangle algo to choose)
12882 // mesh volume around blocFissure (skin triangles and quadrangle given, tetra algo to choose)
12888 //================================================================================
12890 * \brief Generates skin mesh (containing 2D cells) from 3D mesh
12891 * The created 2D mesh elements based on nodes of free faces of boundary volumes
12892 * \return TRUE if operation has been completed successfully, FALSE otherwise
12894 //================================================================================
12896 bool SMESH_MeshEditor::Make2DMeshFrom3D()
12898 // iterates on volume elements and detect all free faces on them
12899 SMESHDS_Mesh* aMesh = GetMeshDS();
12903 ElemFeatures faceType( SMDSAbs_Face );
12904 int nbFree = 0, nbExisted = 0, nbCreated = 0;
12905 SMDS_VolumeIteratorPtr vIt = aMesh->volumesIterator();
12908 const SMDS_MeshVolume* volume = vIt->next();
12909 SMDS_VolumeTool vTool( volume, /*ignoreCentralNodes=*/false );
12910 vTool.SetExternalNormal();
12911 const int iQuad = volume->IsQuadratic();
12912 faceType.SetQuad( iQuad );
12913 for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12915 if (!vTool.IsFreeFace(iface))
12918 vector<const SMDS_MeshNode *> nodes;
12919 int nbFaceNodes = vTool.NbFaceNodes(iface);
12920 const SMDS_MeshNode** faceNodes = vTool.GetFaceNodes(iface);
12922 for ( ; inode < nbFaceNodes; inode += iQuad+1)
12923 nodes.push_back(faceNodes[inode]);
12925 if (iQuad) // add medium nodes
12927 for ( inode = 1; inode < nbFaceNodes; inode += 2)
12928 nodes.push_back(faceNodes[inode]);
12929 if ( nbFaceNodes == 9 ) // bi-quadratic quad
12930 nodes.push_back(faceNodes[8]);
12932 // add new face based on volume nodes
12933 if (aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false) )
12935 nbExisted++; // face already exsist
12939 AddElement( nodes, faceType.SetPoly( nbFaceNodes/(iQuad+1) > 4 ));
12944 return ( nbFree == ( nbExisted + nbCreated ));
12949 inline const SMDS_MeshNode* getNodeWithSameID(SMESHDS_Mesh* mesh, const SMDS_MeshNode* node)
12951 if ( const SMDS_MeshNode* n = mesh->FindNode( node->GetID() ))
12953 return mesh->AddNodeWithID( node->X(),node->Y(),node->Z(), node->GetID() );
12956 //================================================================================
12958 * \brief Creates missing boundary elements
12959 * \param elements - elements whose boundary is to be checked
12960 * \param dimension - defines type of boundary elements to create
12961 * \param group - a group to store created boundary elements in
12962 * \param targetMesh - a mesh to store created boundary elements in
12963 * \param toCopyElements - if true, the checked elements will be copied into the targetMesh
12964 * \param toCopyExistingBoundary - if true, not only new but also pre-existing
12965 * boundary elements will be copied into the targetMesh
12966 * \param toAddExistingBondary - if true, not only new but also pre-existing
12967 * boundary elements will be added into the new group
12968 * \param aroundElements - if true, elements will be created on boundary of given
12969 * elements else, on boundary of the whole mesh.
12970 * \return nb of added boundary elements
12972 //================================================================================
12974 int SMESH_MeshEditor::MakeBoundaryMesh(const TIDSortedElemSet& elements,
12975 Bnd_Dimension dimension,
12976 SMESH_Group* group/*=0*/,
12977 SMESH_Mesh* targetMesh/*=0*/,
12978 bool toCopyElements/*=false*/,
12979 bool toCopyExistingBoundary/*=false*/,
12980 bool toAddExistingBondary/*= false*/,
12981 bool aroundElements/*= false*/)
12983 SMDSAbs_ElementType missType = (dimension == BND_2DFROM3D) ? SMDSAbs_Face : SMDSAbs_Edge;
12984 SMDSAbs_ElementType elemType = (dimension == BND_1DFROM2D) ? SMDSAbs_Face : SMDSAbs_Volume;
12985 // hope that all elements are of the same type, do not check them all
12986 if ( !elements.empty() && (*elements.begin())->GetType() != elemType )
12987 throw SALOME_Exception(LOCALIZED("wrong element type"));
12990 toCopyElements = toCopyExistingBoundary = false;
12992 SMESH_MeshEditor tgtEditor( targetMesh ? targetMesh : myMesh );
12993 SMESHDS_Mesh* aMesh = GetMeshDS(), *tgtMeshDS = tgtEditor.GetMeshDS();
12994 int nbAddedBnd = 0;
12996 // editor adding present bnd elements and optionally holding elements to add to the group
12997 SMESH_MeshEditor* presentEditor;
12998 SMESH_MeshEditor tgtEditor2( tgtEditor.GetMesh() );
12999 presentEditor = toAddExistingBondary ? &tgtEditor : &tgtEditor2;
13001 SMESH_MesherHelper helper( *myMesh );
13002 const TopAbs_ShapeEnum missShapeType = ( missType==SMDSAbs_Face ? TopAbs_FACE : TopAbs_EDGE );
13003 SMDS_VolumeTool vTool;
13004 TIDSortedElemSet avoidSet;
13005 const TIDSortedElemSet emptySet, *elemSet = aroundElements ? &elements : &emptySet;
13008 typedef vector<const SMDS_MeshNode*> TConnectivity;
13009 TConnectivity tgtNodes;
13010 ElemFeatures elemKind( missType ), elemToCopy;
13012 vector<const SMDS_MeshElement*> presentBndElems;
13013 vector<TConnectivity> missingBndElems;
13014 vector<int> freeFacets;
13015 TConnectivity nodes, elemNodes;
13017 SMDS_ElemIteratorPtr eIt;
13018 if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
13019 else eIt = elemSetIterator( elements );
13021 while (eIt->more())
13023 const SMDS_MeshElement* elem = eIt->next();
13024 const int iQuad = elem->IsQuadratic();
13025 elemKind.SetQuad( iQuad );
13027 // ------------------------------------------------------------------------------------
13028 // 1. For an elem, get present bnd elements and connectivities of missing bnd elements
13029 // ------------------------------------------------------------------------------------
13030 presentBndElems.clear();
13031 missingBndElems.clear();
13032 freeFacets.clear(); nodes.clear(); elemNodes.clear();
13033 if ( vTool.Set(elem, /*ignoreCentralNodes=*/true) ) // elem is a volume --------------
13035 const SMDS_MeshElement* otherVol = 0;
13036 for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
13038 if ( !vTool.IsFreeFace(iface, &otherVol) &&
13039 ( !aroundElements || elements.count( otherVol )))
13041 freeFacets.push_back( iface );
13043 if ( missType == SMDSAbs_Face )
13044 vTool.SetExternalNormal();
13045 for ( size_t i = 0; i < freeFacets.size(); ++i )
13047 int iface = freeFacets[i];
13048 const SMDS_MeshNode** nn = vTool.GetFaceNodes(iface);
13049 const size_t nbFaceNodes = vTool.NbFaceNodes (iface);
13050 if ( missType == SMDSAbs_Edge ) // boundary edges
13052 nodes.resize( 2+iQuad );
13053 for ( size_t i = 0; i < nbFaceNodes; i += 1+iQuad )
13055 for ( size_t j = 0; j < nodes.size(); ++j )
13056 nodes[ j ] = nn[ i+j ];
13057 if ( const SMDS_MeshElement* edge =
13058 aMesh->FindElement( nodes, SMDSAbs_Edge, /*noMedium=*/false ))
13059 presentBndElems.push_back( edge );
13061 missingBndElems.push_back( nodes );
13064 else // boundary face
13067 for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
13068 nodes.push_back( nn[inode] ); // add corner nodes
13070 for ( inode = 1; inode < nbFaceNodes; inode += 2)
13071 nodes.push_back( nn[inode] ); // add medium nodes
13072 int iCenter = vTool.GetCenterNodeIndex(iface); // for HEX27
13074 nodes.push_back( vTool.GetNodes()[ iCenter ] );
13076 if (const SMDS_MeshElement * f = aMesh->FindElement( nodes,
13077 SMDSAbs_Face, /*noMedium=*/false ))
13078 presentBndElems.push_back( f );
13080 missingBndElems.push_back( nodes );
13082 if ( targetMesh != myMesh )
13084 // add 1D elements on face boundary to be added to a new mesh
13085 const SMDS_MeshElement* edge;
13086 for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
13089 edge = aMesh->FindEdge( nn[inode], nn[inode+1], nn[inode+2]);
13091 edge = aMesh->FindEdge( nn[inode], nn[inode+1]);
13092 if ( edge && avoidSet.insert( edge ).second )
13093 presentBndElems.push_back( edge );
13099 else if ( elem->GetType() == SMDSAbs_Face ) // elem is a face ------------------------
13101 avoidSet.clear(), avoidSet.insert( elem );
13102 elemNodes.assign( SMDS_MeshElement::iterator( elem->interlacedNodesElemIterator() ),
13103 SMDS_MeshElement::iterator() );
13104 elemNodes.push_back( elemNodes[0] );
13105 nodes.resize( 2 + iQuad );
13106 const int nbLinks = elem->NbCornerNodes();
13107 for ( int i = 0, iN = 0; i < nbLinks; i++, iN += 1+iQuad )
13109 nodes[0] = elemNodes[iN];
13110 nodes[1] = elemNodes[iN+1+iQuad];
13111 if ( SMESH_MeshAlgos::FindFaceInSet( nodes[0], nodes[1], *elemSet, avoidSet))
13112 continue; // not free link
13114 if ( iQuad ) nodes[2] = elemNodes[iN+1];
13115 if ( const SMDS_MeshElement* edge =
13116 aMesh->FindElement(nodes,SMDSAbs_Edge,/*noMedium=*/false))
13117 presentBndElems.push_back( edge );
13119 missingBndElems.push_back( nodes );
13123 // ---------------------------------
13124 // 2. Add missing boundary elements
13125 // ---------------------------------
13126 if ( targetMesh != myMesh )
13127 // instead of making a map of nodes in this mesh and targetMesh,
13128 // we create nodes with same IDs.
13129 for ( size_t i = 0; i < missingBndElems.size(); ++i )
13131 TConnectivity& srcNodes = missingBndElems[i];
13132 tgtNodes.resize( srcNodes.size() );
13133 for ( inode = 0; inode < srcNodes.size(); ++inode )
13134 tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, srcNodes[inode] );
13135 if ( aroundElements && tgtEditor.GetMeshDS()->FindElement( tgtNodes,
13137 /*noMedium=*/false))
13139 tgtEditor.AddElement( tgtNodes, elemKind.SetPoly( tgtNodes.size()/(iQuad+1) > 4 ));
13143 for ( size_t i = 0; i < missingBndElems.size(); ++i )
13145 TConnectivity& nodes = missingBndElems[ i ];
13146 if ( aroundElements && tgtEditor.GetMeshDS()->FindElement( nodes,
13148 /*noMedium=*/false))
13150 SMDS_MeshElement* newElem =
13151 tgtEditor.AddElement( nodes, elemKind.SetPoly( nodes.size()/(iQuad+1) > 4 ));
13152 nbAddedBnd += bool( newElem );
13154 // try to set a new element to a shape
13155 if ( myMesh->HasShapeToMesh() )
13158 set< pair<TopAbs_ShapeEnum, int > > mediumShapes;
13159 const size_t nbN = nodes.size() / (iQuad+1 );
13160 for ( inode = 0; inode < nbN && ok; ++inode )
13162 pair<int, TopAbs_ShapeEnum> i_stype =
13163 helper.GetMediumPos( nodes[inode], nodes[(inode+1)%nbN]);
13164 if (( ok = ( i_stype.first > 0 && i_stype.second >= TopAbs_FACE )))
13165 mediumShapes.insert( make_pair ( i_stype.second, i_stype.first ));
13167 if ( ok && mediumShapes.size() > 1 )
13169 set< pair<TopAbs_ShapeEnum, int > >::iterator stype_i = mediumShapes.begin();
13170 pair<TopAbs_ShapeEnum, int> stype_i_0 = *stype_i;
13171 for ( ++stype_i; stype_i != mediumShapes.end() && ok; ++stype_i )
13173 if (( ok = ( stype_i->first != stype_i_0.first )))
13174 ok = helper.IsSubShape( aMesh->IndexToShape( stype_i->second ),
13175 aMesh->IndexToShape( stype_i_0.second ));
13178 if ( ok && mediumShapes.begin()->first == missShapeType )
13179 aMesh->SetMeshElementOnShape( newElem, mediumShapes.begin()->second );
13183 // ----------------------------------
13184 // 3. Copy present boundary elements
13185 // ----------------------------------
13186 if ( toCopyExistingBoundary )
13187 for ( size_t i = 0 ; i < presentBndElems.size(); ++i )
13189 const SMDS_MeshElement* e = presentBndElems[i];
13190 tgtNodes.resize( e->NbNodes() );
13191 for ( inode = 0; inode < tgtNodes.size(); ++inode )
13192 tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, e->GetNode(inode) );
13193 presentEditor->AddElement( tgtNodes, elemToCopy.Init( e ));
13195 else // store present elements to add them to a group
13196 for ( size_t i = 0 ; i < presentBndElems.size(); ++i )
13198 presentEditor->myLastCreatedElems.Append( presentBndElems[ i ]);
13201 } // loop on given elements
13203 // ---------------------------------------------
13204 // 4. Fill group with boundary elements
13205 // ---------------------------------------------
13208 if ( SMESHDS_Group* g = dynamic_cast<SMESHDS_Group*>( group->GetGroupDS() ))
13209 for ( int i = 0; i < tgtEditor.myLastCreatedElems.Size(); ++i )
13210 g->SMDSGroup().Add( tgtEditor.myLastCreatedElems( i+1 ));
13212 tgtEditor.myLastCreatedElems.Clear();
13213 tgtEditor2.myLastCreatedElems.Clear();
13215 // -----------------------
13216 // 5. Copy given elements
13217 // -----------------------
13218 if ( toCopyElements && targetMesh != myMesh )
13220 if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
13221 else eIt = elemSetIterator( elements );
13222 while (eIt->more())
13224 const SMDS_MeshElement* elem = eIt->next();
13225 tgtNodes.resize( elem->NbNodes() );
13226 for ( inode = 0; inode < tgtNodes.size(); ++inode )
13227 tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, elem->GetNode(inode) );
13228 tgtEditor.AddElement( tgtNodes, elemToCopy.Init( elem ));
13230 tgtEditor.myLastCreatedElems.Clear();
13236 //================================================================================
13238 * \brief Copy node position and set \a to node on the same geometry
13240 //================================================================================
13242 void SMESH_MeshEditor::copyPosition( const SMDS_MeshNode* from,
13243 const SMDS_MeshNode* to )
13245 if ( !from || !to ) return;
13247 SMDS_PositionPtr pos = from->GetPosition();
13248 if ( !pos || from->getshapeId() < 1 ) return;
13250 switch ( pos->GetTypeOfPosition() )
13252 case SMDS_TOP_3DSPACE: break;
13254 case SMDS_TOP_FACE:
13256 const SMDS_FacePosition* fPos = static_cast< const SMDS_FacePosition* >( pos );
13257 GetMeshDS()->SetNodeOnFace( to, from->getshapeId(),
13258 fPos->GetUParameter(), fPos->GetVParameter() );
13261 case SMDS_TOP_EDGE:
13263 // WARNING: it is dangerous to set equal nodes on one EDGE!!!!!!!!
13264 const SMDS_EdgePosition* ePos = static_cast< const SMDS_EdgePosition* >( pos );
13265 GetMeshDS()->SetNodeOnEdge( to, from->getshapeId(), ePos->GetUParameter() );
13268 case SMDS_TOP_VERTEX:
13270 GetMeshDS()->SetNodeOnVertex( to, from->getshapeId() );
13273 case SMDS_TOP_UNSPEC: