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>
97 #include <Standard_Failure.hxx>
98 #include <Standard_ErrorHandler.hxx>
100 #define cast2Node(elem) static_cast<const SMDS_MeshNode*>( elem )
103 using namespace SMESH::Controls;
107 template < class ELEM_SET >
108 SMDS_ElemIteratorPtr elemSetIterator( const ELEM_SET& elements )
110 typedef SMDS_SetIterator
111 < SMDS_pElement, typename ELEM_SET::const_iterator> TSetIterator;
112 return SMDS_ElemIteratorPtr( new TSetIterator( elements.begin(), elements.end() ));
116 //=======================================================================
117 //function : SMESH_MeshEditor
119 //=======================================================================
121 SMESH_MeshEditor::SMESH_MeshEditor( SMESH_Mesh* theMesh )
122 :myMesh( theMesh ) // theMesh may be NULL
126 //================================================================================
128 * \brief Return mesh DS
130 //================================================================================
132 SMESHDS_Mesh * SMESH_MeshEditor::GetMeshDS()
134 return myMesh->GetMeshDS();
138 //================================================================================
140 * \brief Clears myLastCreatedNodes and myLastCreatedElems
142 //================================================================================
144 void SMESH_MeshEditor::ClearLastCreated()
146 myLastCreatedNodes.Clear();
147 myLastCreatedElems.Clear();
150 //================================================================================
152 * \brief Initializes members by an existing element
153 * \param [in] elem - the source element
154 * \param [in] basicOnly - if true, does not set additional data of Ball and Polyhedron
156 //================================================================================
158 SMESH_MeshEditor::ElemFeatures&
159 SMESH_MeshEditor::ElemFeatures::Init( const SMDS_MeshElement* elem, bool basicOnly )
163 myType = elem->GetType();
164 if ( myType == SMDSAbs_Face || myType == SMDSAbs_Volume )
166 myIsPoly = elem->IsPoly();
169 myIsQuad = elem->IsQuadratic();
170 if ( myType == SMDSAbs_Volume && !basicOnly )
172 vector<int > quant = static_cast<const SMDS_VtkVolume* >( elem )->GetQuantities();
173 myPolyhedQuantities.swap( quant );
177 else if ( myType == SMDSAbs_Ball && !basicOnly )
179 myBallDiameter = static_cast<const SMDS_BallElement*>(elem)->GetDiameter();
185 //=======================================================================
189 //=======================================================================
192 SMESH_MeshEditor::AddElement(const vector<const SMDS_MeshNode*> & node,
193 const ElemFeatures& features)
195 SMDS_MeshElement* e = 0;
196 int nbnode = node.size();
197 SMESHDS_Mesh* mesh = GetMeshDS();
198 const int ID = features.myID;
200 switch ( features.myType ) {
202 if ( !features.myIsPoly ) {
204 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], ID);
205 else e = mesh->AddFace (node[0], node[1], node[2] );
207 else if (nbnode == 4) {
208 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3], ID);
209 else e = mesh->AddFace (node[0], node[1], node[2], node[3] );
211 else if (nbnode == 6) {
212 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
213 node[4], node[5], ID);
214 else e = mesh->AddFace (node[0], node[1], node[2], node[3],
217 else if (nbnode == 7) {
218 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
219 node[4], node[5], node[6], ID);
220 else e = mesh->AddFace (node[0], node[1], node[2], node[3],
221 node[4], node[5], node[6] );
223 else if (nbnode == 8) {
224 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
225 node[4], node[5], node[6], node[7], ID);
226 else e = mesh->AddFace (node[0], node[1], node[2], node[3],
227 node[4], node[5], node[6], node[7] );
229 else if (nbnode == 9) {
230 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
231 node[4], node[5], node[6], node[7], node[8], ID);
232 else e = mesh->AddFace (node[0], node[1], node[2], node[3],
233 node[4], node[5], node[6], node[7], node[8] );
236 else if ( !features.myIsQuad )
238 if ( ID >= 1 ) e = mesh->AddPolygonalFaceWithID(node, ID);
239 else e = mesh->AddPolygonalFace (node );
241 else if ( nbnode % 2 == 0 ) // just a protection
243 if ( ID >= 1 ) e = mesh->AddQuadPolygonalFaceWithID(node, ID);
244 else e = mesh->AddQuadPolygonalFace (node );
249 if ( !features.myIsPoly ) {
251 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3], ID);
252 else e = mesh->AddVolume (node[0], node[1], node[2], node[3] );
254 else if (nbnode == 5) {
255 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
257 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
260 else if (nbnode == 6) {
261 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
262 node[4], node[5], ID);
263 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
266 else if (nbnode == 8) {
267 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
268 node[4], node[5], node[6], node[7], ID);
269 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
270 node[4], node[5], node[6], node[7] );
272 else if (nbnode == 10) {
273 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
274 node[4], node[5], node[6], node[7],
275 node[8], node[9], ID);
276 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
277 node[4], node[5], node[6], node[7],
280 else if (nbnode == 12) {
281 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
282 node[4], node[5], node[6], node[7],
283 node[8], node[9], node[10], node[11], ID);
284 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
285 node[4], node[5], node[6], node[7],
286 node[8], node[9], node[10], node[11] );
288 else if (nbnode == 13) {
289 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
290 node[4], node[5], node[6], node[7],
291 node[8], node[9], node[10],node[11],
293 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
294 node[4], node[5], node[6], node[7],
295 node[8], node[9], node[10],node[11],
298 else if (nbnode == 15) {
299 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
300 node[4], node[5], node[6], node[7],
301 node[8], node[9], node[10],node[11],
302 node[12],node[13],node[14],ID);
303 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
304 node[4], node[5], node[6], node[7],
305 node[8], node[9], node[10],node[11],
306 node[12],node[13],node[14] );
308 else if (nbnode == 20) {
309 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
310 node[4], node[5], node[6], node[7],
311 node[8], node[9], node[10],node[11],
312 node[12],node[13],node[14],node[15],
313 node[16],node[17],node[18],node[19],ID);
314 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
315 node[4], node[5], node[6], node[7],
316 node[8], node[9], node[10],node[11],
317 node[12],node[13],node[14],node[15],
318 node[16],node[17],node[18],node[19] );
320 else if (nbnode == 27) {
321 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
322 node[4], node[5], node[6], node[7],
323 node[8], node[9], node[10],node[11],
324 node[12],node[13],node[14],node[15],
325 node[16],node[17],node[18],node[19],
326 node[20],node[21],node[22],node[23],
327 node[24],node[25],node[26], ID);
328 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
329 node[4], node[5], node[6], node[7],
330 node[8], node[9], node[10],node[11],
331 node[12],node[13],node[14],node[15],
332 node[16],node[17],node[18],node[19],
333 node[20],node[21],node[22],node[23],
334 node[24],node[25],node[26] );
337 else if ( !features.myIsQuad )
339 if ( ID >= 1 ) e = mesh->AddPolyhedralVolumeWithID(node, features.myPolyhedQuantities, ID);
340 else e = mesh->AddPolyhedralVolume (node, features.myPolyhedQuantities );
344 // if ( ID >= 1 ) e = mesh->AddQuadPolyhedralVolumeWithID(node, features.myPolyhedQuantities,ID);
345 // else e = mesh->AddQuadPolyhedralVolume (node, features.myPolyhedQuantities );
351 if ( ID >= 1 ) e = mesh->AddEdgeWithID(node[0], node[1], ID);
352 else e = mesh->AddEdge (node[0], node[1] );
354 else if ( nbnode == 3 ) {
355 if ( ID >= 1 ) e = mesh->AddEdgeWithID(node[0], node[1], node[2], ID);
356 else e = mesh->AddEdge (node[0], node[1], node[2] );
360 case SMDSAbs_0DElement:
362 if ( ID >= 1 ) e = mesh->Add0DElementWithID(node[0], ID);
363 else e = mesh->Add0DElement (node[0] );
368 if ( ID >= 1 ) e = mesh->AddNodeWithID(node[0]->X(), node[0]->Y(), node[0]->Z(), ID);
369 else e = mesh->AddNode (node[0]->X(), node[0]->Y(), node[0]->Z() );
373 if ( ID >= 1 ) e = mesh->AddBallWithID(node[0], features.myBallDiameter, ID);
374 else e = mesh->AddBall (node[0], features.myBallDiameter );
379 if ( e ) myLastCreatedElems.Append( e );
383 //=======================================================================
387 //=======================================================================
389 SMDS_MeshElement* SMESH_MeshEditor::AddElement(const vector<int> & nodeIDs,
390 const ElemFeatures& features)
392 vector<const SMDS_MeshNode*> nodes;
393 nodes.reserve( nodeIDs.size() );
394 vector<int>::const_iterator id = nodeIDs.begin();
395 while ( id != nodeIDs.end() ) {
396 if ( const SMDS_MeshNode* node = GetMeshDS()->FindNode( *id++ ))
397 nodes.push_back( node );
401 return AddElement( nodes, features );
404 //=======================================================================
406 //purpose : Remove a node or an element.
407 // Modify a compute state of sub-meshes which become empty
408 //=======================================================================
410 int SMESH_MeshEditor::Remove (const list< int >& theIDs,
413 myLastCreatedElems.Clear();
414 myLastCreatedNodes.Clear();
416 SMESHDS_Mesh* aMesh = GetMeshDS();
417 set< SMESH_subMesh *> smmap;
420 list<int>::const_iterator it = theIDs.begin();
421 for ( ; it != theIDs.end(); it++ ) {
422 const SMDS_MeshElement * elem;
424 elem = aMesh->FindNode( *it );
426 elem = aMesh->FindElement( *it );
430 // Notify VERTEX sub-meshes about modification
432 const SMDS_MeshNode* node = cast2Node( elem );
433 if ( node->GetPosition()->GetTypeOfPosition() == SMDS_TOP_VERTEX )
434 if ( int aShapeID = node->getshapeId() )
435 if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( aShapeID ) )
438 // Find sub-meshes to notify about modification
439 // SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
440 // while ( nodeIt->more() ) {
441 // const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
442 // const SMDS_PositionPtr& aPosition = node->GetPosition();
443 // if ( aPosition.get() ) {
444 // if ( int aShapeID = aPosition->GetShapeId() ) {
445 // if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( aShapeID ) )
446 // smmap.insert( sm );
453 aMesh->RemoveNode( static_cast< const SMDS_MeshNode* >( elem ));
455 aMesh->RemoveElement( elem );
459 // Notify sub-meshes about modification
460 if ( !smmap.empty() ) {
461 set< SMESH_subMesh *>::iterator smIt;
462 for ( smIt = smmap.begin(); smIt != smmap.end(); smIt++ )
463 (*smIt)->ComputeStateEngine( SMESH_subMesh::MESH_ENTITY_REMOVED );
466 // // Check if the whole mesh becomes empty
467 // if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( 1 ) )
468 // sm->ComputeStateEngine( SMESH_subMesh::CHECK_COMPUTE_STATE );
473 //================================================================================
475 * \brief Create 0D elements on all nodes of the given object.
476 * \param elements - Elements on whose nodes to create 0D elements; if empty,
477 * the all mesh is treated
478 * \param all0DElems - returns all 0D elements found or created on nodes of \a elements
479 * \param duplicateElements - to add one more 0D element to a node or not
481 //================================================================================
483 void SMESH_MeshEditor::Create0DElementsOnAllNodes( const TIDSortedElemSet& elements,
484 TIDSortedElemSet& all0DElems,
485 const bool duplicateElements )
487 SMDS_ElemIteratorPtr elemIt;
488 if ( elements.empty() )
490 elemIt = GetMeshDS()->elementsIterator( SMDSAbs_Node );
494 elemIt = elemSetIterator( elements );
497 while ( elemIt->more() )
499 const SMDS_MeshElement* e = elemIt->next();
500 SMDS_ElemIteratorPtr nodeIt = e->nodesIterator();
501 while ( nodeIt->more() )
503 const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
504 SMDS_ElemIteratorPtr it0D = n->GetInverseElementIterator( SMDSAbs_0DElement );
505 if ( duplicateElements || !it0D->more() )
507 myLastCreatedElems.Append( GetMeshDS()->Add0DElement( n ));
508 all0DElems.insert( myLastCreatedElems.Last() );
510 while ( it0D->more() )
511 all0DElems.insert( it0D->next() );
516 //=======================================================================
517 //function : FindShape
518 //purpose : Return an index of the shape theElem is on
519 // or zero if a shape not found
520 //=======================================================================
522 int SMESH_MeshEditor::FindShape (const SMDS_MeshElement * theElem)
524 myLastCreatedElems.Clear();
525 myLastCreatedNodes.Clear();
527 SMESHDS_Mesh * aMesh = GetMeshDS();
528 if ( aMesh->ShapeToMesh().IsNull() )
531 int aShapeID = theElem->getshapeId();
535 if ( SMESHDS_SubMesh * sm = aMesh->MeshElements( aShapeID ))
536 if ( sm->Contains( theElem ))
539 if ( theElem->GetType() == SMDSAbs_Node ) {
540 MESSAGE( ":( Error: invalid myShapeId of node " << theElem->GetID() );
543 MESSAGE( ":( Error: invalid myShapeId of element " << theElem->GetID() );
546 TopoDS_Shape aShape; // the shape a node of theElem is on
547 if ( theElem->GetType() != SMDSAbs_Node )
549 SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator();
550 while ( nodeIt->more() ) {
551 const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
552 if ((aShapeID = node->getshapeId()) > 0) {
553 if ( SMESHDS_SubMesh * sm = aMesh->MeshElements( aShapeID ) ) {
554 if ( sm->Contains( theElem ))
556 if ( aShape.IsNull() )
557 aShape = aMesh->IndexToShape( aShapeID );
563 // None of nodes is on a proper shape,
564 // find the shape among ancestors of aShape on which a node is
565 if ( !aShape.IsNull() ) {
566 TopTools_ListIteratorOfListOfShape ancIt( GetMesh()->GetAncestors( aShape ));
567 for ( ; ancIt.More(); ancIt.Next() ) {
568 SMESHDS_SubMesh * sm = aMesh->MeshElements( ancIt.Value() );
569 if ( sm && sm->Contains( theElem ))
570 return aMesh->ShapeToIndex( ancIt.Value() );
575 SMESHDS_SubMeshIteratorPtr smIt = GetMeshDS()->SubMeshes();
576 while ( const SMESHDS_SubMesh* sm = smIt->next() )
577 if ( sm->Contains( theElem ))
584 //=======================================================================
585 //function : IsMedium
587 //=======================================================================
589 bool SMESH_MeshEditor::IsMedium(const SMDS_MeshNode* node,
590 const SMDSAbs_ElementType typeToCheck)
592 bool isMedium = false;
593 SMDS_ElemIteratorPtr it = node->GetInverseElementIterator(typeToCheck);
594 while (it->more() && !isMedium ) {
595 const SMDS_MeshElement* elem = it->next();
596 isMedium = elem->IsMediumNode(node);
601 //=======================================================================
602 //function : shiftNodesQuadTria
603 //purpose : Shift nodes in the array corresponded to quadratic triangle
604 // example: (0,1,2,3,4,5) -> (1,2,0,4,5,3)
605 //=======================================================================
607 static void shiftNodesQuadTria(vector< const SMDS_MeshNode* >& aNodes)
609 const SMDS_MeshNode* nd1 = aNodes[0];
610 aNodes[0] = aNodes[1];
611 aNodes[1] = aNodes[2];
613 const SMDS_MeshNode* nd2 = aNodes[3];
614 aNodes[3] = aNodes[4];
615 aNodes[4] = aNodes[5];
619 //=======================================================================
620 //function : nbEdgeConnectivity
621 //purpose : return number of the edges connected with the theNode.
622 // if theEdges has connections with the other type of the
623 // elements, return -1
624 //=======================================================================
626 static int nbEdgeConnectivity(const SMDS_MeshNode* theNode)
628 // SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator();
630 // while(elemIt->more()) {
635 return theNode->NbInverseElements();
638 //=======================================================================
639 //function : getNodesFromTwoTria
641 //=======================================================================
643 static bool getNodesFromTwoTria(const SMDS_MeshElement * theTria1,
644 const SMDS_MeshElement * theTria2,
645 vector< const SMDS_MeshNode*>& N1,
646 vector< const SMDS_MeshNode*>& N2)
648 N1.assign( theTria1->begin_nodes(), theTria1->end_nodes() );
649 if ( N1.size() < 6 ) return false;
650 N2.assign( theTria2->begin_nodes(), theTria2->end_nodes() );
651 if ( N2.size() < 6 ) return false;
653 int sames[3] = {-1,-1,-1};
665 if(nbsames!=2) return false;
667 shiftNodesQuadTria(N1);
669 shiftNodesQuadTria(N1);
672 i = sames[0] + sames[1] + sames[2];
674 shiftNodesQuadTria(N2);
676 // now we receive following N1 and N2 (using numeration as in the image below)
677 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
678 // i.e. first nodes from both arrays form a new diagonal
682 //=======================================================================
683 //function : InverseDiag
684 //purpose : Replace two neighbour triangles with ones built on the same 4 nodes
685 // but having other common link.
686 // Return False if args are improper
687 //=======================================================================
689 bool SMESH_MeshEditor::InverseDiag (const SMDS_MeshElement * theTria1,
690 const SMDS_MeshElement * theTria2 )
692 myLastCreatedElems.Clear();
693 myLastCreatedNodes.Clear();
695 if (!theTria1 || !theTria2)
698 const SMDS_VtkFace* F1 = dynamic_cast<const SMDS_VtkFace*>( theTria1 );
699 if (!F1) return false;
700 const SMDS_VtkFace* F2 = dynamic_cast<const SMDS_VtkFace*>( theTria2 );
701 if (!F2) return false;
702 if ((theTria1->GetEntityType() == SMDSEntity_Triangle) &&
703 (theTria2->GetEntityType() == SMDSEntity_Triangle)) {
705 // 1 +--+ A theTria1: ( 1 A B ) A->2 ( 1 2 B ) 1 +--+ A
706 // | /| theTria2: ( B A 2 ) B->1 ( 1 A 2 ) |\ |
710 // put nodes in array and find out indices of the same ones
711 const SMDS_MeshNode* aNodes [6];
712 int sameInd [] = { -1, -1, -1, -1, -1, -1 };
714 SMDS_ElemIteratorPtr it = theTria1->nodesIterator();
715 while ( it->more() ) {
716 aNodes[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
718 if ( i > 2 ) // theTria2
719 // find same node of theTria1
720 for ( int j = 0; j < 3; j++ )
721 if ( aNodes[ i ] == aNodes[ j ]) {
730 return false; // theTria1 is not a triangle
731 it = theTria2->nodesIterator();
733 if ( i == 6 && it->more() )
734 return false; // theTria2 is not a triangle
737 // find indices of 1,2 and of A,B in theTria1
738 int iA = -1, iB = 0, i1 = 0, i2 = 0;
739 for ( i = 0; i < 6; i++ ) {
740 if ( sameInd [ i ] == -1 ) {
745 if ( iA >= 0) iB = i;
749 // nodes 1 and 2 should not be the same
750 if ( aNodes[ i1 ] == aNodes[ i2 ] )
754 aNodes[ iA ] = aNodes[ i2 ];
756 aNodes[ sameInd[ iB ]] = aNodes[ i1 ];
758 GetMeshDS()->ChangeElementNodes( theTria1, aNodes, 3 );
759 GetMeshDS()->ChangeElementNodes( theTria2, &aNodes[ 3 ], 3 );
763 } // end if(F1 && F2)
765 // check case of quadratic faces
766 if (theTria1->GetEntityType() != SMDSEntity_Quad_Triangle &&
767 theTria1->GetEntityType() != SMDSEntity_BiQuad_Triangle)
769 if (theTria2->GetEntityType() != SMDSEntity_Quad_Triangle&&
770 theTria2->GetEntityType() != SMDSEntity_BiQuad_Triangle)
774 // 1 +--+--+ 2 theTria1: (1 2 4 5 9 7) or (2 4 1 9 7 5) or (4 1 2 7 5 9)
775 // | /| theTria2: (2 3 4 6 8 9) or (3 4 2 8 9 6) or (4 2 3 9 6 8)
783 vector< const SMDS_MeshNode* > N1;
784 vector< const SMDS_MeshNode* > N2;
785 if(!getNodesFromTwoTria(theTria1,theTria2,N1,N2))
787 // now we receive following N1 and N2 (using numeration as above image)
788 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
789 // i.e. first nodes from both arrays determ new diagonal
791 vector< const SMDS_MeshNode*> N1new( N1.size() );
792 vector< const SMDS_MeshNode*> N2new( N2.size() );
793 N1new.back() = N1.back(); // central node of biquadratic
794 N2new.back() = N2.back();
795 N1new[0] = N1[0]; N2new[0] = N1[0];
796 N1new[1] = N2[0]; N2new[1] = N1[1];
797 N1new[2] = N2[1]; N2new[2] = N2[0];
798 N1new[3] = N1[4]; N2new[3] = N1[3];
799 N1new[4] = N2[3]; N2new[4] = N2[5];
800 N1new[5] = N1[5]; N2new[5] = N1[4];
801 // change nodes in faces
802 GetMeshDS()->ChangeElementNodes( theTria1, &N1new[0], N1new.size() );
803 GetMeshDS()->ChangeElementNodes( theTria2, &N2new[0], N2new.size() );
805 // move the central node of biquadratic triangle
806 SMESH_MesherHelper helper( *GetMesh() );
807 for ( int is2nd = 0; is2nd < 2; ++is2nd )
809 const SMDS_MeshElement* tria = is2nd ? theTria2 : theTria1;
810 vector< const SMDS_MeshNode*>& nodes = is2nd ? N2new : N1new;
811 if ( nodes.size() < 7 )
813 helper.SetSubShape( tria->getshapeId() );
814 const TopoDS_Face& F = TopoDS::Face( helper.GetSubShape() );
818 xyz = ( SMESH_TNodeXYZ( nodes[3] ) +
819 SMESH_TNodeXYZ( nodes[4] ) +
820 SMESH_TNodeXYZ( nodes[5] )) / 3.;
825 gp_XY uv = ( helper.GetNodeUV( F, nodes[3], nodes[2], &checkUV ) +
826 helper.GetNodeUV( F, nodes[4], nodes[0], &checkUV ) +
827 helper.GetNodeUV( F, nodes[5], nodes[1], &checkUV )) / 3.;
829 Handle(Geom_Surface) S = BRep_Tool::Surface(F,loc);
830 xyz = S->Value( uv.X(), uv.Y() );
831 xyz.Transform( loc );
832 if ( nodes[6]->GetPosition()->GetTypeOfPosition() == SMDS_TOP_FACE && // set UV
833 nodes[6]->getshapeId() > 0 )
834 GetMeshDS()->SetNodeOnFace( nodes[6], nodes[6]->getshapeId(), uv.X(), uv.Y() );
836 GetMeshDS()->MoveNode( nodes[6], xyz.X(), xyz.Y(), xyz.Z() );
841 //=======================================================================
842 //function : findTriangles
843 //purpose : find triangles sharing theNode1-theNode2 link
844 //=======================================================================
846 static bool findTriangles(const SMDS_MeshNode * theNode1,
847 const SMDS_MeshNode * theNode2,
848 const SMDS_MeshElement*& theTria1,
849 const SMDS_MeshElement*& theTria2)
851 if ( !theNode1 || !theNode2 ) return false;
853 theTria1 = theTria2 = 0;
855 set< const SMDS_MeshElement* > emap;
856 SMDS_ElemIteratorPtr it = theNode1->GetInverseElementIterator(SMDSAbs_Face);
858 const SMDS_MeshElement* elem = it->next();
859 if ( elem->NbCornerNodes() == 3 )
862 it = theNode2->GetInverseElementIterator(SMDSAbs_Face);
864 const SMDS_MeshElement* elem = it->next();
865 if ( emap.count( elem )) {
873 // theTria1 must be element with minimum ID
874 if ( theTria2->GetID() < theTria1->GetID() )
875 std::swap( theTria2, theTria1 );
883 //=======================================================================
884 //function : InverseDiag
885 //purpose : Replace two neighbour triangles sharing theNode1-theNode2 link
886 // with ones built on the same 4 nodes but having other common link.
887 // Return false if proper faces not found
888 //=======================================================================
890 bool SMESH_MeshEditor::InverseDiag (const SMDS_MeshNode * theNode1,
891 const SMDS_MeshNode * theNode2)
893 myLastCreatedElems.Clear();
894 myLastCreatedNodes.Clear();
896 const SMDS_MeshElement *tr1, *tr2;
897 if ( !findTriangles( theNode1, theNode2, tr1, tr2 ))
900 const SMDS_VtkFace* F1 = dynamic_cast<const SMDS_VtkFace*>( tr1 );
901 if (!F1) return false;
902 const SMDS_VtkFace* F2 = dynamic_cast<const SMDS_VtkFace*>( tr2 );
903 if (!F2) return false;
904 if ((tr1->GetEntityType() == SMDSEntity_Triangle) &&
905 (tr2->GetEntityType() == SMDSEntity_Triangle)) {
907 // 1 +--+ A tr1: ( 1 A B ) A->2 ( 1 2 B ) 1 +--+ A
908 // | /| tr2: ( B A 2 ) B->1 ( 1 A 2 ) |\ |
912 // put nodes in array
913 // and find indices of 1,2 and of A in tr1 and of B in tr2
914 int i, iA1 = 0, i1 = 0;
915 const SMDS_MeshNode* aNodes1 [3];
916 SMDS_ElemIteratorPtr it;
917 for (i = 0, it = tr1->nodesIterator(); it->more(); i++ ) {
918 aNodes1[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
919 if ( aNodes1[ i ] == theNode1 )
920 iA1 = i; // node A in tr1
921 else if ( aNodes1[ i ] != theNode2 )
925 const SMDS_MeshNode* aNodes2 [3];
926 for (i = 0, it = tr2->nodesIterator(); it->more(); i++ ) {
927 aNodes2[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
928 if ( aNodes2[ i ] == theNode2 )
929 iB2 = i; // node B in tr2
930 else if ( aNodes2[ i ] != theNode1 )
934 // nodes 1 and 2 should not be the same
935 if ( aNodes1[ i1 ] == aNodes2[ i2 ] )
939 aNodes1[ iA1 ] = aNodes2[ i2 ];
941 aNodes2[ iB2 ] = aNodes1[ i1 ];
943 GetMeshDS()->ChangeElementNodes( tr1, aNodes1, 3 );
944 GetMeshDS()->ChangeElementNodes( tr2, aNodes2, 3 );
949 // check case of quadratic faces
950 return InverseDiag(tr1,tr2);
953 //=======================================================================
954 //function : getQuadrangleNodes
955 //purpose : fill theQuadNodes - nodes of a quadrangle resulting from
956 // fusion of triangles tr1 and tr2 having shared link on
957 // theNode1 and theNode2
958 //=======================================================================
960 bool getQuadrangleNodes(const SMDS_MeshNode * theQuadNodes [],
961 const SMDS_MeshNode * theNode1,
962 const SMDS_MeshNode * theNode2,
963 const SMDS_MeshElement * tr1,
964 const SMDS_MeshElement * tr2 )
966 if( tr1->NbNodes() != tr2->NbNodes() )
968 // find the 4-th node to insert into tr1
969 const SMDS_MeshNode* n4 = 0;
970 SMDS_ElemIteratorPtr it = tr2->nodesIterator();
972 while ( !n4 && i<3 ) {
973 const SMDS_MeshNode * n = cast2Node( it->next() );
975 bool isDiag = ( n == theNode1 || n == theNode2 );
979 // Make an array of nodes to be in a quadrangle
980 int iNode = 0, iFirstDiag = -1;
981 it = tr1->nodesIterator();
984 const SMDS_MeshNode * n = cast2Node( it->next() );
986 bool isDiag = ( n == theNode1 || n == theNode2 );
988 if ( iFirstDiag < 0 )
990 else if ( iNode - iFirstDiag == 1 )
991 theQuadNodes[ iNode++ ] = n4; // insert the 4-th node between diagonal nodes
993 else if ( n == n4 ) {
994 return false; // tr1 and tr2 should not have all the same nodes
996 theQuadNodes[ iNode++ ] = n;
998 if ( iNode == 3 ) // diagonal nodes have 0 and 2 indices
999 theQuadNodes[ iNode ] = n4;
1004 //=======================================================================
1005 //function : DeleteDiag
1006 //purpose : Replace two neighbour triangles sharing theNode1-theNode2 link
1007 // with a quadrangle built on the same 4 nodes.
1008 // Return false if proper faces not found
1009 //=======================================================================
1011 bool SMESH_MeshEditor::DeleteDiag (const SMDS_MeshNode * theNode1,
1012 const SMDS_MeshNode * theNode2)
1014 myLastCreatedElems.Clear();
1015 myLastCreatedNodes.Clear();
1017 const SMDS_MeshElement *tr1, *tr2;
1018 if ( !findTriangles( theNode1, theNode2, tr1, tr2 ))
1021 const SMDS_VtkFace* F1 = dynamic_cast<const SMDS_VtkFace*>( tr1 );
1022 if (!F1) return false;
1023 const SMDS_VtkFace* F2 = dynamic_cast<const SMDS_VtkFace*>( tr2 );
1024 if (!F2) return false;
1025 SMESHDS_Mesh * aMesh = GetMeshDS();
1027 if ((tr1->GetEntityType() == SMDSEntity_Triangle) &&
1028 (tr2->GetEntityType() == SMDSEntity_Triangle)) {
1030 const SMDS_MeshNode* aNodes [ 4 ];
1031 if ( ! getQuadrangleNodes( aNodes, theNode1, theNode2, tr1, tr2 ))
1034 const SMDS_MeshElement* newElem = 0;
1035 newElem = aMesh->AddFace( aNodes[0], aNodes[1], aNodes[2], aNodes[3] );
1036 myLastCreatedElems.Append(newElem);
1037 AddToSameGroups( newElem, tr1, aMesh );
1038 int aShapeId = tr1->getshapeId();
1041 aMesh->SetMeshElementOnShape( newElem, aShapeId );
1043 aMesh->RemoveElement( tr1 );
1044 aMesh->RemoveElement( tr2 );
1049 // check case of quadratic faces
1050 if (tr1->GetEntityType() != SMDSEntity_Quad_Triangle)
1052 if (tr2->GetEntityType() != SMDSEntity_Quad_Triangle)
1056 // 1 +--+--+ 2 tr1: (1 2 4 5 9 7) or (2 4 1 9 7 5) or (4 1 2 7 5 9)
1057 // | /| tr2: (2 3 4 6 8 9) or (3 4 2 8 9 6) or (4 2 3 9 6 8)
1065 vector< const SMDS_MeshNode* > N1;
1066 vector< const SMDS_MeshNode* > N2;
1067 if(!getNodesFromTwoTria(tr1,tr2,N1,N2))
1069 // now we receive following N1 and N2 (using numeration as above image)
1070 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
1071 // i.e. first nodes from both arrays determ new diagonal
1073 const SMDS_MeshNode* aNodes[8];
1083 const SMDS_MeshElement* newElem = 0;
1084 newElem = aMesh->AddFace( aNodes[0], aNodes[1], aNodes[2], aNodes[3],
1085 aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
1086 myLastCreatedElems.Append(newElem);
1087 AddToSameGroups( newElem, tr1, aMesh );
1088 int aShapeId = tr1->getshapeId();
1091 aMesh->SetMeshElementOnShape( newElem, aShapeId );
1093 aMesh->RemoveElement( tr1 );
1094 aMesh->RemoveElement( tr2 );
1096 // remove middle node (9)
1097 GetMeshDS()->RemoveNode( N1[4] );
1102 //=======================================================================
1103 //function : Reorient
1104 //purpose : Reverse theElement orientation
1105 //=======================================================================
1107 bool SMESH_MeshEditor::Reorient (const SMDS_MeshElement * theElem)
1109 myLastCreatedElems.Clear();
1110 myLastCreatedNodes.Clear();
1114 SMDS_ElemIteratorPtr it = theElem->nodesIterator();
1115 if ( !it || !it->more() )
1118 const SMDSAbs_ElementType type = theElem->GetType();
1119 if ( type < SMDSAbs_Edge || type > SMDSAbs_Volume )
1122 const SMDSAbs_EntityType geomType = theElem->GetEntityType();
1123 if ( geomType == SMDSEntity_Polyhedra ) // polyhedron
1125 const SMDS_VtkVolume* aPolyedre =
1126 dynamic_cast<const SMDS_VtkVolume*>( theElem );
1128 MESSAGE("Warning: bad volumic element");
1131 const int nbFaces = aPolyedre->NbFaces();
1132 vector<const SMDS_MeshNode *> poly_nodes;
1133 vector<int> quantities (nbFaces);
1135 // reverse each face of the polyedre
1136 for (int iface = 1; iface <= nbFaces; iface++) {
1137 int inode, nbFaceNodes = aPolyedre->NbFaceNodes(iface);
1138 quantities[iface - 1] = nbFaceNodes;
1140 for (inode = nbFaceNodes; inode >= 1; inode--) {
1141 const SMDS_MeshNode* curNode = aPolyedre->GetFaceNode(iface, inode);
1142 poly_nodes.push_back(curNode);
1145 return GetMeshDS()->ChangePolyhedronNodes( theElem, poly_nodes, quantities );
1147 else // other elements
1149 vector<const SMDS_MeshNode*> nodes( theElem->begin_nodes(), theElem->end_nodes() );
1150 const std::vector<int>& interlace = SMDS_MeshCell::reverseSmdsOrder( geomType, nodes.size() );
1151 if ( interlace.empty() )
1153 std::reverse( nodes.begin(), nodes.end() ); // obsolete, just in case
1157 SMDS_MeshCell::applyInterlace( interlace, nodes );
1159 return GetMeshDS()->ChangeElementNodes( theElem, &nodes[0], nodes.size() );
1164 //================================================================================
1166 * \brief Reorient faces.
1167 * \param theFaces - the faces to reorient. If empty the whole mesh is meant
1168 * \param theDirection - desired direction of normal of \a theFace
1169 * \param theFace - one of \a theFaces that should be oriented according to
1170 * \a theDirection and whose orientation defines orientation of other faces
1171 * \return number of reoriented faces.
1173 //================================================================================
1175 int SMESH_MeshEditor::Reorient2D (TIDSortedElemSet & theFaces,
1176 const gp_Dir& theDirection,
1177 const SMDS_MeshElement * theFace)
1180 if ( !theFace || theFace->GetType() != SMDSAbs_Face ) return nbReori;
1182 if ( theFaces.empty() )
1184 SMDS_FaceIteratorPtr fIt = GetMeshDS()->facesIterator(/*idInceasingOrder=*/true);
1185 while ( fIt->more() )
1186 theFaces.insert( theFaces.end(), fIt->next() );
1189 // orient theFace according to theDirection
1191 SMESH_MeshAlgos::FaceNormal( theFace, normal, /*normalized=*/false );
1192 if ( normal * theDirection.XYZ() < 0 )
1193 nbReori += Reorient( theFace );
1195 // Orient other faces
1197 set< const SMDS_MeshElement* > startFaces, visitedFaces;
1198 TIDSortedElemSet avoidSet;
1199 set< SMESH_TLink > checkedLinks;
1200 pair< set< SMESH_TLink >::iterator, bool > linkIt_isNew;
1202 if ( theFaces.size() > 1 )// leave 1 face to prevent finding not selected faces
1203 theFaces.erase( theFace );
1204 startFaces.insert( theFace );
1206 int nodeInd1, nodeInd2;
1207 const SMDS_MeshElement* otherFace;
1208 vector< const SMDS_MeshElement* > facesNearLink;
1209 vector< std::pair< int, int > > nodeIndsOfFace;
1211 set< const SMDS_MeshElement* >::iterator startFace = startFaces.begin();
1212 while ( !startFaces.empty() )
1214 startFace = startFaces.begin();
1215 theFace = *startFace;
1216 startFaces.erase( startFace );
1217 if ( !visitedFaces.insert( theFace ).second )
1221 avoidSet.insert(theFace);
1223 NLink link( theFace->GetNode( 0 ), (SMDS_MeshNode *) 0 );
1225 const int nbNodes = theFace->NbCornerNodes();
1226 for ( int i = 0; i < nbNodes; ++i ) // loop on links of theFace
1228 link.second = theFace->GetNode(( i+1 ) % nbNodes );
1229 linkIt_isNew = checkedLinks.insert( link );
1230 if ( !linkIt_isNew.second )
1232 // link has already been checked and won't be encountered more
1233 // if the group (theFaces) is manifold
1234 //checkedLinks.erase( linkIt_isNew.first );
1238 facesNearLink.clear();
1239 nodeIndsOfFace.clear();
1240 while (( otherFace = SMESH_MeshAlgos::FindFaceInSet( link.first, link.second,
1242 &nodeInd1, &nodeInd2 )))
1243 if ( otherFace != theFace)
1245 facesNearLink.push_back( otherFace );
1246 nodeIndsOfFace.push_back( make_pair( nodeInd1, nodeInd2 ));
1247 avoidSet.insert( otherFace );
1249 if ( facesNearLink.size() > 1 )
1251 // NON-MANIFOLD mesh shell !
1252 // select a face most co-directed with theFace,
1253 // other faces won't be visited this time
1255 SMESH_MeshAlgos::FaceNormal( theFace, NF, /*normalized=*/false );
1256 double proj, maxProj = -1;
1257 for ( size_t i = 0; i < facesNearLink.size(); ++i ) {
1258 SMESH_MeshAlgos::FaceNormal( facesNearLink[i], NOF, /*normalized=*/false );
1259 if (( proj = Abs( NF * NOF )) > maxProj ) {
1261 otherFace = facesNearLink[i];
1262 nodeInd1 = nodeIndsOfFace[i].first;
1263 nodeInd2 = nodeIndsOfFace[i].second;
1266 // not to visit rejected faces
1267 for ( size_t i = 0; i < facesNearLink.size(); ++i )
1268 if ( facesNearLink[i] != otherFace && theFaces.size() > 1 )
1269 visitedFaces.insert( facesNearLink[i] );
1271 else if ( facesNearLink.size() == 1 )
1273 otherFace = facesNearLink[0];
1274 nodeInd1 = nodeIndsOfFace.back().first;
1275 nodeInd2 = nodeIndsOfFace.back().second;
1277 if ( otherFace && otherFace != theFace)
1279 // link must be reverse in otherFace if orientation ot otherFace
1280 // is same as that of theFace
1281 if ( abs(nodeInd2-nodeInd1) == 1 ? nodeInd2 > nodeInd1 : nodeInd1 > nodeInd2 )
1283 nbReori += Reorient( otherFace );
1285 startFaces.insert( otherFace );
1288 std::swap( link.first, link.second ); // reverse the link
1294 //================================================================================
1296 * \brief Reorient faces basing on orientation of adjacent volumes.
1297 * \param theFaces - faces to reorient. If empty, all mesh faces are treated.
1298 * \param theVolumes - reference volumes.
1299 * \param theOutsideNormal - to orient faces to have their normal
1300 * pointing either \a outside or \a inside the adjacent volumes.
1301 * \return number of reoriented faces.
1303 //================================================================================
1305 int SMESH_MeshEditor::Reorient2DBy3D (TIDSortedElemSet & theFaces,
1306 TIDSortedElemSet & theVolumes,
1307 const bool theOutsideNormal)
1311 SMDS_ElemIteratorPtr faceIt;
1312 if ( theFaces.empty() )
1313 faceIt = GetMeshDS()->elementsIterator( SMDSAbs_Face );
1315 faceIt = elemSetIterator( theFaces );
1317 vector< const SMDS_MeshNode* > faceNodes;
1318 TIDSortedElemSet checkedVolumes;
1319 set< const SMDS_MeshNode* > faceNodesSet;
1320 SMDS_VolumeTool volumeTool;
1322 while ( faceIt->more() ) // loop on given faces
1324 const SMDS_MeshElement* face = faceIt->next();
1325 if ( face->GetType() != SMDSAbs_Face )
1328 const size_t nbCornersNodes = face->NbCornerNodes();
1329 faceNodes.assign( face->begin_nodes(), face->end_nodes() );
1331 checkedVolumes.clear();
1332 SMDS_ElemIteratorPtr vIt = faceNodes[ 0 ]->GetInverseElementIterator( SMDSAbs_Volume );
1333 while ( vIt->more() )
1335 const SMDS_MeshElement* volume = vIt->next();
1337 if ( !checkedVolumes.insert( volume ).second )
1339 if ( !theVolumes.empty() && !theVolumes.count( volume ))
1342 // is volume adjacent?
1343 bool allNodesCommon = true;
1344 for ( size_t iN = 1; iN < nbCornersNodes && allNodesCommon; ++iN )
1345 allNodesCommon = ( volume->GetNodeIndex( faceNodes[ iN ]) > -1 );
1346 if ( !allNodesCommon )
1349 // get nodes of a corresponding volume facet
1350 faceNodesSet.clear();
1351 faceNodesSet.insert( faceNodes.begin(), faceNodes.end() );
1352 volumeTool.Set( volume );
1353 int facetID = volumeTool.GetFaceIndex( faceNodesSet );
1354 if ( facetID < 0 ) continue;
1355 volumeTool.SetExternalNormal();
1356 const SMDS_MeshNode** facetNodes = volumeTool.GetFaceNodes( facetID );
1358 // compare order of faceNodes and facetNodes
1359 const int iQ = 1 + ( nbCornersNodes < faceNodes.size() );
1361 for ( int i = 0; i < 2; ++i )
1363 const SMDS_MeshNode* n = facetNodes[ i*iQ ];
1364 for ( size_t iN = 0; iN < nbCornersNodes; ++iN )
1365 if ( faceNodes[ iN ] == n )
1371 bool isOutside = Abs( iNN[0]-iNN[1] ) == 1 ? iNN[0] < iNN[1] : iNN[0] > iNN[1];
1372 if ( isOutside != theOutsideNormal )
1373 nbReori += Reorient( face );
1375 } // loop on given faces
1380 //=======================================================================
1381 //function : getBadRate
1383 //=======================================================================
1385 static double getBadRate (const SMDS_MeshElement* theElem,
1386 SMESH::Controls::NumericalFunctorPtr& theCrit)
1388 SMESH::Controls::TSequenceOfXYZ P;
1389 if ( !theElem || !theCrit->GetPoints( theElem, P ))
1391 return theCrit->GetBadRate( theCrit->GetValue( P ), theElem->NbNodes() );
1392 //return theCrit->GetBadRate( theCrit->GetValue( theElem->GetID() ), theElem->NbNodes() );
1395 //=======================================================================
1396 //function : QuadToTri
1397 //purpose : Cut quadrangles into triangles.
1398 // theCrit is used to select a diagonal to cut
1399 //=======================================================================
1401 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet & theElems,
1402 SMESH::Controls::NumericalFunctorPtr theCrit)
1404 myLastCreatedElems.Clear();
1405 myLastCreatedNodes.Clear();
1407 if ( !theCrit.get() )
1410 SMESHDS_Mesh * aMesh = GetMeshDS();
1412 Handle(Geom_Surface) surface;
1413 SMESH_MesherHelper helper( *GetMesh() );
1415 TIDSortedElemSet::iterator itElem;
1416 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
1418 const SMDS_MeshElement* elem = *itElem;
1419 if ( !elem || elem->GetType() != SMDSAbs_Face )
1421 if ( elem->NbCornerNodes() != 4 )
1424 // retrieve element nodes
1425 vector< const SMDS_MeshNode* > aNodes( elem->begin_nodes(), elem->end_nodes() );
1427 // compare two sets of possible triangles
1428 double aBadRate1, aBadRate2; // to what extent a set is bad
1429 SMDS_FaceOfNodes tr1 ( aNodes[0], aNodes[1], aNodes[2] );
1430 SMDS_FaceOfNodes tr2 ( aNodes[2], aNodes[3], aNodes[0] );
1431 aBadRate1 = getBadRate( &tr1, theCrit ) + getBadRate( &tr2, theCrit );
1433 SMDS_FaceOfNodes tr3 ( aNodes[1], aNodes[2], aNodes[3] );
1434 SMDS_FaceOfNodes tr4 ( aNodes[3], aNodes[0], aNodes[1] );
1435 aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit );
1437 const int aShapeId = FindShape( elem );
1438 const SMDS_MeshElement* newElem1 = 0;
1439 const SMDS_MeshElement* newElem2 = 0;
1441 if ( !elem->IsQuadratic() ) // split liner quadrangle
1443 // for MaxElementLength2D functor we return minimum diagonal for splitting,
1444 // because aBadRate1=2*len(diagonal 1-3); aBadRate2=2*len(diagonal 2-4)
1445 if ( aBadRate1 <= aBadRate2 ) {
1446 // tr1 + tr2 is better
1447 newElem1 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
1448 newElem2 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
1451 // tr3 + tr4 is better
1452 newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
1453 newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
1456 else // split quadratic quadrangle
1458 helper.SetIsQuadratic( true );
1459 helper.SetIsBiQuadratic( aNodes.size() == 9 );
1461 helper.AddTLinks( static_cast< const SMDS_MeshFace* >( elem ));
1462 if ( aNodes.size() == 9 )
1464 helper.SetIsBiQuadratic( true );
1465 if ( aBadRate1 <= aBadRate2 )
1466 helper.AddTLinkNode( aNodes[0], aNodes[2], aNodes[8] );
1468 helper.AddTLinkNode( aNodes[1], aNodes[3], aNodes[8] );
1470 // create a new element
1471 if ( aBadRate1 <= aBadRate2 ) {
1472 newElem1 = helper.AddFace( aNodes[2], aNodes[3], aNodes[0] );
1473 newElem2 = helper.AddFace( aNodes[2], aNodes[0], aNodes[1] );
1476 newElem1 = helper.AddFace( aNodes[3], aNodes[0], aNodes[1] );
1477 newElem2 = helper.AddFace( aNodes[3], aNodes[1], aNodes[2] );
1481 // care of a new element
1483 myLastCreatedElems.Append(newElem1);
1484 myLastCreatedElems.Append(newElem2);
1485 AddToSameGroups( newElem1, elem, aMesh );
1486 AddToSameGroups( newElem2, elem, aMesh );
1488 // put a new triangle on the same shape
1490 aMesh->SetMeshElementOnShape( newElem1, aShapeId );
1491 aMesh->SetMeshElementOnShape( newElem2, aShapeId );
1493 aMesh->RemoveElement( elem );
1498 //=======================================================================
1500 * \brief Split each of given quadrangles into 4 triangles.
1501 * \param theElems - The faces to be splitted. If empty all faces are split.
1503 //=======================================================================
1505 void SMESH_MeshEditor::QuadTo4Tri (TIDSortedElemSet & theElems)
1507 myLastCreatedElems.Clear();
1508 myLastCreatedNodes.Clear();
1510 SMESH_MesherHelper helper( *GetMesh() );
1511 helper.SetElementsOnShape( true );
1513 SMDS_ElemIteratorPtr faceIt;
1514 if ( theElems.empty() ) faceIt = GetMeshDS()->elementsIterator(SMDSAbs_Face);
1515 else faceIt = elemSetIterator( theElems );
1518 gp_XY uv [9]; uv[8] = gp_XY(0,0);
1520 vector< const SMDS_MeshNode* > nodes;
1521 SMESHDS_SubMesh* subMeshDS = 0;
1523 Handle(Geom_Surface) surface;
1524 TopLoc_Location loc;
1526 while ( faceIt->more() )
1528 const SMDS_MeshElement* quad = faceIt->next();
1529 if ( !quad || quad->NbCornerNodes() != 4 )
1532 // get a surface the quad is on
1534 if ( quad->getshapeId() < 1 )
1537 helper.SetSubShape( 0 );
1540 else if ( quad->getshapeId() != helper.GetSubShapeID() )
1542 helper.SetSubShape( quad->getshapeId() );
1543 if ( !helper.GetSubShape().IsNull() &&
1544 helper.GetSubShape().ShapeType() == TopAbs_FACE )
1546 F = TopoDS::Face( helper.GetSubShape() );
1547 surface = BRep_Tool::Surface( F, loc );
1548 subMeshDS = GetMeshDS()->MeshElements( quad->getshapeId() );
1552 helper.SetSubShape( 0 );
1557 // create a central node
1559 const SMDS_MeshNode* nCentral;
1560 nodes.assign( quad->begin_nodes(), quad->end_nodes() );
1562 if ( nodes.size() == 9 )
1564 nCentral = nodes.back();
1571 for ( ; iN < nodes.size(); ++iN )
1572 xyz[ iN ] = SMESH_TNodeXYZ( nodes[ iN ] );
1574 for ( ; iN < 8; ++iN ) // mid-side points of a linear qudrangle
1575 xyz[ iN ] = 0.5 * ( xyz[ iN - 4 ] + xyz[( iN - 3 )%4 ] );
1577 xyz[ 8 ] = helper.calcTFI( 0.5, 0.5,
1578 xyz[0], xyz[1], xyz[2], xyz[3],
1579 xyz[4], xyz[5], xyz[6], xyz[7] );
1583 for ( ; iN < nodes.size(); ++iN )
1584 uv[ iN ] = helper.GetNodeUV( F, nodes[iN], nodes[(iN+2)%4], &checkUV );
1586 for ( ; iN < 8; ++iN ) // UV of mid-side points of a linear qudrangle
1587 uv[ iN ] = helper.GetMiddleUV( surface, uv[ iN - 4 ], uv[( iN - 3 )%4 ] );
1589 uv[ 8 ] = helper.calcTFI( 0.5, 0.5,
1590 uv[0], uv[1], uv[2], uv[3],
1591 uv[4], uv[5], uv[6], uv[7] );
1593 gp_Pnt p = surface->Value( uv[8].X(), uv[8].Y() ).Transformed( loc );
1597 nCentral = helper.AddNode( xyz[8].X(), xyz[8].Y(), xyz[8].Z(), /*id=*/0,
1598 uv[8].X(), uv[8].Y() );
1599 myLastCreatedNodes.Append( nCentral );
1602 // create 4 triangles
1604 helper.SetIsQuadratic ( nodes.size() > 4 );
1605 helper.SetIsBiQuadratic( nodes.size() == 9 );
1606 if ( helper.GetIsQuadratic() )
1607 helper.AddTLinks( static_cast< const SMDS_MeshFace*>( quad ));
1609 GetMeshDS()->RemoveFreeElement( quad, subMeshDS, /*fromGroups=*/false );
1611 for ( int i = 0; i < 4; ++i )
1613 SMDS_MeshElement* tria = helper.AddFace( nodes[ i ],
1616 ReplaceElemInGroups( tria, quad, GetMeshDS() );
1617 myLastCreatedElems.Append( tria );
1622 //=======================================================================
1623 //function : BestSplit
1624 //purpose : Find better diagonal for cutting.
1625 //=======================================================================
1627 int SMESH_MeshEditor::BestSplit (const SMDS_MeshElement* theQuad,
1628 SMESH::Controls::NumericalFunctorPtr theCrit)
1630 myLastCreatedElems.Clear();
1631 myLastCreatedNodes.Clear();
1636 if (!theQuad || theQuad->GetType() != SMDSAbs_Face )
1639 if( theQuad->NbNodes()==4 ||
1640 (theQuad->NbNodes()==8 && theQuad->IsQuadratic()) ) {
1642 // retrieve element nodes
1643 const SMDS_MeshNode* aNodes [4];
1644 SMDS_ElemIteratorPtr itN = theQuad->nodesIterator();
1646 //while (itN->more())
1648 aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
1650 // compare two sets of possible triangles
1651 double aBadRate1, aBadRate2; // to what extent a set is bad
1652 SMDS_FaceOfNodes tr1 ( aNodes[0], aNodes[1], aNodes[2] );
1653 SMDS_FaceOfNodes tr2 ( aNodes[2], aNodes[3], aNodes[0] );
1654 aBadRate1 = getBadRate( &tr1, theCrit ) + getBadRate( &tr2, theCrit );
1656 SMDS_FaceOfNodes tr3 ( aNodes[1], aNodes[2], aNodes[3] );
1657 SMDS_FaceOfNodes tr4 ( aNodes[3], aNodes[0], aNodes[1] );
1658 aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit );
1659 // for MaxElementLength2D functor we return minimum diagonal for splitting,
1660 // because aBadRate1=2*len(diagonal 1-3); aBadRate2=2*len(diagonal 2-4)
1661 if (aBadRate1 <= aBadRate2) // tr1 + tr2 is better
1662 return 1; // diagonal 1-3
1664 return 2; // diagonal 2-4
1671 // Methods of splitting volumes into tetra
1673 const int theHexTo5_1[5*4+1] =
1675 0, 1, 2, 5, 0, 4, 5, 7, 0, 2, 3, 7, 2, 5, 6, 7, 0, 5, 2, 7, -1
1677 const int theHexTo5_2[5*4+1] =
1679 1, 2, 3, 6, 1, 4, 5, 6, 0, 1, 3, 4, 3, 4, 6, 7, 1, 3, 4, 6, -1
1681 const int* theHexTo5[2] = { theHexTo5_1, theHexTo5_2 };
1683 const int theHexTo6_1[6*4+1] =
1685 1, 5, 6, 0, 0, 1, 2, 6, 0, 4, 5, 6, 0, 4, 6, 7, 0, 2, 3, 6, 0, 3, 7, 6, -1
1687 const int theHexTo6_2[6*4+1] =
1689 2, 6, 7, 1, 1, 2, 3, 7, 1, 5, 6, 7, 1, 5, 7, 4, 1, 3, 0, 7, 1, 0, 4, 7, -1
1691 const int theHexTo6_3[6*4+1] =
1693 3, 7, 4, 2, 2, 3, 0, 4, 2, 6, 7, 4, 2, 6, 4, 5, 2, 0, 1, 4, 2, 1, 5, 4, -1
1695 const int theHexTo6_4[6*4+1] =
1697 0, 4, 5, 3, 3, 0, 1, 5, 3, 7, 4, 5, 3, 7, 5, 6, 3, 1, 2, 5, 3, 2, 6, 5, -1
1699 const int* theHexTo6[4] = { theHexTo6_1, theHexTo6_2, theHexTo6_3, theHexTo6_4 };
1701 const int thePyraTo2_1[2*4+1] =
1703 0, 1, 2, 4, 0, 2, 3, 4, -1
1705 const int thePyraTo2_2[2*4+1] =
1707 1, 2, 3, 4, 1, 3, 0, 4, -1
1709 const int* thePyraTo2[2] = { thePyraTo2_1, thePyraTo2_2 };
1711 const int thePentaTo3_1[3*4+1] =
1713 0, 1, 2, 3, 1, 3, 4, 2, 2, 3, 4, 5, -1
1715 const int thePentaTo3_2[3*4+1] =
1717 1, 2, 0, 4, 2, 4, 5, 0, 0, 4, 5, 3, -1
1719 const int thePentaTo3_3[3*4+1] =
1721 2, 0, 1, 5, 0, 5, 3, 1, 1, 5, 3, 4, -1
1723 const int thePentaTo3_4[3*4+1] =
1725 0, 1, 2, 3, 1, 3, 4, 5, 2, 3, 1, 5, -1
1727 const int thePentaTo3_5[3*4+1] =
1729 1, 2, 0, 4, 2, 4, 5, 3, 0, 4, 2, 3, -1
1731 const int thePentaTo3_6[3*4+1] =
1733 2, 0, 1, 5, 0, 5, 3, 4, 1, 5, 0, 4, -1
1735 const int* thePentaTo3[6] = { thePentaTo3_1, thePentaTo3_2, thePentaTo3_3,
1736 thePentaTo3_4, thePentaTo3_5, thePentaTo3_6 };
1738 // Methods of splitting hexahedron into prisms
1740 const int theHexTo4Prisms_BT[6*4+1] = // bottom-top
1742 0, 1, 8, 4, 5, 9, 1, 2, 8, 5, 6, 9, 2, 3, 8, 6, 7, 9, 3, 0, 8, 7, 4, 9, -1
1744 const int theHexTo4Prisms_LR[6*4+1] = // left-right
1746 1, 0, 8, 2, 3, 9, 0, 4, 8, 3, 7, 9, 4, 5, 8, 7, 6, 9, 5, 1, 8, 6, 2, 9, -1
1748 const int theHexTo4Prisms_FB[6*4+1] = // front-back
1750 0, 3, 9, 1, 2, 8, 3, 7, 9, 2, 6, 8, 7, 4, 9, 6, 5, 8, 4, 0, 9, 5, 1, 8, -1
1753 const int theHexTo2Prisms_BT_1[6*2+1] =
1755 0, 1, 3, 4, 5, 7, 1, 2, 3, 5, 6, 7, -1
1757 const int theHexTo2Prisms_BT_2[6*2+1] =
1759 0, 1, 2, 4, 5, 6, 0, 2, 3, 4, 6, 7, -1
1761 const int* theHexTo2Prisms_BT[2] = { theHexTo2Prisms_BT_1, theHexTo2Prisms_BT_2 };
1763 const int theHexTo2Prisms_LR_1[6*2+1] =
1765 1, 0, 4, 2, 3, 7, 1, 4, 5, 2, 7, 6, -1
1767 const int theHexTo2Prisms_LR_2[6*2+1] =
1769 1, 0, 4, 2, 3, 7, 1, 4, 5, 2, 7, 6, -1
1771 const int* theHexTo2Prisms_LR[2] = { theHexTo2Prisms_LR_1, theHexTo2Prisms_LR_2 };
1773 const int theHexTo2Prisms_FB_1[6*2+1] =
1775 0, 3, 4, 1, 2, 5, 3, 7, 4, 2, 6, 5, -1
1777 const int theHexTo2Prisms_FB_2[6*2+1] =
1779 0, 3, 7, 1, 2, 7, 0, 7, 4, 1, 6, 5, -1
1781 const int* theHexTo2Prisms_FB[2] = { theHexTo2Prisms_FB_1, theHexTo2Prisms_FB_2 };
1784 struct TTriangleFacet //!< stores indices of three nodes of tetra facet
1787 TTriangleFacet(int n1, int n2, int n3): _n1(n1), _n2(n2), _n3(n3) {}
1788 bool contains(int n) const { return ( n == _n1 || n == _n2 || n == _n3 ); }
1789 bool hasAdjacentVol( const SMDS_MeshElement* elem,
1790 const SMDSAbs_GeometryType geom = SMDSGeom_TETRA) const;
1796 const int* _connectivity; //!< foursomes of tetra connectivy finished by -1
1797 bool _baryNode; //!< additional node is to be created at cell barycenter
1798 bool _ownConn; //!< to delete _connectivity in destructor
1799 map<int, const SMDS_MeshNode*> _faceBaryNode; //!< map face index to node at BC of face
1801 TSplitMethod( int nbTet=0, const int* conn=0, bool addNode=false)
1802 : _nbSplits(nbTet), _nbCorners(4), _connectivity(conn), _baryNode(addNode), _ownConn(false) {}
1803 ~TSplitMethod() { if ( _ownConn ) delete [] _connectivity; _connectivity = 0; }
1804 bool hasFacet( const TTriangleFacet& facet ) const
1806 if ( _nbCorners == 4 )
1808 const int* tetConn = _connectivity;
1809 for ( ; tetConn[0] >= 0; tetConn += 4 )
1810 if (( facet.contains( tetConn[0] ) +
1811 facet.contains( tetConn[1] ) +
1812 facet.contains( tetConn[2] ) +
1813 facet.contains( tetConn[3] )) == 3 )
1816 else // prism, _nbCorners == 6
1818 const int* prismConn = _connectivity;
1819 for ( ; prismConn[0] >= 0; prismConn += 6 )
1821 if (( facet.contains( prismConn[0] ) &&
1822 facet.contains( prismConn[1] ) &&
1823 facet.contains( prismConn[2] ))
1825 ( facet.contains( prismConn[3] ) &&
1826 facet.contains( prismConn[4] ) &&
1827 facet.contains( prismConn[5] )))
1835 //=======================================================================
1837 * \brief return TSplitMethod for the given element to split into tetrahedra
1839 //=======================================================================
1841 TSplitMethod getTetraSplitMethod( SMDS_VolumeTool& vol, const int theMethodFlags)
1843 const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
1845 // at HEXA_TO_24 method, each face of volume is split into triangles each based on
1846 // an edge and a face barycenter; tertaherdons are based on triangles and
1847 // a volume barycenter
1848 const bool is24TetMode = ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_24 );
1850 // Find out how adjacent volumes are split
1852 vector < list< TTriangleFacet > > triaSplitsByFace( vol.NbFaces() ); // splits of each side
1853 int hasAdjacentSplits = 0, maxTetConnSize = 0;
1854 for ( int iF = 0; iF < vol.NbFaces(); ++iF )
1856 int nbNodes = vol.NbFaceNodes( iF ) / iQ;
1857 maxTetConnSize += 4 * ( nbNodes - (is24TetMode ? 0 : 2));
1858 if ( nbNodes < 4 ) continue;
1860 list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
1861 const int* nInd = vol.GetFaceNodesIndices( iF );
1864 TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
1865 TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
1866 if ( t012.hasAdjacentVol( vol.Element() )) triaSplits.push_back( t012 );
1867 else if ( t123.hasAdjacentVol( vol.Element() )) triaSplits.push_back( t123 );
1871 int iCom = 0; // common node of triangle faces to split into
1872 for ( int iVar = 0; iVar < nbNodes; ++iVar, ++iCom )
1874 TTriangleFacet t012( nInd[ iQ * ( iCom )],
1875 nInd[ iQ * ( (iCom+1)%nbNodes )],
1876 nInd[ iQ * ( (iCom+2)%nbNodes )]);
1877 TTriangleFacet t023( nInd[ iQ * ( iCom )],
1878 nInd[ iQ * ( (iCom+2)%nbNodes )],
1879 nInd[ iQ * ( (iCom+3)%nbNodes )]);
1880 if ( t012.hasAdjacentVol( vol.Element() ) && t023.hasAdjacentVol( vol.Element() ))
1882 triaSplits.push_back( t012 );
1883 triaSplits.push_back( t023 );
1888 if ( !triaSplits.empty() )
1889 hasAdjacentSplits = true;
1892 // Among variants of split method select one compliant with adjacent volumes
1894 TSplitMethod method;
1895 if ( !vol.Element()->IsPoly() && !is24TetMode )
1897 int nbVariants = 2, nbTet = 0;
1898 const int** connVariants = 0;
1899 switch ( vol.Element()->GetEntityType() )
1901 case SMDSEntity_Hexa:
1902 case SMDSEntity_Quad_Hexa:
1903 case SMDSEntity_TriQuad_Hexa:
1904 if ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_5 )
1905 connVariants = theHexTo5, nbTet = 5;
1907 connVariants = theHexTo6, nbTet = 6, nbVariants = 4;
1909 case SMDSEntity_Pyramid:
1910 case SMDSEntity_Quad_Pyramid:
1911 connVariants = thePyraTo2; nbTet = 2;
1913 case SMDSEntity_Penta:
1914 case SMDSEntity_Quad_Penta:
1915 case SMDSEntity_BiQuad_Penta:
1916 connVariants = thePentaTo3; nbTet = 3; nbVariants = 6;
1921 for ( int variant = 0; variant < nbVariants && method._nbSplits == 0; ++variant )
1923 // check method compliancy with adjacent tetras,
1924 // all found splits must be among facets of tetras described by this method
1925 method = TSplitMethod( nbTet, connVariants[variant] );
1926 if ( hasAdjacentSplits && method._nbSplits > 0 )
1928 bool facetCreated = true;
1929 for ( size_t iF = 0; facetCreated && iF < triaSplitsByFace.size(); ++iF )
1931 list< TTriangleFacet >::const_iterator facet = triaSplitsByFace[iF].begin();
1932 for ( ; facetCreated && facet != triaSplitsByFace[iF].end(); ++facet )
1933 facetCreated = method.hasFacet( *facet );
1935 if ( !facetCreated )
1936 method = TSplitMethod(0); // incompatible method
1940 if ( method._nbSplits < 1 )
1942 // No standard method is applicable, use a generic solution:
1943 // each facet of a volume is split into triangles and
1944 // each of triangles and a volume barycenter form a tetrahedron.
1946 const bool isHex27 = ( vol.Element()->GetEntityType() == SMDSEntity_TriQuad_Hexa );
1948 int* connectivity = new int[ maxTetConnSize + 1 ];
1949 method._connectivity = connectivity;
1950 method._ownConn = true;
1951 method._baryNode = !isHex27; // to create central node or not
1954 int baryCenInd = vol.NbNodes() - int( isHex27 );
1955 for ( int iF = 0; iF < vol.NbFaces(); ++iF )
1957 const int nbNodes = vol.NbFaceNodes( iF ) / iQ;
1958 const int* nInd = vol.GetFaceNodesIndices( iF );
1959 // find common node of triangle facets of tetra to create
1960 int iCommon = 0; // index in linear numeration
1961 const list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
1962 if ( !triaSplits.empty() )
1965 const TTriangleFacet* facet = &triaSplits.front();
1966 for ( ; iCommon < nbNodes-1 ; ++iCommon )
1967 if ( facet->contains( nInd[ iQ * iCommon ]) &&
1968 facet->contains( nInd[ iQ * ((iCommon+2)%nbNodes) ]))
1971 else if ( nbNodes > 3 && !is24TetMode )
1973 // find the best method of splitting into triangles by aspect ratio
1974 SMESH::Controls::NumericalFunctorPtr aspectRatio( new SMESH::Controls::AspectRatio);
1975 map< double, int > badness2iCommon;
1976 const SMDS_MeshNode** nodes = vol.GetFaceNodes( iF );
1977 int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
1978 for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCommon )
1981 for ( int iLast = iCommon+2; iLast < iCommon+nbNodes; ++iLast )
1983 SMDS_FaceOfNodes tria ( nodes[ iQ*( iCommon )],
1984 nodes[ iQ*((iLast-1)%nbNodes)],
1985 nodes[ iQ*((iLast )%nbNodes)]);
1986 badness += getBadRate( &tria, aspectRatio );
1988 badness2iCommon.insert( make_pair( badness, iCommon ));
1990 // use iCommon with lowest badness
1991 iCommon = badness2iCommon.begin()->second;
1993 if ( iCommon >= nbNodes )
1994 iCommon = 0; // something wrong
1996 // fill connectivity of tetrahedra based on a current face
1997 int nbTet = nbNodes - 2;
1998 if ( is24TetMode && nbNodes > 3 && triaSplits.empty())
2003 faceBaryCenInd = vol.GetCenterNodeIndex( iF );
2004 method._faceBaryNode[ iF ] = vol.GetNodes()[ faceBaryCenInd ];
2008 method._faceBaryNode[ iF ] = 0;
2009 faceBaryCenInd = baryCenInd + method._faceBaryNode.size();
2012 for ( int i = 0; i < nbTet; ++i )
2014 int i1 = i, i2 = (i+1) % nbNodes;
2015 if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
2016 connectivity[ connSize++ ] = nInd[ iQ * i1 ];
2017 connectivity[ connSize++ ] = nInd[ iQ * i2 ];
2018 connectivity[ connSize++ ] = faceBaryCenInd;
2019 connectivity[ connSize++ ] = baryCenInd;
2024 for ( int i = 0; i < nbTet; ++i )
2026 int i1 = (iCommon+1+i) % nbNodes, i2 = (iCommon+2+i) % nbNodes;
2027 if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
2028 connectivity[ connSize++ ] = nInd[ iQ * iCommon ];
2029 connectivity[ connSize++ ] = nInd[ iQ * i1 ];
2030 connectivity[ connSize++ ] = nInd[ iQ * i2 ];
2031 connectivity[ connSize++ ] = baryCenInd;
2034 method._nbSplits += nbTet;
2036 } // loop on volume faces
2038 connectivity[ connSize++ ] = -1;
2040 } // end of generic solution
2044 //=======================================================================
2046 * \brief return TSplitMethod to split haxhedron into prisms
2048 //=======================================================================
2050 TSplitMethod getPrismSplitMethod( SMDS_VolumeTool& vol,
2051 const int methodFlags,
2052 const int facetToSplit)
2054 // order of facets in HEX according to SMDS_VolumeTool::Hexa_F :
2056 const int iF = ( facetToSplit < 2 ) ? 0 : 1 + ( facetToSplit-2 ) % 2; // [0,1,2]
2058 if ( methodFlags == SMESH_MeshEditor::HEXA_TO_4_PRISMS )
2060 static TSplitMethod to4methods[4]; // order BT, LR, FB
2061 if ( to4methods[iF]._nbSplits == 0 )
2065 to4methods[iF]._connectivity = theHexTo4Prisms_BT;
2066 to4methods[iF]._faceBaryNode[ 0 ] = 0;
2067 to4methods[iF]._faceBaryNode[ 1 ] = 0;
2070 to4methods[iF]._connectivity = theHexTo4Prisms_LR;
2071 to4methods[iF]._faceBaryNode[ 2 ] = 0;
2072 to4methods[iF]._faceBaryNode[ 4 ] = 0;
2075 to4methods[iF]._connectivity = theHexTo4Prisms_FB;
2076 to4methods[iF]._faceBaryNode[ 3 ] = 0;
2077 to4methods[iF]._faceBaryNode[ 5 ] = 0;
2079 default: return to4methods[3];
2081 to4methods[iF]._nbSplits = 4;
2082 to4methods[iF]._nbCorners = 6;
2084 return to4methods[iF];
2086 // else if ( methodFlags == HEXA_TO_2_PRISMS )
2088 TSplitMethod method;
2090 const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
2092 const int nbVariants = 2, nbSplits = 2;
2093 const int** connVariants = 0;
2095 case 0: connVariants = theHexTo2Prisms_BT; break;
2096 case 1: connVariants = theHexTo2Prisms_LR; break;
2097 case 2: connVariants = theHexTo2Prisms_FB; break;
2098 default: return method;
2101 // look for prisms adjacent via facetToSplit and an opposite one
2102 for ( int is2nd = 0; is2nd < 2; ++is2nd )
2104 int iFacet = is2nd ? vol.GetOppFaceIndexOfHex( facetToSplit ) : facetToSplit;
2105 int nbNodes = vol.NbFaceNodes( iFacet ) / iQ;
2106 if ( nbNodes != 4 ) return method;
2108 const int* nInd = vol.GetFaceNodesIndices( iFacet );
2109 TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
2110 TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
2112 if ( t012.hasAdjacentVol( vol.Element(), SMDSGeom_PENTA ))
2114 else if ( t123.hasAdjacentVol( vol.Element(), SMDSGeom_PENTA ))
2119 // there are adjacent prism
2120 for ( int variant = 0; variant < nbVariants; ++variant )
2122 // check method compliancy with adjacent prisms,
2123 // the found prism facets must be among facets of prisms described by current method
2124 method._nbSplits = nbSplits;
2125 method._nbCorners = 6;
2126 method._connectivity = connVariants[ variant ];
2127 if ( method.hasFacet( *t ))
2132 // No adjacent prisms. Select a variant with a best aspect ratio.
2134 double badness[2] = { 0., 0. };
2135 static SMESH::Controls::NumericalFunctorPtr aspectRatio( new SMESH::Controls::AspectRatio);
2136 const SMDS_MeshNode** nodes = vol.GetNodes();
2137 for ( int variant = 0; variant < nbVariants; ++variant )
2138 for ( int is2nd = 0; is2nd < 2; ++is2nd )
2140 int iFacet = is2nd ? vol.GetOppFaceIndexOfHex( facetToSplit ) : facetToSplit;
2141 const int* nInd = vol.GetFaceNodesIndices( iFacet );
2143 method._connectivity = connVariants[ variant ];
2144 TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
2145 TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
2146 TTriangleFacet* t = ( method.hasFacet( t012 )) ? & t012 : & t123;
2148 SMDS_FaceOfNodes tria ( nodes[ t->_n1 ],
2151 badness[ variant ] += getBadRate( &tria, aspectRatio );
2153 const int iBetter = ( badness[1] < badness[0] && badness[0]-badness[1] > 0.1 * badness[0] );
2155 method._nbSplits = nbSplits;
2156 method._nbCorners = 6;
2157 method._connectivity = connVariants[ iBetter ];
2162 //================================================================================
2164 * \brief Check if there is a tetraherdon adjacent to the given element via this facet
2166 //================================================================================
2168 bool TTriangleFacet::hasAdjacentVol( const SMDS_MeshElement* elem,
2169 const SMDSAbs_GeometryType geom ) const
2171 // find the tetrahedron including the three nodes of facet
2172 const SMDS_MeshNode* n1 = elem->GetNode(_n1);
2173 const SMDS_MeshNode* n2 = elem->GetNode(_n2);
2174 const SMDS_MeshNode* n3 = elem->GetNode(_n3);
2175 SMDS_ElemIteratorPtr volIt1 = n1->GetInverseElementIterator(SMDSAbs_Volume);
2176 while ( volIt1->more() )
2178 const SMDS_MeshElement* v = volIt1->next();
2179 if ( v->GetGeomType() != geom )
2181 const int lastCornerInd = v->NbCornerNodes() - 1;
2182 if ( v->IsQuadratic() && v->GetNodeIndex( n1 ) > lastCornerInd )
2183 continue; // medium node not allowed
2184 const int ind2 = v->GetNodeIndex( n2 );
2185 if ( ind2 < 0 || lastCornerInd < ind2 )
2187 const int ind3 = v->GetNodeIndex( n3 );
2188 if ( ind3 < 0 || lastCornerInd < ind3 )
2195 //=======================================================================
2197 * \brief A key of a face of volume
2199 //=======================================================================
2201 struct TVolumeFaceKey: pair< pair< int, int>, pair< int, int> >
2203 TVolumeFaceKey( SMDS_VolumeTool& vol, int iF )
2205 TIDSortedNodeSet sortedNodes;
2206 const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
2207 int nbNodes = vol.NbFaceNodes( iF );
2208 const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iF );
2209 for ( int i = 0; i < nbNodes; i += iQ )
2210 sortedNodes.insert( fNodes[i] );
2211 TIDSortedNodeSet::iterator n = sortedNodes.begin();
2212 first.first = (*(n++))->GetID();
2213 first.second = (*(n++))->GetID();
2214 second.first = (*(n++))->GetID();
2215 second.second = ( sortedNodes.size() > 3 ) ? (*(n++))->GetID() : 0;
2220 //=======================================================================
2221 //function : SplitVolumes
2222 //purpose : Split volume elements into tetrahedra or prisms.
2223 // If facet ID < 0, element is split into tetrahedra,
2224 // else a hexahedron is split into prisms so that the given facet is
2225 // split into triangles
2226 //=======================================================================
2228 void SMESH_MeshEditor::SplitVolumes (const TFacetOfElem & theElems,
2229 const int theMethodFlags)
2231 SMDS_VolumeTool volTool;
2232 SMESH_MesherHelper helper( *GetMesh()), fHelper(*GetMesh());
2233 fHelper.ToFixNodeParameters( true );
2235 SMESHDS_SubMesh* subMesh = 0;//GetMeshDS()->MeshElements(1);
2236 SMESHDS_SubMesh* fSubMesh = 0;//subMesh;
2238 SMESH_SequenceOfElemPtr newNodes, newElems;
2240 // map face of volume to it's baricenrtic node
2241 map< TVolumeFaceKey, const SMDS_MeshNode* > volFace2BaryNode;
2243 vector<const SMDS_MeshElement* > splitVols;
2245 TFacetOfElem::const_iterator elem2facet = theElems.begin();
2246 for ( ; elem2facet != theElems.end(); ++elem2facet )
2248 const SMDS_MeshElement* elem = elem2facet->first;
2249 const int facetToSplit = elem2facet->second;
2250 if ( elem->GetType() != SMDSAbs_Volume )
2252 const SMDSAbs_EntityType geomType = elem->GetEntityType();
2253 if ( geomType == SMDSEntity_Tetra || geomType == SMDSEntity_Quad_Tetra )
2256 if ( !volTool.Set( elem, /*ignoreCentralNodes=*/false )) continue; // strange...
2258 TSplitMethod splitMethod = ( facetToSplit < 0 ?
2259 getTetraSplitMethod( volTool, theMethodFlags ) :
2260 getPrismSplitMethod( volTool, theMethodFlags, facetToSplit ));
2261 if ( splitMethod._nbSplits < 1 ) continue;
2263 // find submesh to add new tetras to
2264 if ( !subMesh || !subMesh->Contains( elem ))
2266 int shapeID = FindShape( elem );
2267 helper.SetSubShape( shapeID ); // helper will add tetras to the found submesh
2268 subMesh = GetMeshDS()->MeshElements( shapeID );
2271 if ( elem->IsQuadratic() )
2274 // add quadratic links to the helper
2275 for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
2277 const SMDS_MeshNode** fNodes = volTool.GetFaceNodes( iF );
2278 int nbN = volTool.NbFaceNodes( iF ) - bool( volTool.GetCenterNodeIndex(iF) > 0 );
2279 for ( int iN = 0; iN < nbN; iN += iQ )
2280 helper.AddTLinkNode( fNodes[iN], fNodes[iN+2], fNodes[iN+1] );
2282 helper.SetIsQuadratic( true );
2287 helper.SetIsQuadratic( false );
2289 vector<const SMDS_MeshNode*> nodes( volTool.GetNodes(),
2290 volTool.GetNodes() + elem->NbNodes() );
2291 helper.SetElementsOnShape( true );
2292 if ( splitMethod._baryNode )
2294 // make a node at barycenter
2295 volTool.GetBaryCenter( bc[0], bc[1], bc[2] );
2296 SMDS_MeshNode* gcNode = helper.AddNode( bc[0], bc[1], bc[2] );
2297 nodes.push_back( gcNode );
2298 newNodes.Append( gcNode );
2300 if ( !splitMethod._faceBaryNode.empty() )
2302 // make or find baricentric nodes of faces
2303 map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.begin();
2304 for ( ; iF_n != splitMethod._faceBaryNode.end(); ++iF_n )
2306 map< TVolumeFaceKey, const SMDS_MeshNode* >::iterator f_n =
2307 volFace2BaryNode.insert
2308 ( make_pair( TVolumeFaceKey( volTool,iF_n->first ), iF_n->second )).first;
2311 volTool.GetFaceBaryCenter( iF_n->first, bc[0], bc[1], bc[2] );
2312 newNodes.Append( f_n->second = helper.AddNode( bc[0], bc[1], bc[2] ));
2314 nodes.push_back( iF_n->second = f_n->second );
2319 splitVols.resize( splitMethod._nbSplits ); // splits of a volume
2320 const int* volConn = splitMethod._connectivity;
2321 if ( splitMethod._nbCorners == 4 ) // tetra
2322 for ( int i = 0; i < splitMethod._nbSplits; ++i, volConn += splitMethod._nbCorners )
2323 newElems.Append( splitVols[ i ] = helper.AddVolume( nodes[ volConn[0] ],
2324 nodes[ volConn[1] ],
2325 nodes[ volConn[2] ],
2326 nodes[ volConn[3] ]));
2328 for ( int i = 0; i < splitMethod._nbSplits; ++i, volConn += splitMethod._nbCorners )
2329 newElems.Append( splitVols[ i ] = helper.AddVolume( nodes[ volConn[0] ],
2330 nodes[ volConn[1] ],
2331 nodes[ volConn[2] ],
2332 nodes[ volConn[3] ],
2333 nodes[ volConn[4] ],
2334 nodes[ volConn[5] ]));
2336 ReplaceElemInGroups( elem, splitVols, GetMeshDS() );
2338 // Split faces on sides of the split volume
2340 const SMDS_MeshNode** volNodes = volTool.GetNodes();
2341 for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
2343 const int nbNodes = volTool.NbFaceNodes( iF ) / iQ;
2344 if ( nbNodes < 4 ) continue;
2346 // find an existing face
2347 vector<const SMDS_MeshNode*> fNodes( volTool.GetFaceNodes( iF ),
2348 volTool.GetFaceNodes( iF ) + volTool.NbFaceNodes( iF ));
2349 while ( const SMDS_MeshElement* face = GetMeshDS()->FindElement( fNodes, SMDSAbs_Face,
2350 /*noMedium=*/false))
2353 helper.SetElementsOnShape( false );
2354 vector< const SMDS_MeshElement* > triangles;
2356 // find submesh to add new triangles in
2357 if ( !fSubMesh || !fSubMesh->Contains( face ))
2359 int shapeID = FindShape( face );
2360 fSubMesh = GetMeshDS()->MeshElements( shapeID );
2362 map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.find(iF);
2363 if ( iF_n != splitMethod._faceBaryNode.end() )
2365 const SMDS_MeshNode *baryNode = iF_n->second;
2366 for ( int iN = 0; iN < nbNodes*iQ; iN += iQ )
2368 const SMDS_MeshNode* n1 = fNodes[iN];
2369 const SMDS_MeshNode *n2 = fNodes[(iN+iQ)%(nbNodes*iQ)];
2370 const SMDS_MeshNode *n3 = baryNode;
2371 if ( !volTool.IsFaceExternal( iF ))
2373 triangles.push_back( helper.AddFace( n1,n2,n3 ));
2375 if ( fSubMesh ) // update position of the bary node on geometry
2378 subMesh->RemoveNode( baryNode, false );
2379 GetMeshDS()->SetNodeOnFace( baryNode, fSubMesh->GetID() );
2380 const TopoDS_Shape& s = GetMeshDS()->IndexToShape( fSubMesh->GetID() );
2381 if ( !s.IsNull() && s.ShapeType() == TopAbs_FACE )
2383 fHelper.SetSubShape( s );
2384 gp_XY uv( 1e100, 1e100 );
2386 if ( !fHelper.CheckNodeUV( TopoDS::Face( s ), baryNode,
2387 uv, /*tol=*/1e-7, /*force=*/true, distXYZ ) &&
2390 // node is too far from the surface
2391 GetMeshDS()->MoveNode( baryNode, distXYZ[1], distXYZ[2], distXYZ[3] );
2392 const_cast<SMDS_MeshNode*>( baryNode )->SetPosition
2393 ( SMDS_PositionPtr( new SMDS_FacePosition( uv.X(), uv.Y() )));
2400 // among possible triangles create ones described by split method
2401 const int* nInd = volTool.GetFaceNodesIndices( iF );
2402 int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
2403 int iCom = 0; // common node of triangle faces to split into
2404 list< TTriangleFacet > facets;
2405 for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCom )
2407 TTriangleFacet t012( nInd[ iQ * ( iCom )],
2408 nInd[ iQ * ( (iCom+1)%nbNodes )],
2409 nInd[ iQ * ( (iCom+2)%nbNodes )]);
2410 TTriangleFacet t023( nInd[ iQ * ( iCom )],
2411 nInd[ iQ * ( (iCom+2)%nbNodes )],
2412 nInd[ iQ * ( (iCom+3)%nbNodes )]);
2413 if ( splitMethod.hasFacet( t012 ) && splitMethod.hasFacet( t023 ))
2415 facets.push_back( t012 );
2416 facets.push_back( t023 );
2417 for ( int iLast = iCom+4; iLast < iCom+nbNodes; ++iLast )
2418 facets.push_back( TTriangleFacet( nInd[ iQ * ( iCom )],
2419 nInd[ iQ * ((iLast-1)%nbNodes )],
2420 nInd[ iQ * ((iLast )%nbNodes )]));
2424 list< TTriangleFacet >::iterator facet = facets.begin();
2425 if ( facet == facets.end() )
2427 for ( ; facet != facets.end(); ++facet )
2429 if ( !volTool.IsFaceExternal( iF ))
2430 swap( facet->_n2, facet->_n3 );
2431 triangles.push_back( helper.AddFace( volNodes[ facet->_n1 ],
2432 volNodes[ facet->_n2 ],
2433 volNodes[ facet->_n3 ]));
2436 for ( size_t i = 0; i < triangles.size(); ++i )
2438 if ( !triangles[ i ]) continue;
2440 fSubMesh->AddElement( triangles[ i ]);
2441 newElems.Append( triangles[ i ]);
2443 ReplaceElemInGroups( face, triangles, GetMeshDS() );
2444 GetMeshDS()->RemoveFreeElement( face, fSubMesh, /*fromGroups=*/false );
2446 } // while a face based on facet nodes exists
2447 } // loop on volume faces to split them into triangles
2449 GetMeshDS()->RemoveFreeElement( elem, subMesh, /*fromGroups=*/false );
2451 if ( geomType == SMDSEntity_TriQuad_Hexa )
2453 // remove medium nodes that could become free
2454 for ( int i = 20; i < volTool.NbNodes(); ++i )
2455 if ( volNodes[i]->NbInverseElements() == 0 )
2456 GetMeshDS()->RemoveNode( volNodes[i] );
2458 } // loop on volumes to split
2460 myLastCreatedNodes = newNodes;
2461 myLastCreatedElems = newElems;
2464 //=======================================================================
2465 //function : GetHexaFacetsToSplit
2466 //purpose : For hexahedra that will be split into prisms, finds facets to
2467 // split into triangles. Only hexahedra adjacent to the one closest
2468 // to theFacetNormal.Location() are returned.
2469 //param [in,out] theHexas - the hexahedra
2470 //param [in] theFacetNormal - facet normal
2471 //param [out] theFacets - the hexahedra and found facet IDs
2472 //=======================================================================
2474 void SMESH_MeshEditor::GetHexaFacetsToSplit( TIDSortedElemSet& theHexas,
2475 const gp_Ax1& theFacetNormal,
2476 TFacetOfElem & theFacets)
2478 #define THIS_METHOD "SMESH_MeshEditor::GetHexaFacetsToSplit(): "
2480 // Find a hexa closest to the location of theFacetNormal
2482 const SMDS_MeshElement* startHex;
2484 // get SMDS_ElemIteratorPtr on theHexas
2485 typedef const SMDS_MeshElement* TValue;
2486 typedef TIDSortedElemSet::iterator TSetIterator;
2487 typedef SMDS::SimpleAccessor<TValue,TSetIterator> TAccesor;
2488 typedef SMDS_MeshElement::GeomFilter TFilter;
2489 typedef SMDS_SetIterator < TValue, TSetIterator, TAccesor, TFilter > TElemSetIter;
2490 SMDS_ElemIteratorPtr elemIt = SMDS_ElemIteratorPtr
2491 ( new TElemSetIter( theHexas.begin(),
2493 SMDS_MeshElement::GeomFilter( SMDSGeom_HEXA )));
2495 SMESH_ElementSearcher* searcher =
2496 SMESH_MeshAlgos::GetElementSearcher( *myMesh->GetMeshDS(), elemIt );
2498 startHex = searcher->FindClosestTo( theFacetNormal.Location(), SMDSAbs_Volume );
2503 throw SALOME_Exception( THIS_METHOD "startHex not found");
2506 // Select a facet of startHex by theFacetNormal
2508 SMDS_VolumeTool vTool( startHex );
2509 double norm[3], dot, maxDot = 0;
2511 for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2512 if ( vTool.GetFaceNormal( iF, norm[0], norm[1], norm[2] ))
2514 dot = Abs( theFacetNormal.Direction().Dot( gp_Dir( norm[0], norm[1], norm[2] )));
2522 throw SALOME_Exception( THIS_METHOD "facet of startHex not found");
2524 // Fill theFacets starting from facetID of startHex
2526 // facets used for searching of volumes adjacent to already treated ones
2527 typedef pair< TFacetOfElem::iterator, int > TElemFacets;
2528 typedef map< TVolumeFaceKey, TElemFacets > TFacetMap;
2529 TFacetMap facetsToCheck;
2531 set<const SMDS_MeshNode*> facetNodes;
2532 const SMDS_MeshElement* curHex;
2534 const bool allHex = ((int) theHexas.size() == myMesh->NbHexas() );
2538 // move in two directions from startHex via facetID
2539 for ( int is2nd = 0; is2nd < 2; ++is2nd )
2542 int curFacet = facetID;
2543 if ( is2nd ) // do not treat startHex twice
2545 vTool.Set( curHex );
2546 if ( vTool.IsFreeFace( curFacet, &curHex ))
2552 vTool.GetFaceNodes( curFacet, facetNodes );
2553 vTool.Set( curHex );
2554 curFacet = vTool.GetFaceIndex( facetNodes );
2559 // store a facet to split
2560 if ( curHex->GetGeomType() != SMDSGeom_HEXA )
2562 theFacets.insert( make_pair( curHex, -1 ));
2565 if ( !allHex && !theHexas.count( curHex ))
2568 pair< TFacetOfElem::iterator, bool > facetIt2isNew =
2569 theFacets.insert( make_pair( curHex, curFacet ));
2570 if ( !facetIt2isNew.second )
2573 // remember not-to-split facets in facetsToCheck
2574 int oppFacet = vTool.GetOppFaceIndexOfHex( curFacet );
2575 for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2577 if ( iF == curFacet && iF == oppFacet )
2579 TVolumeFaceKey facetKey ( vTool, iF );
2580 TElemFacets elemFacet( facetIt2isNew.first, iF );
2581 pair< TFacetMap::iterator, bool > it2isnew =
2582 facetsToCheck.insert( make_pair( facetKey, elemFacet ));
2583 if ( !it2isnew.second )
2584 facetsToCheck.erase( it2isnew.first ); // adjacent hex already checked
2586 // pass to a volume adjacent via oppFacet
2587 if ( vTool.IsFreeFace( oppFacet, &curHex ))
2593 // get a new curFacet
2594 vTool.GetFaceNodes( oppFacet, facetNodes );
2595 vTool.Set( curHex );
2596 curFacet = vTool.GetFaceIndex( facetNodes, /*hint=*/curFacet );
2599 } // move in two directions from startHex via facetID
2601 // Find a new startHex by facetsToCheck
2605 TFacetMap::iterator fIt = facetsToCheck.begin();
2606 while ( !startHex && fIt != facetsToCheck.end() )
2608 const TElemFacets& elemFacets = fIt->second;
2609 const SMDS_MeshElement* hex = elemFacets.first->first;
2610 int splitFacet = elemFacets.first->second;
2611 int lateralFacet = elemFacets.second;
2612 facetsToCheck.erase( fIt );
2613 fIt = facetsToCheck.begin();
2616 if ( vTool.IsFreeFace( lateralFacet, &curHex ) ||
2617 curHex->GetGeomType() != SMDSGeom_HEXA )
2619 if ( !allHex && !theHexas.count( curHex ))
2624 // find a facet of startHex to split
2626 set<const SMDS_MeshNode*> lateralNodes;
2627 vTool.GetFaceNodes( lateralFacet, lateralNodes );
2628 vTool.GetFaceNodes( splitFacet, facetNodes );
2629 int oppLateralFacet = vTool.GetOppFaceIndexOfHex( lateralFacet );
2630 vTool.Set( startHex );
2631 lateralFacet = vTool.GetFaceIndex( lateralNodes, oppLateralFacet );
2633 // look for a facet of startHex having common nodes with facetNodes
2634 // but not lateralFacet
2635 for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2637 if ( iF == lateralFacet )
2639 int nbCommonNodes = 0;
2640 const SMDS_MeshNode** nn = vTool.GetFaceNodes( iF );
2641 for ( int iN = 0, nbN = vTool.NbFaceNodes( iF ); iN < nbN; ++iN )
2642 nbCommonNodes += facetNodes.count( nn[ iN ]);
2644 if ( nbCommonNodes >= 2 )
2651 throw SALOME_Exception( THIS_METHOD "facet of a new startHex not found");
2653 } // while ( startHex )
2660 //================================================================================
2662 * \brief Selects nodes of several elements according to a given interlace
2663 * \param [in] srcNodes - nodes to select from
2664 * \param [out] tgtNodesVec - array of nodes of several elements to fill in
2665 * \param [in] interlace - indices of nodes for all elements
2666 * \param [in] nbElems - nb of elements
2667 * \param [in] nbNodes - nb of nodes in each element
2668 * \param [in] mesh - the mesh
2669 * \param [out] elemQueue - a list to push elements found by the selected nodes
2670 * \param [in] type - type of elements to look for
2672 //================================================================================
2674 void selectNodes( const vector< const SMDS_MeshNode* >& srcNodes,
2675 vector< const SMDS_MeshNode* >* tgtNodesVec,
2676 const int* interlace,
2679 SMESHDS_Mesh* mesh = 0,
2680 list< const SMDS_MeshElement* >* elemQueue=0,
2681 SMDSAbs_ElementType type=SMDSAbs_All)
2683 for ( int iE = 0; iE < nbElems; ++iE )
2685 vector< const SMDS_MeshNode* >& elemNodes = tgtNodesVec[iE];
2686 const int* select = & interlace[iE*nbNodes];
2687 elemNodes.resize( nbNodes );
2688 for ( int iN = 0; iN < nbNodes; ++iN )
2689 elemNodes[iN] = srcNodes[ select[ iN ]];
2691 const SMDS_MeshElement* e;
2693 for ( int iE = 0; iE < nbElems; ++iE )
2694 if (( e = mesh->FindElement( tgtNodesVec[iE], type, /*noMedium=*/false)))
2695 elemQueue->push_back( e );
2699 //=======================================================================
2701 * Split bi-quadratic elements into linear ones without creation of additional nodes
2702 * - bi-quadratic triangle will be split into 3 linear quadrangles;
2703 * - bi-quadratic quadrangle will be split into 4 linear quadrangles;
2704 * - tri-quadratic hexahedron will be split into 8 linear hexahedra;
2705 * Quadratic elements of lower dimension adjacent to the split bi-quadratic element
2706 * will be split in order to keep the mesh conformal.
2707 * \param elems - elements to split
2709 //=======================================================================
2711 void SMESH_MeshEditor::SplitBiQuadraticIntoLinear(TIDSortedElemSet& theElems)
2713 vector< const SMDS_MeshNode* > elemNodes(27), subNodes[12], splitNodes[8];
2714 vector<const SMDS_MeshElement* > splitElems;
2715 list< const SMDS_MeshElement* > elemQueue;
2716 list< const SMDS_MeshElement* >::iterator elemIt;
2718 SMESHDS_Mesh * mesh = GetMeshDS();
2719 ElemFeatures *elemType, hexaType(SMDSAbs_Volume), quadType(SMDSAbs_Face), segType(SMDSAbs_Edge);
2720 int nbElems, nbNodes;
2722 TIDSortedElemSet::iterator elemSetIt = theElems.begin();
2723 for ( ; elemSetIt != theElems.end(); ++elemSetIt )
2726 elemQueue.push_back( *elemSetIt );
2727 for ( elemIt = elemQueue.begin(); elemIt != elemQueue.end(); ++elemIt )
2729 const SMDS_MeshElement* elem = *elemIt;
2730 switch( elem->GetEntityType() )
2732 case SMDSEntity_TriQuad_Hexa: // HEX27
2734 elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2735 nbElems = nbNodes = 8;
2736 elemType = & hexaType;
2738 // get nodes for new elements
2739 static int vInd[8][8] = {{ 0,8,20,11, 16,21,26,24 },
2740 { 1,9,20,8, 17,22,26,21 },
2741 { 2,10,20,9, 18,23,26,22 },
2742 { 3,11,20,10, 19,24,26,23 },
2743 { 16,21,26,24, 4,12,25,15 },
2744 { 17,22,26,21, 5,13,25,12 },
2745 { 18,23,26,22, 6,14,25,13 },
2746 { 19,24,26,23, 7,15,25,14 }};
2747 selectNodes( elemNodes, & splitNodes[0], &vInd[0][0], nbElems, nbNodes );
2749 // add boundary faces to elemQueue
2750 static int fInd[6][9] = {{ 0,1,2,3, 8,9,10,11, 20 },
2751 { 4,5,6,7, 12,13,14,15, 25 },
2752 { 0,1,5,4, 8,17,12,16, 21 },
2753 { 1,2,6,5, 9,18,13,17, 22 },
2754 { 2,3,7,6, 10,19,14,18, 23 },
2755 { 3,0,4,7, 11,16,15,19, 24 }};
2756 selectNodes( elemNodes, & subNodes[0], &fInd[0][0], 6,9, mesh, &elemQueue, SMDSAbs_Face );
2758 // add boundary segments to elemQueue
2759 static int eInd[12][3] = {{ 0,1,8 }, { 1,2,9 }, { 2,3,10 }, { 3,0,11 },
2760 { 4,5,12}, { 5,6,13}, { 6,7,14 }, { 7,4,15 },
2761 { 0,4,16}, { 1,5,17}, { 2,6,18 }, { 3,7,19 }};
2762 selectNodes( elemNodes, & subNodes[0], &eInd[0][0], 12,3, mesh, &elemQueue, SMDSAbs_Edge );
2765 case SMDSEntity_BiQuad_Triangle: // TRIA7
2767 elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2770 elemType = & quadType;
2772 // get nodes for new elements
2773 static int fInd[3][4] = {{ 0,3,6,5 }, { 1,4,6,3 }, { 2,5,6,4 }};
2774 selectNodes( elemNodes, & splitNodes[0], &fInd[0][0], nbElems, nbNodes );
2776 // add boundary segments to elemQueue
2777 static int eInd[3][3] = {{ 0,1,3 }, { 1,2,4 }, { 2,0,5 }};
2778 selectNodes( elemNodes, & subNodes[0], &eInd[0][0], 3,3, mesh, &elemQueue, SMDSAbs_Edge );
2781 case SMDSEntity_BiQuad_Quadrangle: // QUAD9
2783 elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2786 elemType = & quadType;
2788 // get nodes for new elements
2789 static int fInd[4][4] = {{ 0,4,8,7 }, { 1,5,8,4 }, { 2,6,8,5 }, { 3,7,8,6 }};
2790 selectNodes( elemNodes, & splitNodes[0], &fInd[0][0], nbElems, nbNodes );
2792 // add boundary segments to elemQueue
2793 static int eInd[4][3] = {{ 0,1,4 }, { 1,2,5 }, { 2,3,6 }, { 3,0,7 }};
2794 selectNodes( elemNodes, & subNodes[0], &eInd[0][0], 4,3, mesh, &elemQueue, SMDSAbs_Edge );
2797 case SMDSEntity_Quad_Edge:
2799 if ( elemIt == elemQueue.begin() )
2800 continue; // an elem is in theElems
2801 elemNodes.assign( elem->begin_nodes(), elem->end_nodes() );
2804 elemType = & segType;
2806 // get nodes for new elements
2807 static int eInd[2][2] = {{ 0,2 }, { 2,1 }};
2808 selectNodes( elemNodes, & splitNodes[0], &eInd[0][0], nbElems, nbNodes );
2812 } // switch( elem->GetEntityType() )
2814 // Create new elements
2816 SMESHDS_SubMesh* subMesh = mesh->MeshElements( elem->getshapeId() );
2820 //elemType->SetID( elem->GetID() ); // create an elem with the same ID as a removed one
2821 mesh->RemoveFreeElement( elem, subMesh, /*fromGroups=*/false );
2822 //splitElems.push_back( AddElement( splitNodes[ 0 ], *elemType ));
2823 //elemType->SetID( -1 );
2825 for ( int iE = 0; iE < nbElems; ++iE )
2826 splitElems.push_back( AddElement( splitNodes[ iE ], *elemType ));
2829 ReplaceElemInGroups( elem, splitElems, mesh );
2832 for ( size_t i = 0; i < splitElems.size(); ++i )
2833 subMesh->AddElement( splitElems[i] );
2838 //=======================================================================
2839 //function : AddToSameGroups
2840 //purpose : add elemToAdd to the groups the elemInGroups belongs to
2841 //=======================================================================
2843 void SMESH_MeshEditor::AddToSameGroups (const SMDS_MeshElement* elemToAdd,
2844 const SMDS_MeshElement* elemInGroups,
2845 SMESHDS_Mesh * aMesh)
2847 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2848 if (!groups.empty()) {
2849 set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2850 for ( ; grIt != groups.end(); grIt++ ) {
2851 SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2852 if ( group && group->Contains( elemInGroups ))
2853 group->SMDSGroup().Add( elemToAdd );
2859 //=======================================================================
2860 //function : RemoveElemFromGroups
2861 //purpose : Remove removeelem to the groups the elemInGroups belongs to
2862 //=======================================================================
2863 void SMESH_MeshEditor::RemoveElemFromGroups (const SMDS_MeshElement* removeelem,
2864 SMESHDS_Mesh * aMesh)
2866 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2867 if (!groups.empty())
2869 set<SMESHDS_GroupBase*>::const_iterator GrIt = groups.begin();
2870 for (; GrIt != groups.end(); GrIt++)
2872 SMESHDS_Group* grp = dynamic_cast<SMESHDS_Group*>(*GrIt);
2873 if (!grp || grp->IsEmpty()) continue;
2874 grp->SMDSGroup().Remove(removeelem);
2879 //================================================================================
2881 * \brief Replace elemToRm by elemToAdd in the all groups
2883 //================================================================================
2885 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement* elemToRm,
2886 const SMDS_MeshElement* elemToAdd,
2887 SMESHDS_Mesh * aMesh)
2889 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2890 if (!groups.empty()) {
2891 set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2892 for ( ; grIt != groups.end(); grIt++ ) {
2893 SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2894 if ( group && group->SMDSGroup().Remove( elemToRm ) && elemToAdd )
2895 group->SMDSGroup().Add( elemToAdd );
2900 //================================================================================
2902 * \brief Replace elemToRm by elemToAdd in the all groups
2904 //================================================================================
2906 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement* elemToRm,
2907 const vector<const SMDS_MeshElement*>& elemToAdd,
2908 SMESHDS_Mesh * aMesh)
2910 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2911 if (!groups.empty())
2913 set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2914 for ( ; grIt != groups.end(); grIt++ ) {
2915 SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2916 if ( group && group->SMDSGroup().Remove( elemToRm ) )
2917 for ( size_t i = 0; i < elemToAdd.size(); ++i )
2918 group->SMDSGroup().Add( elemToAdd[ i ] );
2923 //=======================================================================
2924 //function : QuadToTri
2925 //purpose : Cut quadrangles into triangles.
2926 // theCrit is used to select a diagonal to cut
2927 //=======================================================================
2929 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet & theElems,
2930 const bool the13Diag)
2932 myLastCreatedElems.Clear();
2933 myLastCreatedNodes.Clear();
2935 SMESHDS_Mesh * aMesh = GetMeshDS();
2937 Handle(Geom_Surface) surface;
2938 SMESH_MesherHelper helper( *GetMesh() );
2940 TIDSortedElemSet::iterator itElem;
2941 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
2943 const SMDS_MeshElement* elem = *itElem;
2944 if ( !elem || elem->GetGeomType() != SMDSGeom_QUADRANGLE )
2947 if ( elem->NbNodes() == 4 ) {
2948 // retrieve element nodes
2949 const SMDS_MeshNode* aNodes [4];
2950 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
2952 while ( itN->more() )
2953 aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
2955 int aShapeId = FindShape( elem );
2956 const SMDS_MeshElement* newElem1 = 0;
2957 const SMDS_MeshElement* newElem2 = 0;
2959 newElem1 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
2960 newElem2 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
2963 newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
2964 newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
2966 myLastCreatedElems.Append(newElem1);
2967 myLastCreatedElems.Append(newElem2);
2968 // put a new triangle on the same shape and add to the same groups
2971 aMesh->SetMeshElementOnShape( newElem1, aShapeId );
2972 aMesh->SetMeshElementOnShape( newElem2, aShapeId );
2974 AddToSameGroups( newElem1, elem, aMesh );
2975 AddToSameGroups( newElem2, elem, aMesh );
2976 aMesh->RemoveElement( elem );
2979 // Quadratic quadrangle
2981 else if ( elem->NbNodes() >= 8 )
2983 // get surface elem is on
2984 int aShapeId = FindShape( elem );
2985 if ( aShapeId != helper.GetSubShapeID() ) {
2989 shape = aMesh->IndexToShape( aShapeId );
2990 if ( !shape.IsNull() && shape.ShapeType() == TopAbs_FACE ) {
2991 TopoDS_Face face = TopoDS::Face( shape );
2992 surface = BRep_Tool::Surface( face );
2993 if ( !surface.IsNull() )
2994 helper.SetSubShape( shape );
2998 const SMDS_MeshNode* aNodes [9]; aNodes[8] = 0;
2999 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3000 for ( int i = 0; itN->more(); ++i )
3001 aNodes[ i ] = static_cast<const SMDS_MeshNode*>( itN->next() );
3003 const SMDS_MeshNode* centrNode = aNodes[8];
3004 if ( centrNode == 0 )
3006 centrNode = helper.GetCentralNode( aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3007 aNodes[4], aNodes[5], aNodes[6], aNodes[7],
3009 myLastCreatedNodes.Append(centrNode);
3012 // create a new element
3013 const SMDS_MeshElement* newElem1 = 0;
3014 const SMDS_MeshElement* newElem2 = 0;
3016 newElem1 = aMesh->AddFace(aNodes[2], aNodes[3], aNodes[0],
3017 aNodes[6], aNodes[7], centrNode );
3018 newElem2 = aMesh->AddFace(aNodes[2], aNodes[0], aNodes[1],
3019 centrNode, aNodes[4], aNodes[5] );
3022 newElem1 = aMesh->AddFace(aNodes[3], aNodes[0], aNodes[1],
3023 aNodes[7], aNodes[4], centrNode );
3024 newElem2 = aMesh->AddFace(aNodes[3], aNodes[1], aNodes[2],
3025 centrNode, aNodes[5], aNodes[6] );
3027 myLastCreatedElems.Append(newElem1);
3028 myLastCreatedElems.Append(newElem2);
3029 // put a new triangle on the same shape and add to the same groups
3032 aMesh->SetMeshElementOnShape( newElem1, aShapeId );
3033 aMesh->SetMeshElementOnShape( newElem2, aShapeId );
3035 AddToSameGroups( newElem1, elem, aMesh );
3036 AddToSameGroups( newElem2, elem, aMesh );
3037 aMesh->RemoveElement( elem );
3044 //=======================================================================
3045 //function : getAngle
3047 //=======================================================================
3049 double getAngle(const SMDS_MeshElement * tr1,
3050 const SMDS_MeshElement * tr2,
3051 const SMDS_MeshNode * n1,
3052 const SMDS_MeshNode * n2)
3054 double angle = 2. * M_PI; // bad angle
3057 SMESH::Controls::TSequenceOfXYZ P1, P2;
3058 if ( !SMESH::Controls::NumericalFunctor::GetPoints( tr1, P1 ) ||
3059 !SMESH::Controls::NumericalFunctor::GetPoints( tr2, P2 ))
3062 if(!tr1->IsQuadratic())
3063 N1 = gp_Vec( P1(2) - P1(1) ) ^ gp_Vec( P1(3) - P1(1) );
3065 N1 = gp_Vec( P1(3) - P1(1) ) ^ gp_Vec( P1(5) - P1(1) );
3066 if ( N1.SquareMagnitude() <= gp::Resolution() )
3068 if(!tr2->IsQuadratic())
3069 N2 = gp_Vec( P2(2) - P2(1) ) ^ gp_Vec( P2(3) - P2(1) );
3071 N2 = gp_Vec( P2(3) - P2(1) ) ^ gp_Vec( P2(5) - P2(1) );
3072 if ( N2.SquareMagnitude() <= gp::Resolution() )
3075 // find the first diagonal node n1 in the triangles:
3076 // take in account a diagonal link orientation
3077 const SMDS_MeshElement *nFirst[2], *tr[] = { tr1, tr2 };
3078 for ( int t = 0; t < 2; t++ ) {
3079 SMDS_ElemIteratorPtr it = tr[ t ]->nodesIterator();
3080 int i = 0, iDiag = -1;
3081 while ( it->more()) {
3082 const SMDS_MeshElement *n = it->next();
3083 if ( n == n1 || n == n2 ) {
3087 if ( i - iDiag == 1 )
3088 nFirst[ t ] = ( n == n1 ? n2 : n1 );
3097 if ( nFirst[ 0 ] == nFirst[ 1 ] )
3100 angle = N1.Angle( N2 );
3105 // =================================================
3106 // class generating a unique ID for a pair of nodes
3107 // and able to return nodes by that ID
3108 // =================================================
3112 LinkID_Gen( const SMESHDS_Mesh* theMesh )
3113 :myMesh( theMesh ), myMaxID( theMesh->MaxNodeID() + 1)
3116 long GetLinkID (const SMDS_MeshNode * n1,
3117 const SMDS_MeshNode * n2) const
3119 return ( Min(n1->GetID(),n2->GetID()) * myMaxID + Max(n1->GetID(),n2->GetID()));
3122 bool GetNodes (const long theLinkID,
3123 const SMDS_MeshNode* & theNode1,
3124 const SMDS_MeshNode* & theNode2) const
3126 theNode1 = myMesh->FindNode( theLinkID / myMaxID );
3127 if ( !theNode1 ) return false;
3128 theNode2 = myMesh->FindNode( theLinkID % myMaxID );
3129 if ( !theNode2 ) return false;
3135 const SMESHDS_Mesh* myMesh;
3140 //=======================================================================
3141 //function : TriToQuad
3142 //purpose : Fuse neighbour triangles into quadrangles.
3143 // theCrit is used to select a neighbour to fuse with.
3144 // theMaxAngle is a max angle between element normals at which
3145 // fusion is still performed.
3146 //=======================================================================
3148 bool SMESH_MeshEditor::TriToQuad (TIDSortedElemSet & theElems,
3149 SMESH::Controls::NumericalFunctorPtr theCrit,
3150 const double theMaxAngle)
3152 myLastCreatedElems.Clear();
3153 myLastCreatedNodes.Clear();
3155 if ( !theCrit.get() )
3158 SMESHDS_Mesh * aMesh = GetMeshDS();
3160 // Prepare data for algo: build
3161 // 1. map of elements with their linkIDs
3162 // 2. map of linkIDs with their elements
3164 map< SMESH_TLink, list< const SMDS_MeshElement* > > mapLi_listEl;
3165 map< SMESH_TLink, list< const SMDS_MeshElement* > >::iterator itLE;
3166 map< const SMDS_MeshElement*, set< SMESH_TLink > > mapEl_setLi;
3167 map< const SMDS_MeshElement*, set< SMESH_TLink > >::iterator itEL;
3169 TIDSortedElemSet::iterator itElem;
3170 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
3172 const SMDS_MeshElement* elem = *itElem;
3173 if(!elem || elem->GetType() != SMDSAbs_Face ) continue;
3174 bool IsTria = ( elem->NbCornerNodes()==3 );
3175 if (!IsTria) continue;
3177 // retrieve element nodes
3178 const SMDS_MeshNode* aNodes [4];
3179 SMDS_NodeIteratorPtr itN = elem->nodeIterator();
3182 aNodes[ i++ ] = itN->next();
3183 aNodes[ 3 ] = aNodes[ 0 ];
3186 for ( i = 0; i < 3; i++ ) {
3187 SMESH_TLink link( aNodes[i], aNodes[i+1] );
3188 // check if elements sharing a link can be fused
3189 itLE = mapLi_listEl.find( link );
3190 if ( itLE != mapLi_listEl.end() ) {
3191 if ((*itLE).second.size() > 1 ) // consider only 2 elems adjacent by a link
3193 const SMDS_MeshElement* elem2 = (*itLE).second.front();
3194 //if ( FindShape( elem ) != FindShape( elem2 ))
3195 // continue; // do not fuse triangles laying on different shapes
3196 if ( getAngle( elem, elem2, aNodes[i], aNodes[i+1] ) > theMaxAngle )
3197 continue; // avoid making badly shaped quads
3198 (*itLE).second.push_back( elem );
3201 mapLi_listEl[ link ].push_back( elem );
3203 mapEl_setLi [ elem ].insert( link );
3206 // Clean the maps from the links shared by a sole element, ie
3207 // links to which only one element is bound in mapLi_listEl
3209 for ( itLE = mapLi_listEl.begin(); itLE != mapLi_listEl.end(); itLE++ ) {
3210 int nbElems = (*itLE).second.size();
3211 if ( nbElems < 2 ) {
3212 const SMDS_MeshElement* elem = (*itLE).second.front();
3213 SMESH_TLink link = (*itLE).first;
3214 mapEl_setLi[ elem ].erase( link );
3215 if ( mapEl_setLi[ elem ].empty() )
3216 mapEl_setLi.erase( elem );
3220 // Algo: fuse triangles into quadrangles
3222 while ( ! mapEl_setLi.empty() ) {
3223 // Look for the start element:
3224 // the element having the least nb of shared links
3225 const SMDS_MeshElement* startElem = 0;
3227 for ( itEL = mapEl_setLi.begin(); itEL != mapEl_setLi.end(); itEL++ ) {
3228 int nbLinks = (*itEL).second.size();
3229 if ( nbLinks < minNbLinks ) {
3230 startElem = (*itEL).first;
3231 minNbLinks = nbLinks;
3232 if ( minNbLinks == 1 )
3237 // search elements to fuse starting from startElem or links of elements
3238 // fused earlyer - startLinks
3239 list< SMESH_TLink > startLinks;
3240 while ( startElem || !startLinks.empty() ) {
3241 while ( !startElem && !startLinks.empty() ) {
3242 // Get an element to start, by a link
3243 SMESH_TLink linkId = startLinks.front();
3244 startLinks.pop_front();
3245 itLE = mapLi_listEl.find( linkId );
3246 if ( itLE != mapLi_listEl.end() ) {
3247 list< const SMDS_MeshElement* > & listElem = (*itLE).second;
3248 list< const SMDS_MeshElement* >::iterator itE = listElem.begin();
3249 for ( ; itE != listElem.end() ; itE++ )
3250 if ( mapEl_setLi.find( (*itE) ) != mapEl_setLi.end() )
3252 mapLi_listEl.erase( itLE );
3257 // Get candidates to be fused
3258 const SMDS_MeshElement *tr1 = startElem, *tr2 = 0, *tr3 = 0;
3259 const SMESH_TLink *link12 = 0, *link13 = 0;
3261 ASSERT( mapEl_setLi.find( tr1 ) != mapEl_setLi.end() );
3262 set< SMESH_TLink >& setLi = mapEl_setLi[ tr1 ];
3263 ASSERT( !setLi.empty() );
3264 set< SMESH_TLink >::iterator itLi;
3265 for ( itLi = setLi.begin(); itLi != setLi.end(); itLi++ )
3267 const SMESH_TLink & link = (*itLi);
3268 itLE = mapLi_listEl.find( link );
3269 if ( itLE == mapLi_listEl.end() )
3272 const SMDS_MeshElement* elem = (*itLE).second.front();
3274 elem = (*itLE).second.back();
3275 mapLi_listEl.erase( itLE );
3276 if ( mapEl_setLi.find( elem ) == mapEl_setLi.end())
3287 // add other links of elem to list of links to re-start from
3288 set< SMESH_TLink >& links = mapEl_setLi[ elem ];
3289 set< SMESH_TLink >::iterator it;
3290 for ( it = links.begin(); it != links.end(); it++ ) {
3291 const SMESH_TLink& link2 = (*it);
3292 if ( link2 != link )
3293 startLinks.push_back( link2 );
3297 // Get nodes of possible quadrangles
3298 const SMDS_MeshNode *n12 [4], *n13 [4];
3299 bool Ok12 = false, Ok13 = false;
3300 const SMDS_MeshNode *linkNode1, *linkNode2;
3302 linkNode1 = link12->first;
3303 linkNode2 = link12->second;
3304 if ( tr2 && getQuadrangleNodes( n12, linkNode1, linkNode2, tr1, tr2 ))
3308 linkNode1 = link13->first;
3309 linkNode2 = link13->second;
3310 if ( tr3 && getQuadrangleNodes( n13, linkNode1, linkNode2, tr1, tr3 ))
3314 // Choose a pair to fuse
3315 if ( Ok12 && Ok13 ) {
3316 SMDS_FaceOfNodes quad12 ( n12[ 0 ], n12[ 1 ], n12[ 2 ], n12[ 3 ] );
3317 SMDS_FaceOfNodes quad13 ( n13[ 0 ], n13[ 1 ], n13[ 2 ], n13[ 3 ] );
3318 double aBadRate12 = getBadRate( &quad12, theCrit );
3319 double aBadRate13 = getBadRate( &quad13, theCrit );
3320 if ( aBadRate13 < aBadRate12 )
3327 // and remove fused elems and remove links from the maps
3328 mapEl_setLi.erase( tr1 );
3331 mapEl_setLi.erase( tr2 );
3332 mapLi_listEl.erase( *link12 );
3333 if ( tr1->NbNodes() == 3 )
3335 const SMDS_MeshElement* newElem = 0;
3336 newElem = aMesh->AddFace(n12[0], n12[1], n12[2], n12[3] );
3337 myLastCreatedElems.Append(newElem);
3338 AddToSameGroups( newElem, tr1, aMesh );
3339 int aShapeId = tr1->getshapeId();
3341 aMesh->SetMeshElementOnShape( newElem, aShapeId );
3342 aMesh->RemoveElement( tr1 );
3343 aMesh->RemoveElement( tr2 );
3346 vector< const SMDS_MeshNode* > N1;
3347 vector< const SMDS_MeshNode* > N2;
3348 getNodesFromTwoTria(tr1,tr2,N1,N2);
3349 // now we receive following N1 and N2 (using numeration as in image in InverseDiag())
3350 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
3351 // i.e. first nodes from both arrays form a new diagonal
3352 const SMDS_MeshNode* aNodes[8];
3361 const SMDS_MeshElement* newElem = 0;
3362 if ( N1.size() == 7 || N2.size() == 7 ) // biquadratic
3363 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3364 aNodes[4], aNodes[5], aNodes[6], aNodes[7], N1[4]);
3366 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3367 aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
3368 myLastCreatedElems.Append(newElem);
3369 AddToSameGroups( newElem, tr1, aMesh );
3370 int aShapeId = tr1->getshapeId();
3372 aMesh->SetMeshElementOnShape( newElem, aShapeId );
3373 aMesh->RemoveElement( tr1 );
3374 aMesh->RemoveElement( tr2 );
3375 // remove middle node (9)
3376 if ( N1[4]->NbInverseElements() == 0 )
3377 aMesh->RemoveNode( N1[4] );
3378 if ( N1.size() == 7 && N1[6]->NbInverseElements() == 0 )
3379 aMesh->RemoveNode( N1[6] );
3380 if ( N2.size() == 7 && N2[6]->NbInverseElements() == 0 )
3381 aMesh->RemoveNode( N2[6] );
3386 mapEl_setLi.erase( tr3 );
3387 mapLi_listEl.erase( *link13 );
3388 if ( tr1->NbNodes() == 3 ) {
3389 const SMDS_MeshElement* newElem = 0;
3390 newElem = aMesh->AddFace(n13[0], n13[1], n13[2], n13[3] );
3391 myLastCreatedElems.Append(newElem);
3392 AddToSameGroups( newElem, tr1, aMesh );
3393 int aShapeId = tr1->getshapeId();
3395 aMesh->SetMeshElementOnShape( newElem, aShapeId );
3396 aMesh->RemoveElement( tr1 );
3397 aMesh->RemoveElement( tr3 );
3400 vector< const SMDS_MeshNode* > N1;
3401 vector< const SMDS_MeshNode* > N2;
3402 getNodesFromTwoTria(tr1,tr3,N1,N2);
3403 // now we receive following N1 and N2 (using numeration as above image)
3404 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
3405 // i.e. first nodes from both arrays form a new diagonal
3406 const SMDS_MeshNode* aNodes[8];
3415 const SMDS_MeshElement* newElem = 0;
3416 if ( N1.size() == 7 || N2.size() == 7 ) // biquadratic
3417 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3418 aNodes[4], aNodes[5], aNodes[6], aNodes[7], N1[4]);
3420 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3421 aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
3422 myLastCreatedElems.Append(newElem);
3423 AddToSameGroups( newElem, tr1, aMesh );
3424 int aShapeId = tr1->getshapeId();
3426 aMesh->SetMeshElementOnShape( newElem, aShapeId );
3427 aMesh->RemoveElement( tr1 );
3428 aMesh->RemoveElement( tr3 );
3429 // remove middle node (9)
3430 if ( N1[4]->NbInverseElements() == 0 )
3431 aMesh->RemoveNode( N1[4] );
3432 if ( N1.size() == 7 && N1[6]->NbInverseElements() == 0 )
3433 aMesh->RemoveNode( N1[6] );
3434 if ( N2.size() == 7 && N2[6]->NbInverseElements() == 0 )
3435 aMesh->RemoveNode( N2[6] );
3439 // Next element to fuse: the rejected one
3441 startElem = Ok12 ? tr3 : tr2;
3443 } // if ( startElem )
3444 } // while ( startElem || !startLinks.empty() )
3445 } // while ( ! mapEl_setLi.empty() )
3451 /*#define DUMPSO(txt) \
3452 // cout << txt << endl;
3453 //=============================================================================
3457 //=============================================================================
3458 static void swap( int i1, int i2, int idNodes[], gp_Pnt P[] )
3462 int tmp = idNodes[ i1 ];
3463 idNodes[ i1 ] = idNodes[ i2 ];
3464 idNodes[ i2 ] = tmp;
3465 gp_Pnt Ptmp = P[ i1 ];
3468 DUMPSO( i1 << "(" << idNodes[ i2 ] << ") <-> " << i2 << "(" << idNodes[ i1 ] << ")");
3471 //=======================================================================
3472 //function : SortQuadNodes
3473 //purpose : Set 4 nodes of a quadrangle face in a good order.
3474 // Swap 1<->2 or 2<->3 nodes and correspondingly return
3476 //=======================================================================
3478 int SMESH_MeshEditor::SortQuadNodes (const SMDS_Mesh * theMesh,
3483 for ( i = 0; i < 4; i++ ) {
3484 const SMDS_MeshNode *n = theMesh->FindNode( idNodes[i] );
3486 P[ i ].SetCoord( n->X(), n->Y(), n->Z() );
3489 gp_Vec V1(P[0], P[1]);
3490 gp_Vec V2(P[0], P[2]);
3491 gp_Vec V3(P[0], P[3]);
3493 gp_Vec Cross1 = V1 ^ V2;
3494 gp_Vec Cross2 = V2 ^ V3;
3497 if (Cross1.Dot(Cross2) < 0)
3502 if (Cross1.Dot(Cross2) < 0)
3506 swap ( i, i + 1, idNodes, P );
3508 // for ( int ii = 0; ii < 4; ii++ ) {
3509 // const SMDS_MeshNode *n = theMesh->FindNode( idNodes[ii] );
3510 // DUMPSO( ii << "(" << idNodes[ii] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
3516 //=======================================================================
3517 //function : SortHexaNodes
3518 //purpose : Set 8 nodes of a hexahedron in a good order.
3519 // Return success status
3520 //=======================================================================
3522 bool SMESH_MeshEditor::SortHexaNodes (const SMDS_Mesh * theMesh,
3527 DUMPSO( "INPUT: ========================================");
3528 for ( i = 0; i < 8; i++ ) {
3529 const SMDS_MeshNode *n = theMesh->FindNode( idNodes[i] );
3530 if ( !n ) return false;
3531 P[ i ].SetCoord( n->X(), n->Y(), n->Z() );
3532 DUMPSO( i << "(" << idNodes[i] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
3534 DUMPSO( "========================================");
3537 set<int> faceNodes; // ids of bottom face nodes, to be found
3538 set<int> checkedId1; // ids of tried 2-nd nodes
3539 Standard_Real leastDist = DBL_MAX; // dist of the 4-th node from 123 plane
3540 const Standard_Real tol = 1.e-6; // tolerance to find nodes in plane
3541 int iMin, iLoop1 = 0;
3543 // Loop to try the 2-nd nodes
3545 while ( leastDist > DBL_MIN && ++iLoop1 < 8 )
3547 // Find not checked 2-nd node
3548 for ( i = 1; i < 8; i++ )
3549 if ( checkedId1.find( idNodes[i] ) == checkedId1.end() ) {
3550 int id1 = idNodes[i];
3551 swap ( 1, i, idNodes, P );
3552 checkedId1.insert ( id1 );
3556 // Find the 3-d node so that 1-2-3 triangle to be on a hexa face,
3557 // ie that all but meybe one (id3 which is on the same face) nodes
3558 // lay on the same side from the triangle plane.
3560 bool manyInPlane = false; // more than 4 nodes lay in plane
3562 while ( ++iLoop2 < 6 ) {
3564 // get 1-2-3 plane coeffs
3565 Standard_Real A, B, C, D;
3566 gp_Vec N = gp_Vec (P[0], P[1]).Crossed( gp_Vec (P[0], P[2]) );
3567 if ( N.SquareMagnitude() > gp::Resolution() )
3569 gp_Pln pln ( P[0], N );
3570 pln.Coefficients( A, B, C, D );
3572 // find the node (iMin) closest to pln
3573 Standard_Real dist[ 8 ], minDist = DBL_MAX;
3575 for ( i = 3; i < 8; i++ ) {
3576 dist[i] = A * P[i].X() + B * P[i].Y() + C * P[i].Z() + D;
3577 if ( fabs( dist[i] ) < minDist ) {
3578 minDist = fabs( dist[i] );
3581 if ( fabs( dist[i] ) <= tol )
3582 idInPln.insert( idNodes[i] );
3585 // there should not be more than 4 nodes in bottom plane
3586 if ( idInPln.size() > 1 )
3588 DUMPSO( "### idInPln.size() = " << idInPln.size());
3589 // idInPlane does not contain the first 3 nodes
3590 if ( manyInPlane || idInPln.size() == 5)
3591 return false; // all nodes in one plane
3594 // set the 1-st node to be not in plane
3595 for ( i = 3; i < 8; i++ ) {
3596 if ( idInPln.find( idNodes[ i ] ) == idInPln.end() ) {
3597 DUMPSO( "### Reset 0-th node");
3598 swap( 0, i, idNodes, P );
3603 // reset to re-check second nodes
3604 leastDist = DBL_MAX;
3608 break; // from iLoop2;
3611 // check that the other 4 nodes are on the same side
3612 bool sameSide = true;
3613 bool isNeg = dist[ iMin == 3 ? 4 : 3 ] <= 0.;
3614 for ( i = 3; sameSide && i < 8; i++ ) {
3616 sameSide = ( isNeg == dist[i] <= 0.);
3619 // keep best solution
3620 if ( sameSide && minDist < leastDist ) {
3621 leastDist = minDist;
3623 faceNodes.insert( idNodes[ 1 ] );
3624 faceNodes.insert( idNodes[ 2 ] );
3625 faceNodes.insert( idNodes[ iMin ] );
3626 DUMPSO( "loop " << iLoop2 << " id2 " << idNodes[ 1 ] << " id3 " << idNodes[ 2 ]
3627 << " leastDist = " << leastDist);
3628 if ( leastDist <= DBL_MIN )
3633 // set next 3-d node to check
3634 int iNext = 2 + iLoop2;
3636 DUMPSO( "Try 2-nd");
3637 swap ( 2, iNext, idNodes, P );
3639 } // while ( iLoop2 < 6 )
3642 if ( faceNodes.empty() ) return false;
3644 // Put the faceNodes in proper places
3645 for ( i = 4; i < 8; i++ ) {
3646 if ( faceNodes.find( idNodes[ i ] ) != faceNodes.end() ) {
3647 // find a place to put
3649 while ( faceNodes.find( idNodes[ iTo ] ) != faceNodes.end() )
3651 DUMPSO( "Set faceNodes");
3652 swap ( iTo, i, idNodes, P );
3657 // Set nodes of the found bottom face in good order
3658 DUMPSO( " Found bottom face: ");
3659 i = SortQuadNodes( theMesh, idNodes );
3661 gp_Pnt Ptmp = P[ i ];
3666 // for ( int ii = 0; ii < 4; ii++ ) {
3667 // const SMDS_MeshNode *n = theMesh->FindNode( idNodes[ii] );
3668 // DUMPSO( ii << "(" << idNodes[ii] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
3671 // Gravity center of the top and bottom faces
3672 gp_Pnt aGCb = ( P[0].XYZ() + P[1].XYZ() + P[2].XYZ() + P[3].XYZ() ) / 4.;
3673 gp_Pnt aGCt = ( P[4].XYZ() + P[5].XYZ() + P[6].XYZ() + P[7].XYZ() ) / 4.;
3675 // Get direction from the bottom to the top face
3676 gp_Vec upDir ( aGCb, aGCt );
3677 Standard_Real upDirSize = upDir.Magnitude();
3678 if ( upDirSize <= gp::Resolution() ) return false;
3681 // Assure that the bottom face normal points up
3682 gp_Vec Nb = gp_Vec (P[0], P[1]).Crossed( gp_Vec (P[0], P[2]) );
3683 Nb += gp_Vec (P[0], P[2]).Crossed( gp_Vec (P[0], P[3]) );
3684 if ( Nb.Dot( upDir ) < 0 ) {
3685 DUMPSO( "Reverse bottom face");
3686 swap( 1, 3, idNodes, P );
3689 // Find 5-th node - the one closest to the 1-st among the last 4 nodes.
3690 Standard_Real minDist = DBL_MAX;
3691 for ( i = 4; i < 8; i++ ) {
3692 // projection of P[i] to the plane defined by P[0] and upDir
3693 gp_Pnt Pp = P[i].Translated( upDir * ( upDir.Dot( gp_Vec( P[i], P[0] ))));
3694 Standard_Real sqDist = P[0].SquareDistance( Pp );
3695 if ( sqDist < minDist ) {
3700 DUMPSO( "Set 4-th");
3701 swap ( 4, iMin, idNodes, P );
3703 // Set nodes of the top face in good order
3704 DUMPSO( "Sort top face");
3705 i = SortQuadNodes( theMesh, &idNodes[4] );
3708 gp_Pnt Ptmp = P[ i ];
3713 // Assure that direction of the top face normal is from the bottom face
3714 gp_Vec Nt = gp_Vec (P[4], P[5]).Crossed( gp_Vec (P[4], P[6]) );
3715 Nt += gp_Vec (P[4], P[6]).Crossed( gp_Vec (P[4], P[7]) );
3716 if ( Nt.Dot( upDir ) < 0 ) {
3717 DUMPSO( "Reverse top face");
3718 swap( 5, 7, idNodes, P );
3721 // DUMPSO( "OUTPUT: ========================================");
3722 // for ( i = 0; i < 8; i++ ) {
3723 // float *p = ugrid->GetPoint(idNodes[i]);
3724 // DUMPSO( i << "(" << idNodes[i] << ") : " << p[0] << " " << p[1] << " " << p[2]);
3730 //================================================================================
3732 * \brief Return nodes linked to the given one
3733 * \param theNode - the node
3734 * \param linkedNodes - the found nodes
3735 * \param type - the type of elements to check
3737 * Medium nodes are ignored
3739 //================================================================================
3741 void SMESH_MeshEditor::GetLinkedNodes( const SMDS_MeshNode* theNode,
3742 TIDSortedElemSet & linkedNodes,
3743 SMDSAbs_ElementType type )
3745 SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(type);
3746 while ( elemIt->more() )
3748 const SMDS_MeshElement* elem = elemIt->next();
3749 if(elem->GetType() == SMDSAbs_0DElement)
3752 SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
3753 if ( elem->GetType() == SMDSAbs_Volume )
3755 SMDS_VolumeTool vol( elem );
3756 while ( nodeIt->more() ) {
3757 const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
3758 if ( theNode != n && vol.IsLinked( theNode, n ))
3759 linkedNodes.insert( n );
3764 for ( int i = 0; nodeIt->more(); ++i ) {
3765 const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
3766 if ( n == theNode ) {
3767 int iBefore = i - 1;
3769 if ( elem->IsQuadratic() ) {
3770 int nb = elem->NbNodes() / 2;
3771 iAfter = SMESH_MesherHelper::WrapIndex( iAfter, nb );
3772 iBefore = SMESH_MesherHelper::WrapIndex( iBefore, nb );
3774 linkedNodes.insert( elem->GetNodeWrap( iAfter ));
3775 linkedNodes.insert( elem->GetNodeWrap( iBefore ));
3782 //=======================================================================
3783 //function : laplacianSmooth
3784 //purpose : pulls theNode toward the center of surrounding nodes directly
3785 // connected to that node along an element edge
3786 //=======================================================================
3788 void laplacianSmooth(const SMDS_MeshNode* theNode,
3789 const Handle(Geom_Surface)& theSurface,
3790 map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
3792 // find surrounding nodes
3794 TIDSortedElemSet nodeSet;
3795 SMESH_MeshEditor::GetLinkedNodes( theNode, nodeSet, SMDSAbs_Face );
3797 // compute new coodrs
3799 double coord[] = { 0., 0., 0. };
3800 TIDSortedElemSet::iterator nodeSetIt = nodeSet.begin();
3801 for ( ; nodeSetIt != nodeSet.end(); nodeSetIt++ ) {
3802 const SMDS_MeshNode* node = cast2Node(*nodeSetIt);
3803 if ( theSurface.IsNull() ) { // smooth in 3D
3804 coord[0] += node->X();
3805 coord[1] += node->Y();
3806 coord[2] += node->Z();
3808 else { // smooth in 2D
3809 ASSERT( theUVMap.find( node ) != theUVMap.end() );
3810 gp_XY* uv = theUVMap[ node ];
3811 coord[0] += uv->X();
3812 coord[1] += uv->Y();
3815 int nbNodes = nodeSet.size();
3818 coord[0] /= nbNodes;
3819 coord[1] /= nbNodes;
3821 if ( !theSurface.IsNull() ) {
3822 ASSERT( theUVMap.find( theNode ) != theUVMap.end() );
3823 theUVMap[ theNode ]->SetCoord( coord[0], coord[1] );
3824 gp_Pnt p3d = theSurface->Value( coord[0], coord[1] );
3830 coord[2] /= nbNodes;
3834 const_cast< SMDS_MeshNode* >( theNode )->setXYZ(coord[0],coord[1],coord[2]);
3837 //=======================================================================
3838 //function : centroidalSmooth
3839 //purpose : pulls theNode toward the element-area-weighted centroid of the
3840 // surrounding elements
3841 //=======================================================================
3843 void centroidalSmooth(const SMDS_MeshNode* theNode,
3844 const Handle(Geom_Surface)& theSurface,
3845 map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
3847 gp_XYZ aNewXYZ(0.,0.,0.);
3848 SMESH::Controls::Area anAreaFunc;
3849 double totalArea = 0.;
3854 SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(SMDSAbs_Face);
3855 while ( elemIt->more() )
3857 const SMDS_MeshElement* elem = elemIt->next();
3860 gp_XYZ elemCenter(0.,0.,0.);
3861 SMESH::Controls::TSequenceOfXYZ aNodePoints;
3862 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3863 int nn = elem->NbNodes();
3864 if(elem->IsQuadratic()) nn = nn/2;
3866 //while ( itN->more() ) {
3868 const SMDS_MeshNode* aNode = static_cast<const SMDS_MeshNode*>( itN->next() );
3870 gp_XYZ aP( aNode->X(), aNode->Y(), aNode->Z() );
3871 aNodePoints.push_back( aP );
3872 if ( !theSurface.IsNull() ) { // smooth in 2D
3873 ASSERT( theUVMap.find( aNode ) != theUVMap.end() );
3874 gp_XY* uv = theUVMap[ aNode ];
3875 aP.SetCoord( uv->X(), uv->Y(), 0. );
3879 double elemArea = anAreaFunc.GetValue( aNodePoints );
3880 totalArea += elemArea;
3882 aNewXYZ += elemCenter * elemArea;
3884 aNewXYZ /= totalArea;
3885 if ( !theSurface.IsNull() ) {
3886 theUVMap[ theNode ]->SetCoord( aNewXYZ.X(), aNewXYZ.Y() );
3887 aNewXYZ = theSurface->Value( aNewXYZ.X(), aNewXYZ.Y() ).XYZ();
3892 const_cast< SMDS_MeshNode* >( theNode )->setXYZ(aNewXYZ.X(),aNewXYZ.Y(),aNewXYZ.Z());
3895 //=======================================================================
3896 //function : getClosestUV
3897 //purpose : return UV of closest projection
3898 //=======================================================================
3900 static bool getClosestUV (Extrema_GenExtPS& projector,
3901 const gp_Pnt& point,
3904 projector.Perform( point );
3905 if ( projector.IsDone() ) {
3906 double u, v, minVal = DBL_MAX;
3907 for ( int i = projector.NbExt(); i > 0; i-- )
3908 if ( projector.SquareDistance( i ) < minVal ) {
3909 minVal = projector.SquareDistance( i );
3910 projector.Point( i ).Parameter( u, v );
3912 result.SetCoord( u, v );
3918 //=======================================================================
3920 //purpose : Smooth theElements during theNbIterations or until a worst
3921 // element has aspect ratio <= theTgtAspectRatio.
3922 // Aspect Ratio varies in range [1.0, inf].
3923 // If theElements is empty, the whole mesh is smoothed.
3924 // theFixedNodes contains additionally fixed nodes. Nodes built
3925 // on edges and boundary nodes are always fixed.
3926 //=======================================================================
3928 void SMESH_MeshEditor::Smooth (TIDSortedElemSet & theElems,
3929 set<const SMDS_MeshNode*> & theFixedNodes,
3930 const SmoothMethod theSmoothMethod,
3931 const int theNbIterations,
3932 double theTgtAspectRatio,
3935 myLastCreatedElems.Clear();
3936 myLastCreatedNodes.Clear();
3938 if ( theTgtAspectRatio < 1.0 )
3939 theTgtAspectRatio = 1.0;
3941 const double disttol = 1.e-16;
3943 SMESH::Controls::AspectRatio aQualityFunc;
3945 SMESHDS_Mesh* aMesh = GetMeshDS();
3947 if ( theElems.empty() ) {
3948 // add all faces to theElems
3949 SMDS_FaceIteratorPtr fIt = aMesh->facesIterator();
3950 while ( fIt->more() ) {
3951 const SMDS_MeshElement* face = fIt->next();
3952 theElems.insert( theElems.end(), face );
3955 // get all face ids theElems are on
3956 set< int > faceIdSet;
3957 TIDSortedElemSet::iterator itElem;
3959 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
3960 int fId = FindShape( *itElem );
3961 // check that corresponding submesh exists and a shape is face
3963 faceIdSet.find( fId ) == faceIdSet.end() &&
3964 aMesh->MeshElements( fId )) {
3965 TopoDS_Shape F = aMesh->IndexToShape( fId );
3966 if ( !F.IsNull() && F.ShapeType() == TopAbs_FACE )
3967 faceIdSet.insert( fId );
3970 faceIdSet.insert( 0 ); // to smooth elements that are not on any TopoDS_Face
3972 // ===============================================
3973 // smooth elements on each TopoDS_Face separately
3974 // ===============================================
3976 SMESH_MesherHelper helper( *GetMesh() );
3978 set< int >::reverse_iterator fId = faceIdSet.rbegin(); // treat 0 fId at the end
3979 for ( ; fId != faceIdSet.rend(); ++fId )
3981 // get face surface and submesh
3982 Handle(Geom_Surface) surface;
3983 SMESHDS_SubMesh* faceSubMesh = 0;
3986 double u1 = 0, u2 = 0, v1 = 0, v2 = 0;
3987 bool isUPeriodic = false, isVPeriodic = false;
3990 face = TopoDS::Face( aMesh->IndexToShape( *fId ));
3991 surface = BRep_Tool::Surface( face );
3992 faceSubMesh = aMesh->MeshElements( *fId );
3993 fToler2 = BRep_Tool::Tolerance( face );
3994 fToler2 *= fToler2 * 10.;
3995 isUPeriodic = surface->IsUPeriodic();
3996 // if ( isUPeriodic )
3997 // surface->UPeriod();
3998 isVPeriodic = surface->IsVPeriodic();
3999 // if ( isVPeriodic )
4000 // surface->VPeriod();
4001 surface->Bounds( u1, u2, v1, v2 );
4002 helper.SetSubShape( face );
4004 // ---------------------------------------------------------
4005 // for elements on a face, find movable and fixed nodes and
4006 // compute UV for them
4007 // ---------------------------------------------------------
4008 bool checkBoundaryNodes = false;
4009 bool isQuadratic = false;
4010 set<const SMDS_MeshNode*> setMovableNodes;
4011 map< const SMDS_MeshNode*, gp_XY* > uvMap, uvMap2;
4012 list< gp_XY > listUV; // uvs the 2 uvMaps refer to
4013 list< const SMDS_MeshElement* > elemsOnFace;
4015 Extrema_GenExtPS projector;
4016 GeomAdaptor_Surface surfAdaptor;
4017 if ( !surface.IsNull() ) {
4018 surfAdaptor.Load( surface );
4019 projector.Initialize( surfAdaptor, 20,20, 1e-5,1e-5 );
4021 int nbElemOnFace = 0;
4022 itElem = theElems.begin();
4023 // loop on not yet smoothed elements: look for elems on a face
4024 while ( itElem != theElems.end() )
4026 if ( faceSubMesh && nbElemOnFace == faceSubMesh->NbElements() )
4027 break; // all elements found
4029 const SMDS_MeshElement* elem = *itElem;
4030 if ( !elem || elem->GetType() != SMDSAbs_Face || elem->NbNodes() < 3 ||
4031 ( faceSubMesh && !faceSubMesh->Contains( elem ))) {
4035 elemsOnFace.push_back( elem );
4036 theElems.erase( itElem++ );
4040 isQuadratic = elem->IsQuadratic();
4042 // get movable nodes of elem
4043 const SMDS_MeshNode* node;
4044 SMDS_TypeOfPosition posType;
4045 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
4046 int nn = 0, nbn = elem->NbNodes();
4047 if(elem->IsQuadratic())
4049 while ( nn++ < nbn ) {
4050 node = static_cast<const SMDS_MeshNode*>( itN->next() );
4051 const SMDS_PositionPtr& pos = node->GetPosition();
4052 posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
4053 if (posType != SMDS_TOP_EDGE &&
4054 posType != SMDS_TOP_VERTEX &&
4055 theFixedNodes.find( node ) == theFixedNodes.end())
4057 // check if all faces around the node are on faceSubMesh
4058 // because a node on edge may be bound to face
4060 if ( faceSubMesh ) {
4061 SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
4062 while ( eIt->more() && all ) {
4063 const SMDS_MeshElement* e = eIt->next();
4064 all = faceSubMesh->Contains( e );
4068 setMovableNodes.insert( node );
4070 checkBoundaryNodes = true;
4072 if ( posType == SMDS_TOP_3DSPACE )
4073 checkBoundaryNodes = true;
4076 if ( surface.IsNull() )
4079 // get nodes to check UV
4080 list< const SMDS_MeshNode* > uvCheckNodes;
4081 const SMDS_MeshNode* nodeInFace = 0;
4082 itN = elem->nodesIterator();
4083 nn = 0; nbn = elem->NbNodes();
4084 if(elem->IsQuadratic())
4086 while ( nn++ < nbn ) {
4087 node = static_cast<const SMDS_MeshNode*>( itN->next() );
4088 if ( node->GetPosition()->GetDim() == 2 )
4090 if ( uvMap.find( node ) == uvMap.end() )
4091 uvCheckNodes.push_back( node );
4092 // add nodes of elems sharing node
4093 // SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
4094 // while ( eIt->more() ) {
4095 // const SMDS_MeshElement* e = eIt->next();
4096 // if ( e != elem ) {
4097 // SMDS_ElemIteratorPtr nIt = e->nodesIterator();
4098 // while ( nIt->more() ) {
4099 // const SMDS_MeshNode* n =
4100 // static_cast<const SMDS_MeshNode*>( nIt->next() );
4101 // if ( uvMap.find( n ) == uvMap.end() )
4102 // uvCheckNodes.push_back( n );
4108 list< const SMDS_MeshNode* >::iterator n = uvCheckNodes.begin();
4109 for ( ; n != uvCheckNodes.end(); ++n ) {
4112 const SMDS_PositionPtr& pos = node->GetPosition();
4113 posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
4117 bool toCheck = true;
4118 uv = helper.GetNodeUV( face, node, nodeInFace, &toCheck );
4120 // compute not existing UV
4121 bool project = ( posType == SMDS_TOP_3DSPACE );
4122 // double dist1 = DBL_MAX, dist2 = 0;
4123 // if ( posType != SMDS_TOP_3DSPACE ) {
4124 // dist1 = pNode.SquareDistance( surface->Value( uv.X(), uv.Y() ));
4125 // project = dist1 > fToler2;
4127 if ( project ) { // compute new UV
4129 gp_Pnt pNode = SMESH_TNodeXYZ( node );
4130 if ( !getClosestUV( projector, pNode, newUV )) {
4131 MESSAGE("Node Projection Failed " << node);
4135 newUV.SetX( ElCLib::InPeriod( newUV.X(), u1, u2 ));
4137 newUV.SetY( ElCLib::InPeriod( newUV.Y(), v1, v2 ));
4139 // if ( posType != SMDS_TOP_3DSPACE )
4140 // dist2 = pNode.SquareDistance( surface->Value( newUV.X(), newUV.Y() ));
4141 // if ( dist2 < dist1 )
4145 // store UV in the map
4146 listUV.push_back( uv );
4147 uvMap.insert( make_pair( node, &listUV.back() ));
4149 } // loop on not yet smoothed elements
4151 if ( !faceSubMesh || nbElemOnFace != faceSubMesh->NbElements() )
4152 checkBoundaryNodes = true;
4154 // fix nodes on mesh boundary
4156 if ( checkBoundaryNodes ) {
4157 map< SMESH_TLink, int > linkNbMap; // how many times a link encounters in elemsOnFace
4158 map< SMESH_TLink, int >::iterator link_nb;
4159 // put all elements links to linkNbMap
4160 list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4161 for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
4162 const SMDS_MeshElement* elem = (*elemIt);
4163 int nbn = elem->NbCornerNodes();
4164 // loop on elem links: insert them in linkNbMap
4165 for ( int iN = 0; iN < nbn; ++iN ) {
4166 const SMDS_MeshNode* n1 = elem->GetNode( iN );
4167 const SMDS_MeshNode* n2 = elem->GetNode(( iN+1 ) % nbn);
4168 SMESH_TLink link( n1, n2 );
4169 link_nb = linkNbMap.insert( make_pair( link, 0 )).first;
4173 // remove nodes that are in links encountered only once from setMovableNodes
4174 for ( link_nb = linkNbMap.begin(); link_nb != linkNbMap.end(); ++link_nb ) {
4175 if ( link_nb->second == 1 ) {
4176 setMovableNodes.erase( link_nb->first.node1() );
4177 setMovableNodes.erase( link_nb->first.node2() );
4182 // -----------------------------------------------------
4183 // for nodes on seam edge, compute one more UV ( uvMap2 );
4184 // find movable nodes linked to nodes on seam and which
4185 // are to be smoothed using the second UV ( uvMap2 )
4186 // -----------------------------------------------------
4188 set<const SMDS_MeshNode*> nodesNearSeam; // to smooth using uvMap2
4189 if ( !surface.IsNull() ) {
4190 TopExp_Explorer eExp( face, TopAbs_EDGE );
4191 for ( ; eExp.More(); eExp.Next() ) {
4192 TopoDS_Edge edge = TopoDS::Edge( eExp.Current() );
4193 if ( !BRep_Tool::IsClosed( edge, face ))
4195 SMESHDS_SubMesh* sm = aMesh->MeshElements( edge );
4196 if ( !sm ) continue;
4197 // find out which parameter varies for a node on seam
4200 Handle(Geom2d_Curve) pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
4201 if ( pcurve.IsNull() ) continue;
4202 uv1 = pcurve->Value( f );
4204 pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
4205 if ( pcurve.IsNull() ) continue;
4206 uv2 = pcurve->Value( f );
4207 int iPar = Abs( uv1.X() - uv2.X() ) > Abs( uv1.Y() - uv2.Y() ) ? 1 : 2;
4209 if ( uv1.Coord( iPar ) > uv2.Coord( iPar ))
4210 std::swap( uv1, uv2 );
4211 // get nodes on seam and its vertices
4212 list< const SMDS_MeshNode* > seamNodes;
4213 SMDS_NodeIteratorPtr nSeamIt = sm->GetNodes();
4214 while ( nSeamIt->more() ) {
4215 const SMDS_MeshNode* node = nSeamIt->next();
4216 if ( !isQuadratic || !IsMedium( node ))
4217 seamNodes.push_back( node );
4219 TopExp_Explorer vExp( edge, TopAbs_VERTEX );
4220 for ( ; vExp.More(); vExp.Next() ) {
4221 sm = aMesh->MeshElements( vExp.Current() );
4223 nSeamIt = sm->GetNodes();
4224 while ( nSeamIt->more() )
4225 seamNodes.push_back( nSeamIt->next() );
4228 // loop on nodes on seam
4229 list< const SMDS_MeshNode* >::iterator noSeIt = seamNodes.begin();
4230 for ( ; noSeIt != seamNodes.end(); ++noSeIt ) {
4231 const SMDS_MeshNode* nSeam = *noSeIt;
4232 map< const SMDS_MeshNode*, gp_XY* >::iterator n_uv = uvMap.find( nSeam );
4233 if ( n_uv == uvMap.end() )
4236 n_uv->second->SetCoord( iPar, uv1.Coord( iPar ));
4237 // set the second UV
4238 listUV.push_back( *n_uv->second );
4239 listUV.back().SetCoord( iPar, uv2.Coord( iPar ));
4240 if ( uvMap2.empty() )
4241 uvMap2 = uvMap; // copy the uvMap contents
4242 uvMap2[ nSeam ] = &listUV.back();
4244 // collect movable nodes linked to ones on seam in nodesNearSeam
4245 SMDS_ElemIteratorPtr eIt = nSeam->GetInverseElementIterator(SMDSAbs_Face);
4246 while ( eIt->more() ) {
4247 const SMDS_MeshElement* e = eIt->next();
4248 int nbUseMap1 = 0, nbUseMap2 = 0;
4249 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
4250 int nn = 0, nbn = e->NbNodes();
4251 if(e->IsQuadratic()) nbn = nbn/2;
4252 while ( nn++ < nbn )
4254 const SMDS_MeshNode* n =
4255 static_cast<const SMDS_MeshNode*>( nIt->next() );
4257 setMovableNodes.find( n ) == setMovableNodes.end() )
4259 // add only nodes being closer to uv2 than to uv1
4260 // gp_Pnt pMid (0.5 * ( n->X() + nSeam->X() ),
4261 // 0.5 * ( n->Y() + nSeam->Y() ),
4262 // 0.5 * ( n->Z() + nSeam->Z() ));
4264 // getClosestUV( projector, pMid, uv );
4265 double x = uvMap[ n ]->Coord( iPar );
4266 if ( Abs( uv1.Coord( iPar ) - x ) >
4267 Abs( uv2.Coord( iPar ) - x )) {
4268 nodesNearSeam.insert( n );
4274 // for centroidalSmooth all element nodes must
4275 // be on one side of a seam
4276 if ( theSmoothMethod == CENTROIDAL && nbUseMap1 && nbUseMap2 ) {
4277 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
4279 while ( nn++ < nbn ) {
4280 const SMDS_MeshNode* n =
4281 static_cast<const SMDS_MeshNode*>( nIt->next() );
4282 setMovableNodes.erase( n );
4286 } // loop on nodes on seam
4287 } // loop on edge of a face
4288 } // if ( !face.IsNull() )
4290 if ( setMovableNodes.empty() ) {
4291 MESSAGE( "Face id : " << *fId << " - NO SMOOTHING: no nodes to move!!!");
4292 continue; // goto next face
4300 double maxRatio = -1., maxDisplacement = -1.;
4301 set<const SMDS_MeshNode*>::iterator nodeToMove;
4302 for ( it = 0; it < theNbIterations; it++ ) {
4303 maxDisplacement = 0.;
4304 nodeToMove = setMovableNodes.begin();
4305 for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
4306 const SMDS_MeshNode* node = (*nodeToMove);
4307 gp_XYZ aPrevPos ( node->X(), node->Y(), node->Z() );
4310 bool map2 = ( nodesNearSeam.find( node ) != nodesNearSeam.end() );
4311 if ( theSmoothMethod == LAPLACIAN )
4312 laplacianSmooth( node, surface, map2 ? uvMap2 : uvMap );
4314 centroidalSmooth( node, surface, map2 ? uvMap2 : uvMap );
4316 // node displacement
4317 gp_XYZ aNewPos ( node->X(), node->Y(), node->Z() );
4318 Standard_Real aDispl = (aPrevPos - aNewPos).SquareModulus();
4319 if ( aDispl > maxDisplacement )
4320 maxDisplacement = aDispl;
4322 // no node movement => exit
4323 //if ( maxDisplacement < 1.e-16 ) {
4324 if ( maxDisplacement < disttol ) {
4325 MESSAGE("-- no node movement --");
4329 // check elements quality
4331 list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4332 for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
4333 const SMDS_MeshElement* elem = (*elemIt);
4334 if ( !elem || elem->GetType() != SMDSAbs_Face )
4336 SMESH::Controls::TSequenceOfXYZ aPoints;
4337 if ( aQualityFunc.GetPoints( elem, aPoints )) {
4338 double aValue = aQualityFunc.GetValue( aPoints );
4339 if ( aValue > maxRatio )
4343 if ( maxRatio <= theTgtAspectRatio ) {
4344 //MESSAGE("-- quality achieved --");
4347 if (it+1 == theNbIterations) {
4348 //MESSAGE("-- Iteration limit exceeded --");
4350 } // smoothing iterations
4352 // MESSAGE(" Face id: " << *fId <<
4353 // " Nb iterstions: " << it <<
4354 // " Displacement: " << maxDisplacement <<
4355 // " Aspect Ratio " << maxRatio);
4357 // ---------------------------------------
4358 // new nodes positions are computed,
4359 // record movement in DS and set new UV
4360 // ---------------------------------------
4361 nodeToMove = setMovableNodes.begin();
4362 for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
4363 SMDS_MeshNode* node = const_cast< SMDS_MeshNode* > (*nodeToMove);
4364 aMesh->MoveNode( node, node->X(), node->Y(), node->Z() );
4365 map< const SMDS_MeshNode*, gp_XY* >::iterator node_uv = uvMap.find( node );
4366 if ( node_uv != uvMap.end() ) {
4367 gp_XY* uv = node_uv->second;
4369 ( SMDS_PositionPtr( new SMDS_FacePosition( uv->X(), uv->Y() )));
4373 // move medium nodes of quadratic elements
4376 vector<const SMDS_MeshNode*> nodes;
4378 list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4379 for ( ; elemIt != elemsOnFace.end(); ++elemIt )
4381 const SMDS_MeshElement* QF = *elemIt;
4382 if ( QF->IsQuadratic() )
4384 nodes.assign( SMDS_MeshElement::iterator( QF->interlacedNodesElemIterator() ),
4385 SMDS_MeshElement::iterator() );
4386 nodes.push_back( nodes[0] );
4388 for (size_t i = 1; i < nodes.size(); i += 2 ) // i points to a medium node
4390 if ( !surface.IsNull() )
4392 gp_XY uv1 = helper.GetNodeUV( face, nodes[i-1], nodes[i+1], &checkUV );
4393 gp_XY uv2 = helper.GetNodeUV( face, nodes[i+1], nodes[i-1], &checkUV );
4394 gp_XY uv = helper.GetMiddleUV( surface, uv1, uv2 );
4395 xyz = surface->Value( uv.X(), uv.Y() );
4398 xyz = 0.5 * ( SMESH_TNodeXYZ( nodes[i-1] ) + SMESH_TNodeXYZ( nodes[i+1] ));
4400 if (( SMESH_TNodeXYZ( nodes[i] ) - xyz.XYZ() ).Modulus() > disttol )
4401 // we have to move a medium node
4402 aMesh->MoveNode( nodes[i], xyz.X(), xyz.Y(), xyz.Z() );
4408 } // loop on face ids
4414 //=======================================================================
4415 //function : isReverse
4416 //purpose : Return true if normal of prevNodes is not co-directied with
4417 // gp_Vec(prevNodes[iNotSame],nextNodes[iNotSame]).
4418 // iNotSame is where prevNodes and nextNodes are different.
4419 // If result is true then future volume orientation is OK
4420 //=======================================================================
4422 bool isReverse(const SMDS_MeshElement* face,
4423 const vector<const SMDS_MeshNode*>& prevNodes,
4424 const vector<const SMDS_MeshNode*>& nextNodes,
4428 SMESH_TNodeXYZ pP = prevNodes[ iNotSame ];
4429 SMESH_TNodeXYZ pN = nextNodes[ iNotSame ];
4430 gp_XYZ extrDir( pN - pP ), faceNorm;
4431 SMESH_MeshAlgos::FaceNormal( face, faceNorm, /*normalized=*/false );
4433 return faceNorm * extrDir < 0.0;
4436 //================================================================================
4438 * \brief Assure that theElemSets[0] holds elements, not nodes
4440 //================================================================================
4442 void setElemsFirst( TIDSortedElemSet theElemSets[2] )
4444 if ( !theElemSets[0].empty() &&
4445 (*theElemSets[0].begin())->GetType() == SMDSAbs_Node )
4447 std::swap( theElemSets[0], theElemSets[1] );
4449 else if ( !theElemSets[1].empty() &&
4450 (*theElemSets[1].begin())->GetType() != SMDSAbs_Node )
4452 std::swap( theElemSets[0], theElemSets[1] );
4457 //=======================================================================
4459 * \brief Create elements by sweeping an element
4460 * \param elem - element to sweep
4461 * \param newNodesItVec - nodes generated from each node of the element
4462 * \param newElems - generated elements
4463 * \param nbSteps - number of sweeping steps
4464 * \param srcElements - to append elem for each generated element
4466 //=======================================================================
4468 void SMESH_MeshEditor::sweepElement(const SMDS_MeshElement* elem,
4469 const vector<TNodeOfNodeListMapItr> & newNodesItVec,
4470 list<const SMDS_MeshElement*>& newElems,
4471 const size_t nbSteps,
4472 SMESH_SequenceOfElemPtr& srcElements)
4474 SMESHDS_Mesh* aMesh = GetMeshDS();
4476 const int nbNodes = elem->NbNodes();
4477 const int nbCorners = elem->NbCornerNodes();
4478 SMDSAbs_EntityType baseType = elem->GetEntityType(); /* it can change in case of
4479 polyhedron creation !!! */
4480 // Loop on elem nodes:
4481 // find new nodes and detect same nodes indices
4482 vector < list< const SMDS_MeshNode* >::const_iterator > itNN( nbNodes );
4483 vector<const SMDS_MeshNode*> prevNod( nbNodes );
4484 vector<const SMDS_MeshNode*> nextNod( nbNodes );
4485 vector<const SMDS_MeshNode*> midlNod( nbNodes );
4487 int iNode, nbSame = 0, nbDouble = 0, iNotSameNode = 0;
4488 vector<int> sames(nbNodes);
4489 vector<bool> isSingleNode(nbNodes);
4491 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4492 TNodeOfNodeListMapItr nnIt = newNodesItVec[ iNode ];
4493 const SMDS_MeshNode* node = nnIt->first;
4494 const list< const SMDS_MeshNode* > & listNewNodes = nnIt->second;
4495 if ( listNewNodes.empty() )
4498 itNN [ iNode ] = listNewNodes.begin();
4499 prevNod[ iNode ] = node;
4500 nextNod[ iNode ] = listNewNodes.front();
4502 isSingleNode[iNode] = (listNewNodes.size()==nbSteps); /* medium node of quadratic or
4503 corner node of linear */
4504 if ( prevNod[ iNode ] != nextNod [ iNode ])
4505 nbDouble += !isSingleNode[iNode];
4507 if( iNode < nbCorners ) { // check corners only
4508 if ( prevNod[ iNode ] == nextNod [ iNode ])
4509 sames[nbSame++] = iNode;
4511 iNotSameNode = iNode;
4515 if ( nbSame == nbNodes || nbSame > 2) {
4516 MESSAGE( " Too many same nodes of element " << elem->GetID() );
4520 if ( elem->GetType() == SMDSAbs_Face && !isReverse( elem, prevNod, nextNod, iNotSameNode ))
4522 // fix nodes order to have bottom normal external
4523 if ( baseType == SMDSEntity_Polygon )
4525 std::reverse( itNN.begin(), itNN.end() );
4526 std::reverse( prevNod.begin(), prevNod.end() );
4527 std::reverse( midlNod.begin(), midlNod.end() );
4528 std::reverse( nextNod.begin(), nextNod.end() );
4529 std::reverse( isSingleNode.begin(), isSingleNode.end() );
4533 const vector<int>& ind = SMDS_MeshCell::reverseSmdsOrder( baseType, nbNodes );
4534 SMDS_MeshCell::applyInterlace( ind, itNN );
4535 SMDS_MeshCell::applyInterlace( ind, prevNod );
4536 SMDS_MeshCell::applyInterlace( ind, nextNod );
4537 SMDS_MeshCell::applyInterlace( ind, midlNod );
4538 SMDS_MeshCell::applyInterlace( ind, isSingleNode );
4541 sames[nbSame] = iNotSameNode;
4542 for ( int j = 0; j <= nbSame; ++j )
4543 for ( size_t i = 0; i < ind.size(); ++i )
4544 if ( ind[i] == sames[j] )
4549 iNotSameNode = sames[nbSame];
4553 else if ( elem->GetType() == SMDSAbs_Edge )
4555 // orient a new face same as adjacent one
4557 const SMDS_MeshElement* e;
4558 TIDSortedElemSet dummy;
4559 if (( e = SMESH_MeshAlgos::FindFaceInSet( nextNod[0], prevNod[0], dummy,dummy, &i1, &i2 )) ||
4560 ( e = SMESH_MeshAlgos::FindFaceInSet( prevNod[1], nextNod[1], dummy,dummy, &i1, &i2 )) ||
4561 ( e = SMESH_MeshAlgos::FindFaceInSet( prevNod[0], prevNod[1], dummy,dummy, &i1, &i2 )))
4563 // there is an adjacent face, check order of nodes in it
4564 bool sameOrder = ( Abs( i2 - i1 ) == 1 ) ? ( i2 > i1 ) : ( i2 < i1 );
4567 std::swap( itNN[0], itNN[1] );
4568 std::swap( prevNod[0], prevNod[1] );
4569 std::swap( nextNod[0], nextNod[1] );
4570 #if defined(__APPLE__)
4571 std::swap( isSingleNode[0], isSingleNode[1] );
4573 isSingleNode.swap( isSingleNode[0], isSingleNode[1] );
4576 sames[0] = 1 - sames[0];
4577 iNotSameNode = 1 - iNotSameNode;
4582 int iSameNode = 0, iBeforeSame = 0, iAfterSame = 0, iOpposSame = 0;
4584 iSameNode = sames[ nbSame-1 ];
4585 iBeforeSame = ( iSameNode + nbCorners - 1 ) % nbCorners;
4586 iAfterSame = ( iSameNode + 1 ) % nbCorners;
4587 iOpposSame = ( iSameNode - 2 < 0 ? iSameNode + 2 : iSameNode - 2 );
4590 if ( baseType == SMDSEntity_Polygon )
4592 if ( nbNodes == 3 ) baseType = SMDSEntity_Triangle;
4593 else if ( nbNodes == 4 ) baseType = SMDSEntity_Quadrangle;
4595 else if ( baseType == SMDSEntity_Quad_Polygon )
4597 if ( nbNodes == 6 ) baseType = SMDSEntity_Quad_Triangle;
4598 else if ( nbNodes == 8 ) baseType = SMDSEntity_Quad_Quadrangle;
4601 // make new elements
4602 for ( size_t iStep = 0; iStep < nbSteps; iStep++ )
4605 for ( iNode = 0; iNode < nbNodes; iNode++ )
4607 midlNod[ iNode ] = isSingleNode[iNode] ? 0 : *itNN[ iNode ]++;
4608 nextNod[ iNode ] = *itNN[ iNode ]++;
4611 SMDS_MeshElement* aNewElem = 0;
4612 /*if(!elem->IsPoly())*/ {
4613 switch ( baseType ) {
4615 case SMDSEntity_Node: { // sweep NODE
4616 if ( nbSame == 0 ) {
4617 if ( isSingleNode[0] )
4618 aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ] );
4620 aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ], midlNod[ 0 ] );
4626 case SMDSEntity_Edge: { // sweep EDGE
4627 if ( nbDouble == 0 )
4629 if ( nbSame == 0 ) // ---> quadrangle
4630 aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
4631 nextNod[ 1 ], nextNod[ 0 ] );
4632 else // ---> triangle
4633 aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
4634 nextNod[ iNotSameNode ] );
4636 else // ---> polygon
4638 vector<const SMDS_MeshNode*> poly_nodes;
4639 poly_nodes.push_back( prevNod[0] );
4640 poly_nodes.push_back( prevNod[1] );
4641 if ( prevNod[1] != nextNod[1] )
4643 if ( midlNod[1]) poly_nodes.push_back( midlNod[1]);
4644 poly_nodes.push_back( nextNod[1] );
4646 if ( prevNod[0] != nextNod[0] )
4648 poly_nodes.push_back( nextNod[0] );
4649 if ( midlNod[0]) poly_nodes.push_back( midlNod[0]);
4651 switch ( poly_nodes.size() ) {
4653 aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ], poly_nodes[ 2 ]);
4656 aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ],
4657 poly_nodes[ 2 ], poly_nodes[ 3 ]);
4660 aNewElem = aMesh->AddPolygonalFace (poly_nodes);
4665 case SMDSEntity_Triangle: // TRIANGLE --->
4667 if ( nbDouble > 0 ) break;
4668 if ( nbSame == 0 ) // ---> pentahedron
4669 aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
4670 nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ] );
4672 else if ( nbSame == 1 ) // ---> pyramid
4673 aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
4674 nextNod[ iAfterSame ], nextNod[ iBeforeSame ],
4675 nextNod[ iSameNode ]);
4677 else // 2 same nodes: ---> tetrahedron
4678 aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
4679 nextNod[ iNotSameNode ]);
4682 case SMDSEntity_Quad_Edge: // sweep quadratic EDGE --->
4686 if ( nbDouble+nbSame == 2 )
4688 if(nbSame==0) { // ---> quadratic quadrangle
4689 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
4690 prevNod[2], midlNod[1], nextNod[2], midlNod[0]);
4692 else { //(nbSame==1) // ---> quadratic triangle
4694 return; // medium node on axis
4696 else if(sames[0]==0)
4697 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1],
4698 prevNod[2], midlNod[1], nextNod[2] );
4700 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[0],
4701 prevNod[2], nextNod[2], midlNod[0]);
4704 else if ( nbDouble == 3 )
4706 if ( nbSame == 0 ) { // ---> bi-quadratic quadrangle
4707 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
4708 prevNod[2], midlNod[1], nextNod[2], midlNod[0], midlNod[2]);
4715 case SMDSEntity_Quadrangle: { // sweep QUADRANGLE --->
4716 if ( nbDouble > 0 ) break;
4718 if ( nbSame == 0 ) // ---> hexahedron
4719 aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ], prevNod[ 3 ],
4720 nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ], nextNod[ 3 ]);
4722 else if ( nbSame == 1 ) { // ---> pyramid + pentahedron
4723 aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
4724 nextNod[ iAfterSame ], nextNod[ iBeforeSame ],
4725 nextNod[ iSameNode ]);
4726 newElems.push_back( aNewElem );
4727 aNewElem = aMesh->AddVolume (prevNod[ iAfterSame ], prevNod[ iOpposSame ],
4728 prevNod[ iBeforeSame ], nextNod[ iAfterSame ],
4729 nextNod[ iOpposSame ], nextNod[ iBeforeSame ] );
4731 else if ( nbSame == 2 ) { // ---> pentahedron
4732 if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] )
4733 // iBeforeSame is same too
4734 aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iOpposSame ],
4735 nextNod[ iOpposSame ], prevNod[ iSameNode ],
4736 prevNod[ iAfterSame ], nextNod[ iAfterSame ]);
4738 // iAfterSame is same too
4739 aNewElem = aMesh->AddVolume (prevNod[ iSameNode ], prevNod[ iBeforeSame ],
4740 nextNod[ iBeforeSame ], prevNod[ iAfterSame ],
4741 prevNod[ iOpposSame ], nextNod[ iOpposSame ]);
4745 case SMDSEntity_Quad_Triangle: // sweep (Bi)Quadratic TRIANGLE --->
4746 case SMDSEntity_BiQuad_Triangle: /* ??? */ {
4747 if ( nbDouble+nbSame != 3 ) break;
4749 // ---> pentahedron with 15 nodes
4750 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
4751 nextNod[0], nextNod[1], nextNod[2],
4752 prevNod[3], prevNod[4], prevNod[5],
4753 nextNod[3], nextNod[4], nextNod[5],
4754 midlNod[0], midlNod[1], midlNod[2]);
4756 else if(nbSame==1) {
4757 // ---> 2d order pyramid of 13 nodes
4758 int apex = iSameNode;
4759 int i0 = ( apex + 1 ) % nbCorners;
4760 int i1 = ( apex - 1 + nbCorners ) % nbCorners;
4764 aNewElem = aMesh->AddVolume(prevNod[i1], prevNod[i0],
4765 nextNod[i0], nextNod[i1], prevNod[apex],
4766 prevNod[i01], midlNod[i0],
4767 nextNod[i01], midlNod[i1],
4768 prevNod[i1a], prevNod[i0a],
4769 nextNod[i0a], nextNod[i1a]);
4771 else if(nbSame==2) {
4772 // ---> 2d order tetrahedron of 10 nodes
4773 int n1 = iNotSameNode;
4774 int n2 = ( n1 + 1 ) % nbCorners;
4775 int n3 = ( n1 + nbCorners - 1 ) % nbCorners;
4779 aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], prevNod[n3], nextNod[n1],
4780 prevNod[n12], prevNod[n23], prevNod[n31],
4781 midlNod[n1], nextNod[n12], nextNod[n31]);
4785 case SMDSEntity_Quad_Quadrangle: { // sweep Quadratic QUADRANGLE --->
4787 if ( nbDouble != 4 ) break;
4788 // ---> hexahedron with 20 nodes
4789 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
4790 nextNod[0], nextNod[1], nextNod[2], nextNod[3],
4791 prevNod[4], prevNod[5], prevNod[6], prevNod[7],
4792 nextNod[4], nextNod[5], nextNod[6], nextNod[7],
4793 midlNod[0], midlNod[1], midlNod[2], midlNod[3]);
4795 else if(nbSame==1) {
4796 // ---> pyramid + pentahedron - can not be created since it is needed
4797 // additional middle node at the center of face
4798 //INFOS( " Sweep for face " << elem->GetID() << " can not be created" );
4801 else if( nbSame == 2 ) {
4802 if ( nbDouble != 2 ) break;
4803 // ---> 2d order Pentahedron with 15 nodes
4805 if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] ) {
4806 // iBeforeSame is same too
4813 // iAfterSame is same too
4823 aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], nextNod[n2],
4824 prevNod[n4], prevNod[n5], nextNod[n5],
4825 prevNod[n12], midlNod[n2], nextNod[n12],
4826 prevNod[n45], midlNod[n5], nextNod[n45],
4827 prevNod[n14], prevNod[n25], nextNod[n25]);
4831 case SMDSEntity_BiQuad_Quadrangle: { // sweep BiQuadratic QUADRANGLE --->
4833 if( nbSame == 0 && nbDouble == 9 ) {
4834 // ---> tri-quadratic hexahedron with 27 nodes
4835 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
4836 nextNod[0], nextNod[1], nextNod[2], nextNod[3],
4837 prevNod[4], prevNod[5], prevNod[6], prevNod[7],
4838 nextNod[4], nextNod[5], nextNod[6], nextNod[7],
4839 midlNod[0], midlNod[1], midlNod[2], midlNod[3],
4840 prevNod[8], // bottom center
4841 midlNod[4], midlNod[5], midlNod[6], midlNod[7],
4842 nextNod[8], // top center
4843 midlNod[8]);// elem center
4851 case SMDSEntity_Polygon: { // sweep POLYGON
4853 if ( nbNodes == 6 && nbSame == 0 && nbDouble == 0 ) {
4854 // ---> hexagonal prism
4855 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
4856 prevNod[3], prevNod[4], prevNod[5],
4857 nextNod[0], nextNod[1], nextNod[2],
4858 nextNod[3], nextNod[4], nextNod[5]);
4862 case SMDSEntity_Ball:
4867 } // switch ( baseType )
4870 if ( !aNewElem && elem->GetType() == SMDSAbs_Face ) // try to create a polyherdal prism
4872 if ( baseType != SMDSEntity_Polygon )
4874 const std::vector<int>& ind = SMDS_MeshCell::interlacedSmdsOrder(baseType,nbNodes);
4875 SMDS_MeshCell::applyInterlace( ind, prevNod );
4876 SMDS_MeshCell::applyInterlace( ind, nextNod );
4877 SMDS_MeshCell::applyInterlace( ind, midlNod );
4878 SMDS_MeshCell::applyInterlace( ind, itNN );
4879 SMDS_MeshCell::applyInterlace( ind, isSingleNode );
4880 baseType = SMDSEntity_Polygon; // WARNING: change baseType !!!!
4882 vector<const SMDS_MeshNode*> polyedre_nodes (nbNodes*2 + 4*nbNodes);
4883 vector<int> quantities (nbNodes + 2);
4884 polyedre_nodes.clear();
4888 for (int inode = 0; inode < nbNodes; inode++)
4889 polyedre_nodes.push_back( prevNod[inode] );
4890 quantities.push_back( nbNodes );
4893 polyedre_nodes.push_back( nextNod[0] );
4894 for (int inode = nbNodes; inode-1; --inode )
4895 polyedre_nodes.push_back( nextNod[inode-1] );
4896 quantities.push_back( nbNodes );
4904 const int iQuad = elem->IsQuadratic();
4905 for (int iface = 0; iface < nbNodes; iface += 1+iQuad )
4907 const int prevNbNodes = polyedre_nodes.size(); // to detect degenerated face
4908 int inextface = (iface+1+iQuad) % nbNodes;
4909 int imid = (iface+1) % nbNodes;
4910 polyedre_nodes.push_back( prevNod[inextface] ); // 0
4911 if ( iQuad ) polyedre_nodes.push_back( prevNod[imid] ); // 4
4912 polyedre_nodes.push_back( prevNod[iface] ); // 1
4913 if ( prevNod[iface] != nextNod[iface] ) // 1 != 2
4915 if ( midlNod[ iface ]) polyedre_nodes.push_back( midlNod[ iface ]); // 5
4916 polyedre_nodes.push_back( nextNod[iface] ); // 2
4918 if ( iQuad ) polyedre_nodes.push_back( nextNod[imid] ); // 6
4919 if ( prevNod[inextface] != nextNod[inextface] ) // 0 != 3
4921 polyedre_nodes.push_back( nextNod[inextface] ); // 3
4922 if ( midlNod[ inextface ]) polyedre_nodes.push_back( midlNod[ inextface ]);// 7
4924 const int nbFaceNodes = polyedre_nodes.size() - prevNbNodes;
4925 if ( nbFaceNodes > 2 )
4926 quantities.push_back( nbFaceNodes );
4927 else // degenerated face
4928 polyedre_nodes.resize( prevNbNodes );
4930 aNewElem = aMesh->AddPolyhedralVolume (polyedre_nodes, quantities);
4932 } // try to create a polyherdal prism
4935 newElems.push_back( aNewElem );
4936 myLastCreatedElems.Append(aNewElem);
4937 srcElements.Append( elem );
4940 // set new prev nodes
4941 for ( iNode = 0; iNode < nbNodes; iNode++ )
4942 prevNod[ iNode ] = nextNod[ iNode ];
4947 //=======================================================================
4949 * \brief Create 1D and 2D elements around swept elements
4950 * \param mapNewNodes - source nodes and ones generated from them
4951 * \param newElemsMap - source elements and ones generated from them
4952 * \param elemNewNodesMap - nodes generated from each node of each element
4953 * \param elemSet - all swept elements
4954 * \param nbSteps - number of sweeping steps
4955 * \param srcElements - to append elem for each generated element
4957 //=======================================================================
4959 void SMESH_MeshEditor::makeWalls (TNodeOfNodeListMap & mapNewNodes,
4960 TTElemOfElemListMap & newElemsMap,
4961 TElemOfVecOfNnlmiMap & elemNewNodesMap,
4962 TIDSortedElemSet& elemSet,
4964 SMESH_SequenceOfElemPtr& srcElements)
4966 ASSERT( newElemsMap.size() == elemNewNodesMap.size() );
4967 SMESHDS_Mesh* aMesh = GetMeshDS();
4969 // Find nodes belonging to only one initial element - sweep them into edges.
4971 TNodeOfNodeListMapItr nList = mapNewNodes.begin();
4972 for ( ; nList != mapNewNodes.end(); nList++ )
4974 const SMDS_MeshNode* node =
4975 static_cast<const SMDS_MeshNode*>( nList->first );
4976 if ( newElemsMap.count( node ))
4977 continue; // node was extruded into edge
4978 SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator();
4979 int nbInitElems = 0;
4980 const SMDS_MeshElement* el = 0;
4981 SMDSAbs_ElementType highType = SMDSAbs_Edge; // count most complex elements only
4982 while ( eIt->more() && nbInitElems < 2 ) {
4983 const SMDS_MeshElement* e = eIt->next();
4984 SMDSAbs_ElementType type = e->GetType();
4985 if ( type == SMDSAbs_Volume ||
4989 if ( type > highType ) {
4996 if ( nbInitElems == 1 ) {
4997 bool NotCreateEdge = el && el->IsMediumNode(node);
4998 if(!NotCreateEdge) {
4999 vector<TNodeOfNodeListMapItr> newNodesItVec( 1, nList );
5000 list<const SMDS_MeshElement*> newEdges;
5001 sweepElement( node, newNodesItVec, newEdges, nbSteps, srcElements );
5006 // Make a ceiling for each element ie an equal element of last new nodes.
5007 // Find free links of faces - make edges and sweep them into faces.
5009 ElemFeatures polyFace( SMDSAbs_Face, /*isPoly=*/true ), anyFace;
5011 TTElemOfElemListMap::iterator itElem = newElemsMap.begin();
5012 TElemOfVecOfNnlmiMap::iterator itElemNodes = elemNewNodesMap.begin();
5013 for ( ; itElem != newElemsMap.end(); itElem++, itElemNodes++ )
5015 const SMDS_MeshElement* elem = itElem->first;
5016 vector<TNodeOfNodeListMapItr>& vecNewNodes = itElemNodes->second;
5018 if(itElem->second.size()==0) continue;
5020 const bool isQuadratic = elem->IsQuadratic();
5022 if ( elem->GetType() == SMDSAbs_Edge ) {
5023 // create a ceiling edge
5024 if ( !isQuadratic ) {
5025 if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
5026 vecNewNodes[ 1 ]->second.back())) {
5027 myLastCreatedElems.Append(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
5028 vecNewNodes[ 1 ]->second.back()));
5029 srcElements.Append( elem );
5033 if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
5034 vecNewNodes[ 1 ]->second.back(),
5035 vecNewNodes[ 2 ]->second.back())) {
5036 myLastCreatedElems.Append(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
5037 vecNewNodes[ 1 ]->second.back(),
5038 vecNewNodes[ 2 ]->second.back()));
5039 srcElements.Append( elem );
5043 if ( elem->GetType() != SMDSAbs_Face )
5046 bool hasFreeLinks = false;
5048 TIDSortedElemSet avoidSet;
5049 avoidSet.insert( elem );
5051 set<const SMDS_MeshNode*> aFaceLastNodes;
5052 int iNode, nbNodes = vecNewNodes.size();
5053 if ( !isQuadratic ) {
5054 // loop on the face nodes
5055 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
5056 aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
5057 // look for free links of the face
5058 int iNext = ( iNode + 1 == nbNodes ) ? 0 : iNode + 1;
5059 const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
5060 const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
5061 // check if a link n1-n2 is free
5062 if ( ! SMESH_MeshAlgos::FindFaceInSet ( n1, n2, elemSet, avoidSet )) {
5063 hasFreeLinks = true;
5064 // make a new edge and a ceiling for a new edge
5065 const SMDS_MeshElement* edge;
5066 if ( ! ( edge = aMesh->FindEdge( n1, n2 ))) {
5067 myLastCreatedElems.Append( edge = aMesh->AddEdge( n1, n2 )); // free link edge
5068 srcElements.Append( myLastCreatedElems.Last() );
5070 n1 = vecNewNodes[ iNode ]->second.back();
5071 n2 = vecNewNodes[ iNext ]->second.back();
5072 if ( !aMesh->FindEdge( n1, n2 )) {
5073 myLastCreatedElems.Append(aMesh->AddEdge( n1, n2 )); // new edge ceiling
5074 srcElements.Append( edge );
5079 else { // elem is quadratic face
5080 int nbn = nbNodes/2;
5081 for ( iNode = 0; iNode < nbn; iNode++ ) {
5082 aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
5083 int iNext = ( iNode + 1 == nbn ) ? 0 : iNode + 1;
5084 const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
5085 const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
5086 const SMDS_MeshNode* n3 = vecNewNodes[ iNode+nbn ]->first;
5087 // check if a link is free
5088 if ( ! SMESH_MeshAlgos::FindFaceInSet ( n1, n2, elemSet, avoidSet ) &&
5089 ! SMESH_MeshAlgos::FindFaceInSet ( n1, n3, elemSet, avoidSet ) &&
5090 ! SMESH_MeshAlgos::FindFaceInSet ( n3, n2, elemSet, avoidSet ) ) {
5091 hasFreeLinks = true;
5092 // make an edge and a ceiling for a new edge
5094 if ( !aMesh->FindEdge( n1, n2, n3 )) {
5095 myLastCreatedElems.Append(aMesh->AddEdge( n1, n2, n3 )); // free link edge
5096 srcElements.Append( elem );
5098 n1 = vecNewNodes[ iNode ]->second.back();
5099 n2 = vecNewNodes[ iNext ]->second.back();
5100 n3 = vecNewNodes[ iNode+nbn ]->second.back();
5101 if ( !aMesh->FindEdge( n1, n2, n3 )) {
5102 myLastCreatedElems.Append(aMesh->AddEdge( n1, n2, n3 )); // ceiling edge
5103 srcElements.Append( elem );
5107 for ( iNode = nbn; iNode < nbNodes; iNode++ ) {
5108 aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
5112 // sweep free links into faces
5114 if ( hasFreeLinks ) {
5115 list<const SMDS_MeshElement*> & newVolumes = itElem->second;
5116 int iVol, volNb, nbVolumesByStep = newVolumes.size() / nbSteps;
5118 set<const SMDS_MeshNode*> initNodeSet, topNodeSet, faceNodeSet;
5119 set<const SMDS_MeshNode*> initNodeSetNoCenter/*, topNodeSetNoCenter*/;
5120 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
5121 initNodeSet.insert( vecNewNodes[ iNode ]->first );
5122 topNodeSet .insert( vecNewNodes[ iNode ]->second.back() );
5124 if ( isQuadratic && nbNodes % 2 ) { // node set for the case of a biquadratic
5125 initNodeSetNoCenter = initNodeSet; // swept face and a not biquadratic volume
5126 initNodeSetNoCenter.erase( vecNewNodes.back()->first );
5128 for ( volNb = 0; volNb < nbVolumesByStep; volNb++ ) {
5129 list<const SMDS_MeshElement*>::iterator v = newVolumes.begin();
5130 std::advance( v, volNb );
5131 // find indices of free faces of a volume and their source edges
5132 list< int > freeInd;
5133 list< const SMDS_MeshElement* > srcEdges; // source edges of free faces
5134 SMDS_VolumeTool vTool( *v, /*ignoreCentralNodes=*/false );
5135 int iF, nbF = vTool.NbFaces();
5136 for ( iF = 0; iF < nbF; iF ++ ) {
5137 if (vTool.IsFreeFace( iF ) &&
5138 vTool.GetFaceNodes( iF, faceNodeSet ) &&
5139 initNodeSet != faceNodeSet) // except an initial face
5141 if ( nbSteps == 1 && faceNodeSet == topNodeSet )
5143 if ( faceNodeSet == initNodeSetNoCenter )
5145 freeInd.push_back( iF );
5146 // find source edge of a free face iF
5147 vector<const SMDS_MeshNode*> commonNodes; // shared by the initial and free faces
5148 vector<const SMDS_MeshNode*>::iterator lastCommom;
5149 commonNodes.resize( nbNodes, 0 );
5150 lastCommom = std::set_intersection( faceNodeSet.begin(), faceNodeSet.end(),
5151 initNodeSet.begin(), initNodeSet.end(),
5152 commonNodes.begin());
5153 if ( std::distance( commonNodes.begin(), lastCommom ) == 3 )
5154 srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1],commonNodes[2]));
5156 srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1]));
5158 if ( !srcEdges.back() )
5160 cout << "SMESH_MeshEditor::makeWalls(), no source edge found for a free face #"
5161 << iF << " of volume #" << vTool.ID() << endl;
5166 if ( freeInd.empty() )
5169 // create wall faces for all steps;
5170 // if such a face has been already created by sweep of edge,
5171 // assure that its orientation is OK
5172 for ( int iStep = 0; iStep < nbSteps; iStep++ )
5174 vTool.Set( *v, /*ignoreCentralNodes=*/false );
5175 vTool.SetExternalNormal();
5176 const int nextShift = vTool.IsForward() ? +1 : -1;
5177 list< int >::iterator ind = freeInd.begin();
5178 list< const SMDS_MeshElement* >::iterator srcEdge = srcEdges.begin();
5179 for ( ; ind != freeInd.end(); ++ind, ++srcEdge ) // loop on free faces
5181 const SMDS_MeshNode** nodes = vTool.GetFaceNodes( *ind );
5182 int nbn = vTool.NbFaceNodes( *ind );
5183 const SMDS_MeshElement * f = 0;
5184 if ( nbn == 3 ) ///// triangle
5186 f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ]);
5188 nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
5190 const SMDS_MeshNode* newOrder[3] = { nodes[ 1 - nextShift ],
5192 nodes[ 1 + nextShift ] };
5194 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5196 myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
5200 else if ( nbn == 4 ) ///// quadrangle
5202 f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ], nodes[ 3 ]);
5204 nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
5206 const SMDS_MeshNode* newOrder[4] = { nodes[ 0 ], nodes[ 2-nextShift ],
5207 nodes[ 2 ], nodes[ 2+nextShift ] };
5209 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5211 myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
5212 newOrder[ 2 ], newOrder[ 3 ]));
5215 else if ( nbn == 6 && isQuadratic ) /////// quadratic triangle
5217 f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[1], nodes[3], nodes[5] );
5219 nodes[2] != f->GetNodeWrap( f->GetNodeIndex( nodes[0] ) + 2*nextShift ))
5221 const SMDS_MeshNode* newOrder[6] = { nodes[2 - 2*nextShift],
5223 nodes[2 + 2*nextShift],
5224 nodes[3 - 2*nextShift],
5226 nodes[3 + 2*nextShift]};
5228 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5230 myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ],
5238 else if ( nbn == 8 && isQuadratic ) /////// quadratic quadrangle
5240 f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[6],
5241 nodes[1], nodes[3], nodes[5], nodes[7] );
5243 nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
5245 const SMDS_MeshNode* newOrder[8] = { nodes[0],
5246 nodes[4 - 2*nextShift],
5248 nodes[4 + 2*nextShift],
5250 nodes[5 - 2*nextShift],
5252 nodes[5 + 2*nextShift] };
5254 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5256 myLastCreatedElems.Append(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
5257 newOrder[ 2 ], newOrder[ 3 ],
5258 newOrder[ 4 ], newOrder[ 5 ],
5259 newOrder[ 6 ], newOrder[ 7 ]));
5262 else if ( nbn == 9 && isQuadratic ) /////// bi-quadratic quadrangle
5264 f = aMesh->FindElement( vector<const SMDS_MeshNode*>( nodes, nodes+nbn ),
5265 SMDSAbs_Face, /*noMedium=*/false);
5267 nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
5269 const SMDS_MeshNode* newOrder[9] = { nodes[0],
5270 nodes[4 - 2*nextShift],
5272 nodes[4 + 2*nextShift],
5274 nodes[5 - 2*nextShift],
5276 nodes[5 + 2*nextShift],
5279 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5281 myLastCreatedElems.Append(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
5282 newOrder[ 2 ], newOrder[ 3 ],
5283 newOrder[ 4 ], newOrder[ 5 ],
5284 newOrder[ 6 ], newOrder[ 7 ],
5288 else //////// polygon
5290 vector<const SMDS_MeshNode*> polygon_nodes ( nodes, nodes+nbn );
5291 const SMDS_MeshFace * f = aMesh->FindFace( polygon_nodes );
5293 nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + nextShift ))
5295 if ( !vTool.IsForward() )
5296 std::reverse( polygon_nodes.begin(), polygon_nodes.end());
5298 aMesh->ChangeElementNodes( f, &polygon_nodes[0], nbn );
5300 AddElement( polygon_nodes, polyFace.SetQuad( (*v)->IsQuadratic() ));
5304 while ( srcElements.Length() < myLastCreatedElems.Length() )
5305 srcElements.Append( *srcEdge );
5307 } // loop on free faces
5309 // go to the next volume
5311 while ( iVol++ < nbVolumesByStep ) v++;
5314 } // loop on volumes of one step
5315 } // sweep free links into faces
5317 // Make a ceiling face with a normal external to a volume
5319 // use SMDS_VolumeTool to get a correctly ordered nodes of a ceiling face
5320 SMDS_VolumeTool lastVol( itElem->second.back(), /*ignoreCentralNodes=*/false );
5321 int iF = lastVol.GetFaceIndex( aFaceLastNodes );
5323 if ( iF < 0 && isQuadratic && nbNodes % 2 ) { // remove a central node of biquadratic
5324 aFaceLastNodes.erase( vecNewNodes.back()->second.back() );
5325 iF = lastVol.GetFaceIndex( aFaceLastNodes );
5329 lastVol.SetExternalNormal();
5330 const SMDS_MeshNode** nodes = lastVol.GetFaceNodes( iF );
5331 const int nbn = lastVol.NbFaceNodes( iF );
5332 vector<const SMDS_MeshNode*> nodeVec( nodes, nodes+nbn );
5333 if ( !hasFreeLinks ||
5334 !aMesh->FindElement( nodeVec, SMDSAbs_Face, /*noMedium=*/false) )
5336 const vector<int>& interlace =
5337 SMDS_MeshCell::interlacedSmdsOrder( elem->GetEntityType(), nbn );
5338 SMDS_MeshCell::applyInterlaceRev( interlace, nodeVec );
5340 AddElement( nodeVec, anyFace.Init( elem ));
5342 while ( srcElements.Length() < myLastCreatedElems.Length() )
5343 srcElements.Append( elem );
5346 } // loop on swept elements
5349 //=======================================================================
5350 //function : RotationSweep
5352 //=======================================================================
5354 SMESH_MeshEditor::PGroupIDs
5355 SMESH_MeshEditor::RotationSweep(TIDSortedElemSet theElemSets[2],
5356 const gp_Ax1& theAxis,
5357 const double theAngle,
5358 const int theNbSteps,
5359 const double theTol,
5360 const bool theMakeGroups,
5361 const bool theMakeWalls)
5363 myLastCreatedElems.Clear();
5364 myLastCreatedNodes.Clear();
5366 // source elements for each generated one
5367 SMESH_SequenceOfElemPtr srcElems, srcNodes;
5370 aTrsf.SetRotation( theAxis, theAngle );
5372 aTrsf2.SetRotation( theAxis, theAngle/2. );
5374 gp_Lin aLine( theAxis );
5375 double aSqTol = theTol * theTol;
5377 SMESHDS_Mesh* aMesh = GetMeshDS();
5379 TNodeOfNodeListMap mapNewNodes;
5380 TElemOfVecOfNnlmiMap mapElemNewNodes;
5381 TTElemOfElemListMap newElemsMap;
5383 const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
5384 myMesh->NbFaces(ORDER_QUADRATIC) +
5385 myMesh->NbVolumes(ORDER_QUADRATIC) );
5386 // loop on theElemSets
5387 setElemsFirst( theElemSets );
5388 TIDSortedElemSet::iterator itElem;
5389 for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
5391 TIDSortedElemSet& theElems = theElemSets[ is2ndSet ];
5392 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
5393 const SMDS_MeshElement* elem = *itElem;
5394 if ( !elem || elem->GetType() == SMDSAbs_Volume )
5396 vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5397 newNodesItVec.reserve( elem->NbNodes() );
5399 // loop on elem nodes
5400 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5401 while ( itN->more() )
5403 const SMDS_MeshNode* node = cast2Node( itN->next() );
5405 gp_XYZ aXYZ( node->X(), node->Y(), node->Z() );
5407 aXYZ.Coord( coord[0], coord[1], coord[2] );
5408 bool isOnAxis = ( aLine.SquareDistance( aXYZ ) <= aSqTol );
5410 // check if a node has been already sweeped
5411 TNodeOfNodeListMapItr nIt =
5412 mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5413 list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5414 if ( listNewNodes.empty() )
5416 // check if we are to create medium nodes between corner ones
5417 bool needMediumNodes = false;
5418 if ( isQuadraticMesh )
5420 SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
5421 while (it->more() && !needMediumNodes )
5423 const SMDS_MeshElement* invElem = it->next();
5424 if ( invElem != elem && !theElems.count( invElem )) continue;
5425 needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
5426 if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
5427 needMediumNodes = true;
5432 const SMDS_MeshNode * newNode = node;
5433 for ( int i = 0; i < theNbSteps; i++ ) {
5435 if ( needMediumNodes ) // create a medium node
5437 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
5438 newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5439 myLastCreatedNodes.Append(newNode);
5440 srcNodes.Append( node );
5441 listNewNodes.push_back( newNode );
5442 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
5445 aTrsf.Transforms( coord[0], coord[1], coord[2] );
5447 // create a corner node
5448 newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5449 myLastCreatedNodes.Append(newNode);
5450 srcNodes.Append( node );
5451 listNewNodes.push_back( newNode );
5454 listNewNodes.push_back( newNode );
5455 // if ( needMediumNodes )
5456 // listNewNodes.push_back( newNode );
5460 newNodesItVec.push_back( nIt );
5462 // make new elements
5463 sweepElement( elem, newNodesItVec, newElemsMap[elem], theNbSteps, srcElems );
5468 makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElemSets[0], theNbSteps, srcElems );
5470 PGroupIDs newGroupIDs;
5471 if ( theMakeGroups )
5472 newGroupIDs = generateGroups( srcNodes, srcElems, "rotated");
5477 //=======================================================================
5478 //function : ExtrusParam
5479 //purpose : standard construction
5480 //=======================================================================
5482 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const gp_Vec& theStep,
5483 const int theNbSteps,
5484 const std::list<double>& theScales,
5485 const gp_XYZ* theBasePoint,
5487 const double theTolerance):
5489 myBaseP( Precision::Infinite(), 0, 0 ),
5490 myFlags( theFlags ),
5491 myTolerance( theTolerance ),
5492 myElemsToUse( NULL )
5494 mySteps = new TColStd_HSequenceOfReal;
5495 const double stepSize = theStep.Magnitude();
5496 for (int i=1; i<=theNbSteps; i++ )
5497 mySteps->Append( stepSize );
5499 int nbScales = theScales.size();
5502 if ( IsLinearVariation() && nbScales < theNbSteps )
5504 myScales.reserve( theNbSteps );
5505 std::list<double>::const_iterator scale = theScales.begin();
5506 double prevScale = 1.0;
5507 for ( int iSc = 1; scale != theScales.end(); ++scale, ++iSc )
5509 int iStep = int( iSc / double( nbScales ) * theNbSteps + 0.5 );
5510 int stDelta = Max( 1, iStep - myScales.size());
5511 double scDelta = ( *scale - prevScale ) / stDelta;
5512 for ( int iStep = 0; iStep < stDelta; ++iStep )
5514 myScales.push_back( prevScale + scDelta );
5515 prevScale = myScales.back();
5522 myScales.assign( theScales.begin(), theScales.end() );
5527 myBaseP = *theBasePoint;
5530 if (( theFlags & EXTRUSION_FLAG_SEW ) &&
5531 ( theTolerance > 0 ))
5533 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDirAndSew;
5537 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDir;
5541 //=======================================================================
5542 //function : ExtrusParam
5543 //purpose : steps are given explicitly
5544 //=======================================================================
5546 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const gp_Dir& theDir,
5547 Handle(TColStd_HSequenceOfReal) theSteps,
5549 const double theTolerance):
5551 mySteps( theSteps ),
5552 myFlags( theFlags ),
5553 myTolerance( theTolerance ),
5554 myElemsToUse( NULL )
5556 if (( theFlags & EXTRUSION_FLAG_SEW ) &&
5557 ( theTolerance > 0 ))
5559 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDirAndSew;
5563 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDir;
5567 //=======================================================================
5568 //function : ExtrusParam
5569 //purpose : for extrusion by normal
5570 //=======================================================================
5572 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const double theStepSize,
5573 const int theNbSteps,
5577 mySteps( new TColStd_HSequenceOfReal ),
5578 myFlags( theFlags ),
5580 myElemsToUse( NULL )
5582 for (int i = 0; i < theNbSteps; i++ )
5583 mySteps->Append( theStepSize );
5587 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByNormal1D;
5591 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByNormal2D;
5595 //=======================================================================
5596 //function : ExtrusParam::SetElementsToUse
5597 //purpose : stores elements to use for extrusion by normal, depending on
5598 // state of EXTRUSION_FLAG_USE_INPUT_ELEMS_ONLY flag;
5599 // define myBaseP for scaling
5600 //=======================================================================
5602 void SMESH_MeshEditor::ExtrusParam::SetElementsToUse( const TIDSortedElemSet& elems,
5603 const TIDSortedElemSet& nodes )
5605 myElemsToUse = ToUseInpElemsOnly() ? & elems : 0;
5607 if ( Precision::IsInfinite( myBaseP.X() )) // myBaseP not defined
5609 myBaseP.SetCoord( 0.,0.,0. );
5610 TIDSortedElemSet newNodes;
5612 const TIDSortedElemSet* elemSets[] = { &elems, &nodes };
5613 for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
5615 const TIDSortedElemSet& elements = *( elemSets[ is2ndSet ]);
5616 TIDSortedElemSet::const_iterator itElem = elements.begin();
5617 for ( ; itElem != elements.end(); itElem++ )
5619 const SMDS_MeshElement* elem = *itElem;
5620 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5621 while ( itN->more() ) {
5622 const SMDS_MeshElement* node = itN->next();
5623 if ( newNodes.insert( node ).second )
5624 myBaseP += SMESH_TNodeXYZ( node );
5628 myBaseP /= newNodes.size();
5632 //=======================================================================
5633 //function : ExtrusParam::beginStepIter
5634 //purpose : prepare iteration on steps
5635 //=======================================================================
5637 void SMESH_MeshEditor::ExtrusParam::beginStepIter( bool withMediumNodes )
5639 myWithMediumNodes = withMediumNodes;
5643 //=======================================================================
5644 //function : ExtrusParam::moreSteps
5645 //purpose : are there more steps?
5646 //=======================================================================
5648 bool SMESH_MeshEditor::ExtrusParam::moreSteps()
5650 return myNextStep <= mySteps->Length() || !myCurSteps.empty();
5652 //=======================================================================
5653 //function : ExtrusParam::nextStep
5654 //purpose : returns the next step
5655 //=======================================================================
5657 double SMESH_MeshEditor::ExtrusParam::nextStep()
5660 if ( !myCurSteps.empty() )
5662 res = myCurSteps.back();
5663 myCurSteps.pop_back();
5665 else if ( myNextStep <= mySteps->Length() )
5667 myCurSteps.push_back( mySteps->Value( myNextStep ));
5669 if ( myWithMediumNodes )
5671 myCurSteps.back() /= 2.;
5672 myCurSteps.push_back( myCurSteps.back() );
5679 //=======================================================================
5680 //function : ExtrusParam::makeNodesByDir
5681 //purpose : create nodes for standard extrusion
5682 //=======================================================================
5684 int SMESH_MeshEditor::ExtrusParam::
5685 makeNodesByDir( SMESHDS_Mesh* mesh,
5686 const SMDS_MeshNode* srcNode,
5687 std::list<const SMDS_MeshNode*> & newNodes,
5688 const bool makeMediumNodes)
5690 gp_XYZ p = SMESH_TNodeXYZ( srcNode );
5693 for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5695 p += myDir.XYZ() * nextStep();
5696 const SMDS_MeshNode * newNode = mesh->AddNode( p.X(), p.Y(), p.Z() );
5697 newNodes.push_back( newNode );
5700 if ( !myScales.empty() )
5702 if ( makeMediumNodes && myMediumScales.empty() )
5704 myMediumScales.resize( myScales.size() );
5705 double prevFactor = 1.;
5706 for ( size_t i = 0; i < myScales.size(); ++i )
5708 myMediumScales[i] = 0.5 * ( prevFactor + myScales[i] );
5709 prevFactor = myScales[i];
5712 typedef std::vector<double>::iterator ScaleIt;
5713 ScaleIt scales[] = { myScales.begin(), myMediumScales.begin() };
5715 size_t iSc = 0, nbScales = myScales.size() + myMediumScales.size();
5717 gp_XYZ center = myBaseP;
5718 std::list<const SMDS_MeshNode*>::iterator nIt = newNodes.begin();
5720 for ( beginStepIter( makeMediumNodes ); moreSteps() && ( iN < nbScales ); ++nIt, ++iN )
5722 center += myDir.XYZ() * nextStep();
5724 iSc += int( makeMediumNodes );
5725 ScaleIt& scale = scales[ iSc % 2 ];
5727 gp_XYZ xyz = SMESH_TNodeXYZ( *nIt );
5728 xyz = ( *scale * ( xyz - center )) + center;
5729 mesh->MoveNode( *nIt, xyz.X(), xyz.Y(), xyz.Z() );
5737 //=======================================================================
5738 //function : ExtrusParam::makeNodesByDirAndSew
5739 //purpose : create nodes for standard extrusion with sewing
5740 //=======================================================================
5742 int SMESH_MeshEditor::ExtrusParam::
5743 makeNodesByDirAndSew( SMESHDS_Mesh* mesh,
5744 const SMDS_MeshNode* srcNode,
5745 std::list<const SMDS_MeshNode*> & newNodes,
5746 const bool makeMediumNodes)
5748 gp_XYZ P1 = SMESH_TNodeXYZ( srcNode );
5751 for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5753 P1 += myDir.XYZ() * nextStep();
5755 // try to search in sequence of existing nodes
5756 // if myNodes.Length()>0 we 'nave to use given sequence
5757 // else - use all nodes of mesh
5758 const SMDS_MeshNode * node = 0;
5759 if ( myNodes.Length() > 0 ) {
5761 for(i=1; i<=myNodes.Length(); i++) {
5762 gp_XYZ P2 = SMESH_TNodeXYZ( myNodes.Value(i) );
5763 if (( P1 - P2 ).SquareModulus() < myTolerance * myTolerance )
5765 node = myNodes.Value(i);
5771 SMDS_NodeIteratorPtr itn = mesh->nodesIterator();
5772 while(itn->more()) {
5773 SMESH_TNodeXYZ P2( itn->next() );
5774 if (( P1 - P2 ).SquareModulus() < myTolerance * myTolerance )
5783 node = mesh->AddNode( P1.X(), P1.Y(), P1.Z() );
5785 newNodes.push_back( node );
5792 //=======================================================================
5793 //function : ExtrusParam::makeNodesByNormal2D
5794 //purpose : create nodes for extrusion using normals of faces
5795 //=======================================================================
5797 int SMESH_MeshEditor::ExtrusParam::
5798 makeNodesByNormal2D( SMESHDS_Mesh* mesh,
5799 const SMDS_MeshNode* srcNode,
5800 std::list<const SMDS_MeshNode*> & newNodes,
5801 const bool makeMediumNodes)
5803 const bool alongAvgNorm = ( myFlags & EXTRUSION_FLAG_BY_AVG_NORMAL );
5805 gp_XYZ p = SMESH_TNodeXYZ( srcNode );
5807 // get normals to faces sharing srcNode
5808 vector< gp_XYZ > norms, baryCenters;
5809 gp_XYZ norm, avgNorm( 0,0,0 );
5810 SMDS_ElemIteratorPtr faceIt = srcNode->GetInverseElementIterator( SMDSAbs_Face );
5811 while ( faceIt->more() )
5813 const SMDS_MeshElement* face = faceIt->next();
5814 if ( myElemsToUse && !myElemsToUse->count( face ))
5816 if ( SMESH_MeshAlgos::FaceNormal( face, norm, /*normalized=*/true ))
5818 norms.push_back( norm );
5820 if ( !alongAvgNorm )
5824 for ( SMDS_ElemIteratorPtr nIt = face->nodesIterator(); nIt->more(); ++nbN )
5825 bc += SMESH_TNodeXYZ( nIt->next() );
5826 baryCenters.push_back( bc / nbN );
5831 if ( norms.empty() ) return 0;
5833 double normSize = avgNorm.Modulus();
5834 if ( normSize < std::numeric_limits<double>::min() )
5837 if ( myFlags & EXTRUSION_FLAG_BY_AVG_NORMAL ) // extrude along avgNorm
5840 return makeNodesByDir( mesh, srcNode, newNodes, makeMediumNodes );
5843 avgNorm /= normSize;
5846 for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5849 double stepSize = nextStep();
5851 if ( norms.size() > 1 )
5853 for ( size_t iF = 0; iF < norms.size(); ++iF ) // loop on faces
5855 // translate plane of a face
5856 baryCenters[ iF ] += norms[ iF ] * stepSize;
5858 // find point of intersection of the face plane located at baryCenters[ iF ]
5859 // and avgNorm located at pNew
5860 double d = -( norms[ iF ] * baryCenters[ iF ]); // d of plane equation ax+by+cz+d=0
5861 double dot = ( norms[ iF ] * avgNorm );
5862 if ( dot < std::numeric_limits<double>::min() )
5863 dot = stepSize * 1e-3;
5864 double step = -( norms[ iF ] * pNew + d ) / dot;
5865 pNew += step * avgNorm;
5870 pNew += stepSize * avgNorm;
5874 const SMDS_MeshNode * newNode = mesh->AddNode( p.X(), p.Y(), p.Z() );
5875 newNodes.push_back( newNode );
5880 //=======================================================================
5881 //function : ExtrusParam::makeNodesByNormal1D
5882 //purpose : create nodes for extrusion using normals of edges
5883 //=======================================================================
5885 int SMESH_MeshEditor::ExtrusParam::
5886 makeNodesByNormal1D( SMESHDS_Mesh* mesh,
5887 const SMDS_MeshNode* srcNode,
5888 std::list<const SMDS_MeshNode*> & newNodes,
5889 const bool makeMediumNodes)
5891 throw SALOME_Exception("Extrusion 1D by Normal not implemented");
5895 //=======================================================================
5896 //function : ExtrusionSweep
5898 //=======================================================================
5900 SMESH_MeshEditor::PGroupIDs
5901 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet theElems[2],
5902 const gp_Vec& theStep,
5903 const int theNbSteps,
5904 TTElemOfElemListMap& newElemsMap,
5906 const double theTolerance)
5908 ExtrusParam aParams( theStep, theNbSteps, std::list<double>(), 0, theFlags, theTolerance );
5909 return ExtrusionSweep( theElems, aParams, newElemsMap );
5913 //=======================================================================
5914 //function : ExtrusionSweep
5916 //=======================================================================
5918 SMESH_MeshEditor::PGroupIDs
5919 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet theElemSets[2],
5920 ExtrusParam& theParams,
5921 TTElemOfElemListMap& newElemsMap)
5923 myLastCreatedElems.Clear();
5924 myLastCreatedNodes.Clear();
5926 // source elements for each generated one
5927 SMESH_SequenceOfElemPtr srcElems, srcNodes;
5929 setElemsFirst( theElemSets );
5930 const int nbSteps = theParams.NbSteps();
5931 theParams.SetElementsToUse( theElemSets[0], theElemSets[1] );
5933 TNodeOfNodeListMap mapNewNodes;
5934 TElemOfVecOfNnlmiMap mapElemNewNodes;
5936 const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
5937 myMesh->NbFaces(ORDER_QUADRATIC) +
5938 myMesh->NbVolumes(ORDER_QUADRATIC) );
5940 TIDSortedElemSet::iterator itElem;
5941 for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
5943 TIDSortedElemSet& theElems = theElemSets[ is2ndSet ];
5944 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
5946 // check element type
5947 const SMDS_MeshElement* elem = *itElem;
5948 if ( !elem || elem->GetType() == SMDSAbs_Volume )
5951 const size_t nbNodes = elem->NbNodes();
5952 vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5953 newNodesItVec.reserve( nbNodes );
5955 // loop on elem nodes
5956 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5957 while ( itN->more() )
5959 // check if a node has been already sweeped
5960 const SMDS_MeshNode* node = cast2Node( itN->next() );
5961 TNodeOfNodeListMap::iterator nIt =
5962 mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5963 list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5964 if ( listNewNodes.empty() )
5968 // check if we are to create medium nodes between corner ones
5969 bool needMediumNodes = false;
5970 if ( isQuadraticMesh )
5972 SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
5973 while (it->more() && !needMediumNodes )
5975 const SMDS_MeshElement* invElem = it->next();
5976 if ( invElem != elem && !theElems.count( invElem )) continue;
5977 needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
5978 if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
5979 needMediumNodes = true;
5982 // create nodes for all steps
5983 if ( theParams.MakeNodes( GetMeshDS(), node, listNewNodes, needMediumNodes ))
5985 list<const SMDS_MeshNode*>::iterator newNodesIt = listNewNodes.begin();
5986 for ( ; newNodesIt != listNewNodes.end(); ++newNodesIt )
5988 myLastCreatedNodes.Append( *newNodesIt );
5989 srcNodes.Append( node );
5994 break; // newNodesItVec will be shorter than nbNodes
5997 newNodesItVec.push_back( nIt );
5999 // make new elements
6000 if ( newNodesItVec.size() == nbNodes )
6001 sweepElement( elem, newNodesItVec, newElemsMap[elem], nbSteps, srcElems );
6005 if ( theParams.ToMakeBoundary() ) {
6006 makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElemSets[0], nbSteps, srcElems );
6008 PGroupIDs newGroupIDs;
6009 if ( theParams.ToMakeGroups() )
6010 newGroupIDs = generateGroups( srcNodes, srcElems, "extruded");
6015 //=======================================================================
6016 //function : ExtrusionAlongTrack
6018 //=======================================================================
6019 SMESH_MeshEditor::Extrusion_Error
6020 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet theElements[2],
6021 SMESH_subMesh* theTrack,
6022 const SMDS_MeshNode* theN1,
6023 const bool theHasAngles,
6024 list<double>& theAngles,
6025 const bool theLinearVariation,
6026 const bool theHasRefPoint,
6027 const gp_Pnt& theRefPoint,
6028 const bool theMakeGroups)
6030 myLastCreatedElems.Clear();
6031 myLastCreatedNodes.Clear();
6034 std::list<double> aPrms;
6035 TIDSortedElemSet::iterator itElem;
6038 TopoDS_Edge aTrackEdge;
6039 TopoDS_Vertex aV1, aV2;
6041 SMDS_ElemIteratorPtr aItE;
6042 SMDS_NodeIteratorPtr aItN;
6043 SMDSAbs_ElementType aTypeE;
6045 TNodeOfNodeListMap mapNewNodes;
6048 aNbE = theElements[0].size() + theElements[1].size();
6051 return EXTR_NO_ELEMENTS;
6053 // 1.1 Track Pattern
6056 SMESHDS_SubMesh* pSubMeshDS = theTrack->GetSubMeshDS();
6058 return ExtrusionAlongTrack( theElements, theTrack->GetFather(), theN1,
6059 theHasAngles, theAngles, theLinearVariation,
6060 theHasRefPoint, theRefPoint, theMakeGroups );
6062 aItE = pSubMeshDS->GetElements();
6063 while ( aItE->more() ) {
6064 const SMDS_MeshElement* pE = aItE->next();
6065 aTypeE = pE->GetType();
6066 // Pattern must contain links only
6067 if ( aTypeE != SMDSAbs_Edge )
6068 return EXTR_PATH_NOT_EDGE;
6071 list<SMESH_MeshEditor_PathPoint> fullList;
6073 const TopoDS_Shape& aS = theTrack->GetSubShape();
6074 // Sub-shape for the Pattern must be an Edge or Wire
6075 if( aS.ShapeType() == TopAbs_EDGE ) {
6076 aTrackEdge = TopoDS::Edge( aS );
6077 // the Edge must not be degenerated
6078 if ( SMESH_Algo::isDegenerated( aTrackEdge ) )
6079 return EXTR_BAD_PATH_SHAPE;
6080 TopExp::Vertices( aTrackEdge, aV1, aV2 );
6081 aItN = theTrack->GetFather()->GetSubMesh( aV1 )->GetSubMeshDS()->GetNodes();
6082 const SMDS_MeshNode* aN1 = aItN->next();
6083 aItN = theTrack->GetFather()->GetSubMesh( aV2 )->GetSubMeshDS()->GetNodes();
6084 const SMDS_MeshNode* aN2 = aItN->next();
6085 // starting node must be aN1 or aN2
6086 if ( !( aN1 == theN1 || aN2 == theN1 ) )
6087 return EXTR_BAD_STARTING_NODE;
6088 aItN = pSubMeshDS->GetNodes();
6089 while ( aItN->more() ) {
6090 const SMDS_MeshNode* pNode = aItN->next();
6091 const SMDS_EdgePosition* pEPos =
6092 static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
6093 double aT = pEPos->GetUParameter();
6094 aPrms.push_back( aT );
6096 //Extrusion_Error err =
6097 makeEdgePathPoints(aPrms, aTrackEdge, (aN1==theN1), fullList);
6098 } else if( aS.ShapeType() == TopAbs_WIRE ) {
6099 list< SMESH_subMesh* > LSM;
6100 TopTools_SequenceOfShape Edges;
6101 SMESH_subMeshIteratorPtr itSM = theTrack->getDependsOnIterator(false,true);
6102 while(itSM->more()) {
6103 SMESH_subMesh* SM = itSM->next();
6105 const TopoDS_Shape& aS = SM->GetSubShape();
6108 list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
6109 int startNid = theN1->GetID();
6110 TColStd_MapOfInteger UsedNums;
6112 int NbEdges = Edges.Length();
6114 for(; i<=NbEdges; i++) {
6116 list< SMESH_subMesh* >::iterator itLSM = LSM.begin();
6117 for(; itLSM!=LSM.end(); itLSM++) {
6119 if(UsedNums.Contains(k)) continue;
6120 aTrackEdge = TopoDS::Edge( Edges.Value(k) );
6121 SMESH_subMesh* locTrack = *itLSM;
6122 SMESHDS_SubMesh* locMeshDS = locTrack->GetSubMeshDS();
6123 TopExp::Vertices( aTrackEdge, aV1, aV2 );
6124 aItN = locTrack->GetFather()->GetSubMesh(aV1)->GetSubMeshDS()->GetNodes();
6125 const SMDS_MeshNode* aN1 = aItN->next();
6126 aItN = locTrack->GetFather()->GetSubMesh(aV2)->GetSubMeshDS()->GetNodes();
6127 const SMDS_MeshNode* aN2 = aItN->next();
6128 // starting node must be aN1 or aN2
6129 if ( !( aN1->GetID() == startNid || aN2->GetID() == startNid ) ) continue;
6130 // 2. Collect parameters on the track edge
6132 aItN = locMeshDS->GetNodes();
6133 while ( aItN->more() ) {
6134 const SMDS_MeshNode* pNode = aItN->next();
6135 const SMDS_EdgePosition* pEPos =
6136 static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
6137 double aT = pEPos->GetUParameter();
6138 aPrms.push_back( aT );
6140 list<SMESH_MeshEditor_PathPoint> LPP;
6141 //Extrusion_Error err =
6142 makeEdgePathPoints(aPrms, aTrackEdge,(aN1->GetID()==startNid), LPP);
6143 LLPPs.push_back(LPP);
6145 // update startN for search following egde
6146 if( aN1->GetID() == startNid ) startNid = aN2->GetID();
6147 else startNid = aN1->GetID();
6151 list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
6152 list<SMESH_MeshEditor_PathPoint> firstList = *itLLPP;
6153 list<SMESH_MeshEditor_PathPoint>::iterator itPP = firstList.begin();
6154 for(; itPP!=firstList.end(); itPP++) {
6155 fullList.push_back( *itPP );
6157 SMESH_MeshEditor_PathPoint PP1 = fullList.back();
6158 fullList.pop_back();
6160 for(; itLLPP!=LLPPs.end(); itLLPP++) {
6161 list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
6162 itPP = currList.begin();
6163 SMESH_MeshEditor_PathPoint PP2 = currList.front();
6164 gp_Dir D1 = PP1.Tangent();
6165 gp_Dir D2 = PP2.Tangent();
6166 gp_Dir Dnew( gp_Vec( (D1.X()+D2.X())/2, (D1.Y()+D2.Y())/2,
6167 (D1.Z()+D2.Z())/2 ) );
6168 PP1.SetTangent(Dnew);
6169 fullList.push_back(PP1);
6171 for(; itPP!=firstList.end(); itPP++) {
6172 fullList.push_back( *itPP );
6174 PP1 = fullList.back();
6175 fullList.pop_back();
6177 // if wire not closed
6178 fullList.push_back(PP1);
6182 return EXTR_BAD_PATH_SHAPE;
6185 return makeExtrElements(theElements, fullList, theHasAngles, theAngles, theLinearVariation,
6186 theHasRefPoint, theRefPoint, theMakeGroups);
6190 //=======================================================================
6191 //function : ExtrusionAlongTrack
6193 //=======================================================================
6194 SMESH_MeshEditor::Extrusion_Error
6195 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet theElements[2],
6196 SMESH_Mesh* theTrack,
6197 const SMDS_MeshNode* theN1,
6198 const bool theHasAngles,
6199 list<double>& theAngles,
6200 const bool theLinearVariation,
6201 const bool theHasRefPoint,
6202 const gp_Pnt& theRefPoint,
6203 const bool theMakeGroups)
6205 myLastCreatedElems.Clear();
6206 myLastCreatedNodes.Clear();
6209 std::list<double> aPrms;
6210 TIDSortedElemSet::iterator itElem;
6213 TopoDS_Edge aTrackEdge;
6214 TopoDS_Vertex aV1, aV2;
6216 SMDS_ElemIteratorPtr aItE;
6217 SMDS_NodeIteratorPtr aItN;
6218 SMDSAbs_ElementType aTypeE;
6220 TNodeOfNodeListMap mapNewNodes;
6223 aNbE = theElements[0].size() + theElements[1].size();
6226 return EXTR_NO_ELEMENTS;
6228 // 1.1 Track Pattern
6231 SMESHDS_Mesh* pMeshDS = theTrack->GetMeshDS();
6233 aItE = pMeshDS->elementsIterator();
6234 while ( aItE->more() ) {
6235 const SMDS_MeshElement* pE = aItE->next();
6236 aTypeE = pE->GetType();
6237 // Pattern must contain links only
6238 if ( aTypeE != SMDSAbs_Edge )
6239 return EXTR_PATH_NOT_EDGE;
6242 list<SMESH_MeshEditor_PathPoint> fullList;
6244 const TopoDS_Shape& aS = theTrack->GetShapeToMesh();
6246 if ( !theTrack->HasShapeToMesh() ) {
6247 //Mesh without shape
6248 const SMDS_MeshNode* currentNode = NULL;
6249 const SMDS_MeshNode* prevNode = theN1;
6250 std::vector<const SMDS_MeshNode*> aNodesList;
6251 aNodesList.push_back(theN1);
6252 int nbEdges = 0, conn=0;
6253 const SMDS_MeshElement* prevElem = NULL;
6254 const SMDS_MeshElement* currentElem = NULL;
6255 int totalNbEdges = theTrack->NbEdges();
6256 SMDS_ElemIteratorPtr nIt;
6259 if( !theTrack->GetMeshDS()->Contains(theN1) ) {
6260 return EXTR_BAD_STARTING_NODE;
6263 conn = nbEdgeConnectivity(theN1);
6265 return EXTR_PATH_NOT_EDGE;
6267 aItE = theN1->GetInverseElementIterator();
6268 prevElem = aItE->next();
6269 currentElem = prevElem;
6271 if(totalNbEdges == 1 ) {
6272 nIt = currentElem->nodesIterator();
6273 currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
6274 if(currentNode == prevNode)
6275 currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
6276 aNodesList.push_back(currentNode);
6278 nIt = currentElem->nodesIterator();
6279 while( nIt->more() ) {
6280 currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
6281 if(currentNode == prevNode)
6282 currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
6283 aNodesList.push_back(currentNode);
6285 //case of the closed mesh
6286 if(currentNode == theN1) {
6291 conn = nbEdgeConnectivity(currentNode);
6293 return EXTR_PATH_NOT_EDGE;
6294 }else if( conn == 1 && nbEdges > 0 ) {
6299 prevNode = currentNode;
6300 aItE = currentNode->GetInverseElementIterator();
6301 currentElem = aItE->next();
6302 if( currentElem == prevElem)
6303 currentElem = aItE->next();
6304 nIt = currentElem->nodesIterator();
6305 prevElem = currentElem;
6311 if(nbEdges != totalNbEdges)
6312 return EXTR_PATH_NOT_EDGE;
6314 TopTools_SequenceOfShape Edges;
6315 list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
6316 int startNid = theN1->GetID();
6317 for ( size_t i = 1; i < aNodesList.size(); i++ )
6319 gp_Pnt p1 = SMESH_TNodeXYZ( aNodesList[i-1] );
6320 gp_Pnt p2 = SMESH_TNodeXYZ( aNodesList[i] );
6321 TopoDS_Edge e = BRepBuilderAPI_MakeEdge( p1, p2 );
6322 list<SMESH_MeshEditor_PathPoint> LPP;
6324 makeEdgePathPoints(aPrms, e, (aNodesList[i-1]->GetID()==startNid), LPP);
6325 LLPPs.push_back(LPP);
6326 if ( aNodesList[i-1]->GetID() == startNid ) startNid = aNodesList[i ]->GetID();
6327 else startNid = aNodesList[i-1]->GetID();
6330 list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
6331 list<SMESH_MeshEditor_PathPoint> firstList = *itLLPP;
6332 list<SMESH_MeshEditor_PathPoint>::iterator itPP = firstList.begin();
6333 for(; itPP!=firstList.end(); itPP++) {
6334 fullList.push_back( *itPP );
6337 SMESH_MeshEditor_PathPoint PP1 = fullList.back();
6338 SMESH_MeshEditor_PathPoint PP2;
6339 fullList.pop_back();
6341 for(; itLLPP!=LLPPs.end(); itLLPP++) {
6342 list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
6343 itPP = currList.begin();
6344 PP2 = currList.front();
6345 gp_Dir D1 = PP1.Tangent();
6346 gp_Dir D2 = PP2.Tangent();
6347 gp_Dir Dnew( 0.5 * ( D1.XYZ() + D2.XYZ() ));
6348 PP1.SetTangent(Dnew);
6349 fullList.push_back(PP1);
6351 for(; itPP!=currList.end(); itPP++) {
6352 fullList.push_back( *itPP );
6354 PP1 = fullList.back();
6355 fullList.pop_back();
6357 fullList.push_back(PP1);
6359 } // Sub-shape for the Pattern must be an Edge or Wire
6360 else if ( aS.ShapeType() == TopAbs_EDGE )
6362 aTrackEdge = TopoDS::Edge( aS );
6363 // the Edge must not be degenerated
6364 if ( SMESH_Algo::isDegenerated( aTrackEdge ) )
6365 return EXTR_BAD_PATH_SHAPE;
6366 TopExp::Vertices( aTrackEdge, aV1, aV2 );
6367 const SMDS_MeshNode* aN1 = SMESH_Algo::VertexNode( aV1, pMeshDS );
6368 const SMDS_MeshNode* aN2 = SMESH_Algo::VertexNode( aV2, pMeshDS );
6369 // starting node must be aN1 or aN2
6370 if ( !( aN1 == theN1 || aN2 == theN1 ) )
6371 return EXTR_BAD_STARTING_NODE;
6372 aItN = pMeshDS->nodesIterator();
6373 while ( aItN->more() ) {
6374 const SMDS_MeshNode* pNode = aItN->next();
6375 if( pNode==aN1 || pNode==aN2 ) continue;
6376 const SMDS_EdgePosition* pEPos =
6377 static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
6378 double aT = pEPos->GetUParameter();
6379 aPrms.push_back( aT );
6381 //Extrusion_Error err =
6382 makeEdgePathPoints(aPrms, aTrackEdge, (aN1==theN1), fullList);
6384 else if( aS.ShapeType() == TopAbs_WIRE ) {
6385 list< SMESH_subMesh* > LSM;
6386 TopTools_SequenceOfShape Edges;
6387 TopExp_Explorer eExp(aS, TopAbs_EDGE);
6388 for(; eExp.More(); eExp.Next()) {
6389 TopoDS_Edge E = TopoDS::Edge( eExp.Current() );
6390 if( SMESH_Algo::isDegenerated(E) ) continue;
6391 SMESH_subMesh* SM = theTrack->GetSubMesh(E);
6397 list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
6398 TopoDS_Vertex aVprev;
6399 TColStd_MapOfInteger UsedNums;
6400 int NbEdges = Edges.Length();
6402 for(; i<=NbEdges; i++) {
6404 list< SMESH_subMesh* >::iterator itLSM = LSM.begin();
6405 for(; itLSM!=LSM.end(); itLSM++) {
6407 if(UsedNums.Contains(k)) continue;
6408 aTrackEdge = TopoDS::Edge( Edges.Value(k) );
6409 SMESH_subMesh* locTrack = *itLSM;
6410 SMESHDS_SubMesh* locMeshDS = locTrack->GetSubMeshDS();
6411 TopExp::Vertices( aTrackEdge, aV1, aV2 );
6412 bool aN1isOK = false, aN2isOK = false;
6413 if ( aVprev.IsNull() ) {
6414 // if previous vertex is not yet defined, it means that we in the beginning of wire
6415 // and we have to find initial vertex corresponding to starting node theN1
6416 const SMDS_MeshNode* aN1 = SMESH_Algo::VertexNode( aV1, pMeshDS );
6417 const SMDS_MeshNode* aN2 = SMESH_Algo::VertexNode( aV2, pMeshDS );
6418 // starting node must be aN1 or aN2
6419 aN1isOK = ( aN1 && aN1 == theN1 );
6420 aN2isOK = ( aN2 && aN2 == theN1 );
6423 // we have specified ending vertex of the previous edge on the previous iteration
6424 // and we have just to check that it corresponds to any vertex in current segment
6425 aN1isOK = aVprev.IsSame( aV1 );
6426 aN2isOK = aVprev.IsSame( aV2 );
6428 if ( !aN1isOK && !aN2isOK ) continue;
6429 // 2. Collect parameters on the track edge
6431 aItN = locMeshDS->GetNodes();
6432 while ( aItN->more() ) {
6433 const SMDS_MeshNode* pNode = aItN->next();
6434 const SMDS_EdgePosition* pEPos =
6435 static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
6436 double aT = pEPos->GetUParameter();
6437 aPrms.push_back( aT );
6439 list<SMESH_MeshEditor_PathPoint> LPP;
6440 //Extrusion_Error err =
6441 makeEdgePathPoints(aPrms, aTrackEdge, aN1isOK, LPP);
6442 LLPPs.push_back(LPP);
6444 // update startN for search following egde
6445 if ( aN1isOK ) aVprev = aV2;
6450 list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
6451 list<SMESH_MeshEditor_PathPoint>& firstList = *itLLPP;
6452 fullList.splice( fullList.end(), firstList );
6454 SMESH_MeshEditor_PathPoint PP1 = fullList.back();
6455 fullList.pop_back();
6457 for(; itLLPP!=LLPPs.end(); itLLPP++) {
6458 list<SMESH_MeshEditor_PathPoint>& currList = *itLLPP;
6459 SMESH_MeshEditor_PathPoint PP2 = currList.front();
6460 gp_Dir D1 = PP1.Tangent();
6461 gp_Dir D2 = PP2.Tangent();
6462 gp_Dir Dnew( D1.XYZ() + D2.XYZ() );
6463 PP1.SetTangent(Dnew);
6464 fullList.push_back(PP1);
6465 fullList.splice( fullList.end(), currList, ++currList.begin(), currList.end() );
6466 PP1 = fullList.back();
6467 fullList.pop_back();
6469 // if wire not closed
6470 fullList.push_back(PP1);
6474 return EXTR_BAD_PATH_SHAPE;
6477 return makeExtrElements(theElements, fullList, theHasAngles, theAngles, theLinearVariation,
6478 theHasRefPoint, theRefPoint, theMakeGroups);
6482 //=======================================================================
6483 //function : makeEdgePathPoints
6484 //purpose : auxiliary for ExtrusionAlongTrack
6485 //=======================================================================
6486 SMESH_MeshEditor::Extrusion_Error
6487 SMESH_MeshEditor::makeEdgePathPoints(std::list<double>& aPrms,
6488 const TopoDS_Edge& aTrackEdge,
6490 list<SMESH_MeshEditor_PathPoint>& LPP)
6492 Standard_Real aTx1, aTx2, aL2, aTolVec, aTolVec2;
6494 aTolVec2=aTolVec*aTolVec;
6496 TopoDS_Vertex aV1, aV2;
6497 TopExp::Vertices( aTrackEdge, aV1, aV2 );
6498 aT1=BRep_Tool::Parameter( aV1, aTrackEdge );
6499 aT2=BRep_Tool::Parameter( aV2, aTrackEdge );
6500 // 2. Collect parameters on the track edge
6501 aPrms.push_front( aT1 );
6502 aPrms.push_back( aT2 );
6505 if( FirstIsStart ) {
6516 SMESH_MeshEditor_PathPoint aPP;
6517 Handle(Geom_Curve) aC3D = BRep_Tool::Curve( aTrackEdge, aTx1, aTx2 );
6518 std::list<double>::iterator aItD = aPrms.begin();
6519 for(; aItD != aPrms.end(); ++aItD) {
6523 aC3D->D1( aT, aP3D, aVec );
6524 aL2 = aVec.SquareMagnitude();
6525 if ( aL2 < aTolVec2 )
6526 return EXTR_CANT_GET_TANGENT;
6527 gp_Dir aTgt( FirstIsStart ? aVec : -aVec );
6529 aPP.SetTangent( aTgt );
6530 aPP.SetParameter( aT );
6537 //=======================================================================
6538 //function : makeExtrElements
6539 //purpose : auxiliary for ExtrusionAlongTrack
6540 //=======================================================================
6541 SMESH_MeshEditor::Extrusion_Error
6542 SMESH_MeshEditor::makeExtrElements(TIDSortedElemSet theElemSets[2],
6543 list<SMESH_MeshEditor_PathPoint>& fullList,
6544 const bool theHasAngles,
6545 list<double>& theAngles,
6546 const bool theLinearVariation,
6547 const bool theHasRefPoint,
6548 const gp_Pnt& theRefPoint,
6549 const bool theMakeGroups)
6551 const int aNbTP = fullList.size();
6554 if( theHasAngles && !theAngles.empty() && theLinearVariation )
6555 linearAngleVariation(aNbTP-1, theAngles);
6557 // fill vector of path points with angles
6558 vector<SMESH_MeshEditor_PathPoint> aPPs;
6559 list<SMESH_MeshEditor_PathPoint>::iterator itPP = fullList.begin();
6560 list<double>::iterator itAngles = theAngles.begin();
6561 aPPs.push_back( *itPP++ );
6562 for( ; itPP != fullList.end(); itPP++) {
6563 aPPs.push_back( *itPP );
6564 if ( theHasAngles && itAngles != theAngles.end() )
6565 aPPs.back().SetAngle( *itAngles++ );
6568 TNodeOfNodeListMap mapNewNodes;
6569 TElemOfVecOfNnlmiMap mapElemNewNodes;
6570 TTElemOfElemListMap newElemsMap;
6571 TIDSortedElemSet::iterator itElem;
6572 // source elements for each generated one
6573 SMESH_SequenceOfElemPtr srcElems, srcNodes;
6575 // 3. Center of rotation aV0
6576 gp_Pnt aV0 = theRefPoint;
6577 if ( !theHasRefPoint )
6579 gp_XYZ aGC( 0.,0.,0. );
6580 TIDSortedElemSet newNodes;
6582 for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
6584 TIDSortedElemSet& theElements = theElemSets[ is2ndSet ];
6585 itElem = theElements.begin();
6586 for ( ; itElem != theElements.end(); itElem++ )
6588 const SMDS_MeshElement* elem = *itElem;
6589 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6590 while ( itN->more() ) {
6591 const SMDS_MeshElement* node = itN->next();
6592 if ( newNodes.insert( node ).second )
6593 aGC += SMESH_TNodeXYZ( node );
6597 aGC /= newNodes.size();
6599 } // if (!theHasRefPoint) {
6601 // 4. Processing the elements
6602 SMESHDS_Mesh* aMesh = GetMeshDS();
6603 list<const SMDS_MeshNode*> emptyList;
6605 setElemsFirst( theElemSets );
6606 for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
6608 TIDSortedElemSet& theElements = theElemSets[ is2ndSet ];
6609 for ( itElem = theElements.begin(); itElem != theElements.end(); itElem++ )
6611 const SMDS_MeshElement* elem = *itElem;
6613 vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
6614 newNodesItVec.reserve( elem->NbNodes() );
6616 // loop on elem nodes
6618 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6619 while ( itN->more() )
6622 // check if a node has been already processed
6623 const SMDS_MeshNode* node = cast2Node( itN->next() );
6624 TNodeOfNodeListMap::iterator nIt = mapNewNodes.insert( make_pair( node, emptyList )).first;
6625 list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
6626 if ( listNewNodes.empty() )
6629 Standard_Real aAngle1x, aAngleT1T0, aTolAng;
6630 gp_Pnt aP0x, aP1x, aPN0, aPN1, aV0x, aV1x;
6631 gp_Ax1 anAx1, anAxT1T0;
6632 gp_Dir aDT1x, aDT0x, aDT1T0;
6637 aPN0 = SMESH_TNodeXYZ( node );
6639 const SMESH_MeshEditor_PathPoint& aPP0 = aPPs[0];
6641 aDT0x= aPP0.Tangent();
6643 for ( int j = 1; j < aNbTP; ++j ) {
6644 const SMESH_MeshEditor_PathPoint& aPP1 = aPPs[j];
6646 aDT1x = aPP1.Tangent();
6647 aAngle1x = aPP1.Angle();
6649 gp_Trsf aTrsf, aTrsfRot, aTrsfRotT1T0;
6651 gp_Vec aV01x( aP0x, aP1x );
6652 aTrsf.SetTranslation( aV01x );
6655 aV1x = aV0x.Transformed( aTrsf );
6656 aPN1 = aPN0.Transformed( aTrsf );
6658 // rotation 1 [ T1,T0 ]
6659 aAngleT1T0=-aDT1x.Angle( aDT0x );
6660 if (fabs(aAngleT1T0) > aTolAng)
6663 anAxT1T0.SetLocation( aV1x );
6664 anAxT1T0.SetDirection( aDT1T0 );
6665 aTrsfRotT1T0.SetRotation( anAxT1T0, aAngleT1T0 );
6667 aPN1 = aPN1.Transformed( aTrsfRotT1T0 );
6671 if ( theHasAngles ) {
6672 anAx1.SetLocation( aV1x );
6673 anAx1.SetDirection( aDT1x );
6674 aTrsfRot.SetRotation( anAx1, aAngle1x );
6676 aPN1 = aPN1.Transformed( aTrsfRot );
6680 if ( elem->IsQuadratic() && !elem->IsMediumNode(node) )
6682 // create additional node
6683 gp_XYZ midP = 0.5 * ( aPN1.XYZ() + aPN0.XYZ() );
6684 const SMDS_MeshNode* newNode = aMesh->AddNode( midP.X(), midP.Y(), midP.Z() );
6685 myLastCreatedNodes.Append(newNode);
6686 srcNodes.Append( node );
6687 listNewNodes.push_back( newNode );
6689 const SMDS_MeshNode* newNode = aMesh->AddNode( aPN1.X(), aPN1.Y(), aPN1.Z() );
6690 myLastCreatedNodes.Append(newNode);
6691 srcNodes.Append( node );
6692 listNewNodes.push_back( newNode );
6700 else if( elem->IsQuadratic() && !elem->IsMediumNode(node) )
6702 // if current elem is quadratic and current node is not medium
6703 // we have to check - may be it is needed to insert additional nodes
6704 list< const SMDS_MeshNode* > & listNewNodes = nIt->second;
6705 if ((int) listNewNodes.size() == aNbTP-1 )
6707 vector<const SMDS_MeshNode*> aNodes(2*(aNbTP-1));
6708 gp_XYZ P(node->X(), node->Y(), node->Z());
6709 list< const SMDS_MeshNode* >::iterator it = listNewNodes.begin();
6711 for(i=0; i<aNbTP-1; i++) {
6712 const SMDS_MeshNode* N = *it;
6713 double x = ( N->X() + P.X() )/2.;
6714 double y = ( N->Y() + P.Y() )/2.;
6715 double z = ( N->Z() + P.Z() )/2.;
6716 const SMDS_MeshNode* newN = aMesh->AddNode(x,y,z);
6717 srcNodes.Append( node );
6718 myLastCreatedNodes.Append(newN);
6721 P = gp_XYZ(N->X(),N->Y(),N->Z());
6723 listNewNodes.clear();
6724 for(i=0; i<2*(aNbTP-1); i++) {
6725 listNewNodes.push_back(aNodes[i]);
6730 newNodesItVec.push_back( nIt );
6733 // make new elements
6734 sweepElement( elem, newNodesItVec, newElemsMap[elem], aNbTP-1, srcElems );
6738 makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElemSets[0], aNbTP-1, srcElems );
6740 if ( theMakeGroups )
6741 generateGroups( srcNodes, srcElems, "extruded");
6747 //=======================================================================
6748 //function : linearAngleVariation
6749 //purpose : spread values over nbSteps
6750 //=======================================================================
6752 void SMESH_MeshEditor::linearAngleVariation(const int nbSteps,
6753 list<double>& Angles)
6755 int nbAngles = Angles.size();
6756 if( nbSteps > nbAngles && nbAngles > 0 )
6758 vector<double> theAngles(nbAngles);
6759 theAngles.assign( Angles.begin(), Angles.end() );
6762 double rAn2St = double( nbAngles ) / double( nbSteps );
6763 double angPrev = 0, angle;
6764 for ( int iSt = 0; iSt < nbSteps; ++iSt )
6766 double angCur = rAn2St * ( iSt+1 );
6767 double angCurFloor = floor( angCur );
6768 double angPrevFloor = floor( angPrev );
6769 if ( angPrevFloor == angCurFloor )
6770 angle = rAn2St * theAngles[ int( angCurFloor ) ];
6772 int iP = int( angPrevFloor );
6773 double angPrevCeil = ceil(angPrev);
6774 angle = ( angPrevCeil - angPrev ) * theAngles[ iP ];
6776 int iC = int( angCurFloor );
6777 if ( iC < nbAngles )
6778 angle += ( angCur - angCurFloor ) * theAngles[ iC ];
6780 iP = int( angPrevCeil );
6782 angle += theAngles[ iC ];
6784 res.push_back(angle);
6792 //================================================================================
6794 * \brief Move or copy theElements applying theTrsf to their nodes
6795 * \param theElems - elements to transform, if theElems is empty then apply to all mesh nodes
6796 * \param theTrsf - transformation to apply
6797 * \param theCopy - if true, create translated copies of theElems
6798 * \param theMakeGroups - if true and theCopy, create translated groups
6799 * \param theTargetMesh - mesh to copy translated elements into
6800 * \return SMESH_MeshEditor::PGroupIDs - list of ids of created groups
6802 //================================================================================
6804 SMESH_MeshEditor::PGroupIDs
6805 SMESH_MeshEditor::Transform (TIDSortedElemSet & theElems,
6806 const gp_Trsf& theTrsf,
6808 const bool theMakeGroups,
6809 SMESH_Mesh* theTargetMesh)
6811 myLastCreatedElems.Clear();
6812 myLastCreatedNodes.Clear();
6814 bool needReverse = false;
6815 string groupPostfix;
6816 switch ( theTrsf.Form() ) {
6819 groupPostfix = "mirrored";
6822 groupPostfix = "mirrored";
6826 groupPostfix = "mirrored";
6829 groupPostfix = "rotated";
6831 case gp_Translation:
6832 groupPostfix = "translated";
6835 groupPostfix = "scaled";
6837 case gp_CompoundTrsf: // different scale by axis
6838 groupPostfix = "scaled";
6841 needReverse = false;
6842 groupPostfix = "transformed";
6845 SMESHDS_Mesh* aTgtMesh = theTargetMesh ? theTargetMesh->GetMeshDS() : 0;
6846 SMESHDS_Mesh* aMesh = GetMeshDS();
6848 SMESH_MeshEditor targetMeshEditor( theTargetMesh );
6849 SMESH_MeshEditor* editor = theTargetMesh ? & targetMeshEditor : theCopy ? this : 0;
6850 SMESH_MeshEditor::ElemFeatures elemType;
6852 // map old node to new one
6853 TNodeNodeMap nodeMap;
6855 // elements sharing moved nodes; those of them which have all
6856 // nodes mirrored but are not in theElems are to be reversed
6857 TIDSortedElemSet inverseElemSet;
6859 // source elements for each generated one
6860 SMESH_SequenceOfElemPtr srcElems, srcNodes;
6862 // issue 021015: EDF 1578 SMESH: Free nodes are removed when translating a mesh
6863 TIDSortedElemSet orphanNode;
6865 if ( theElems.empty() ) // transform the whole mesh
6868 SMDS_ElemIteratorPtr eIt = aMesh->elementsIterator();
6869 while ( eIt->more() ) theElems.insert( eIt->next() );
6871 SMDS_NodeIteratorPtr nIt = aMesh->nodesIterator();
6872 while ( nIt->more() )
6874 const SMDS_MeshNode* node = nIt->next();
6875 if ( node->NbInverseElements() == 0)
6876 orphanNode.insert( node );
6880 // loop on elements to transform nodes : first orphan nodes then elems
6881 TIDSortedElemSet::iterator itElem;
6882 TIDSortedElemSet *elements[] = { &orphanNode, &theElems };
6883 for (int i=0; i<2; i++)
6884 for ( itElem = elements[i]->begin(); itElem != elements[i]->end(); itElem++ )
6886 const SMDS_MeshElement* elem = *itElem;
6890 // loop on elem nodes
6892 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6893 while ( itN->more() )
6895 const SMDS_MeshNode* node = cast2Node( itN->next() );
6896 // check if a node has been already transformed
6897 pair<TNodeNodeMap::iterator,bool> n2n_isnew =
6898 nodeMap.insert( make_pair ( node, node ));
6899 if ( !n2n_isnew.second )
6902 node->GetXYZ( coord );
6903 theTrsf.Transforms( coord[0], coord[1], coord[2] );
6904 if ( theTargetMesh ) {
6905 const SMDS_MeshNode * newNode = aTgtMesh->AddNode( coord[0], coord[1], coord[2] );
6906 n2n_isnew.first->second = newNode;
6907 myLastCreatedNodes.Append(newNode);
6908 srcNodes.Append( node );
6910 else if ( theCopy ) {
6911 const SMDS_MeshNode * newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
6912 n2n_isnew.first->second = newNode;
6913 myLastCreatedNodes.Append(newNode);
6914 srcNodes.Append( node );
6917 aMesh->MoveNode( node, coord[0], coord[1], coord[2] );
6918 // node position on shape becomes invalid
6919 const_cast< SMDS_MeshNode* > ( node )->SetPosition
6920 ( SMDS_SpacePosition::originSpacePosition() );
6923 // keep inverse elements
6924 if ( !theCopy && !theTargetMesh && needReverse ) {
6925 SMDS_ElemIteratorPtr invElemIt = node->GetInverseElementIterator();
6926 while ( invElemIt->more() ) {
6927 const SMDS_MeshElement* iel = invElemIt->next();
6928 inverseElemSet.insert( iel );
6932 } // loop on elems in { &orphanNode, &theElems };
6934 // either create new elements or reverse mirrored ones
6935 if ( !theCopy && !needReverse && !theTargetMesh )
6938 theElems.insert( inverseElemSet.begin(),inverseElemSet.end() );
6940 // Replicate or reverse elements
6942 std::vector<int> iForw;
6943 vector<const SMDS_MeshNode*> nodes;
6944 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
6946 const SMDS_MeshElement* elem = *itElem;
6947 if ( !elem ) continue;
6949 SMDSAbs_GeometryType geomType = elem->GetGeomType();
6950 size_t nbNodes = elem->NbNodes();
6951 if ( geomType == SMDSGeom_NONE ) continue; // node
6953 nodes.resize( nbNodes );
6955 if ( geomType == SMDSGeom_POLYHEDRA ) // ------------------ polyhedral volume
6957 const SMDS_VtkVolume* aPolyedre = dynamic_cast<const SMDS_VtkVolume*>( elem );
6961 bool allTransformed = true;
6962 int nbFaces = aPolyedre->NbFaces();
6963 for (int iface = 1; iface <= nbFaces && allTransformed; iface++)
6965 int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
6966 for (int inode = 1; inode <= nbFaceNodes && allTransformed; inode++)
6968 const SMDS_MeshNode* node = aPolyedre->GetFaceNode(iface, inode);
6969 TNodeNodeMap::iterator nodeMapIt = nodeMap.find(node);
6970 if ( nodeMapIt == nodeMap.end() )
6971 allTransformed = false; // not all nodes transformed
6973 nodes.push_back((*nodeMapIt).second);
6975 if ( needReverse && allTransformed )
6976 std::reverse( nodes.end() - nbFaceNodes, nodes.end() );
6978 if ( !allTransformed )
6979 continue; // not all nodes transformed
6981 else // ----------------------- the rest element types
6983 while ( iForw.size() < nbNodes ) iForw.push_back( iForw.size() );
6984 const vector<int>& iRev = SMDS_MeshCell::reverseSmdsOrder( elem->GetEntityType(), nbNodes );
6985 const vector<int>& i = needReverse ? iRev : iForw;
6987 // find transformed nodes
6989 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6990 while ( itN->more() ) {
6991 const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( itN->next() );
6992 TNodeNodeMap::iterator nodeMapIt = nodeMap.find( node );
6993 if ( nodeMapIt == nodeMap.end() )
6994 break; // not all nodes transformed
6995 nodes[ i [ iNode++ ]] = (*nodeMapIt).second;
6997 if ( iNode != nbNodes )
6998 continue; // not all nodes transformed
7002 // copy in this or a new mesh
7003 if ( editor->AddElement( nodes, elemType.Init( elem, /*basicOnly=*/false )))
7004 srcElems.Append( elem );
7007 // reverse element as it was reversed by transformation
7009 aMesh->ChangeElementNodes( elem, &nodes[0], nbNodes );
7012 } // loop on elements
7014 if ( editor && editor != this )
7015 myLastCreatedElems = editor->myLastCreatedElems;
7017 PGroupIDs newGroupIDs;
7019 if ( ( theMakeGroups && theCopy ) ||
7020 ( theMakeGroups && theTargetMesh ) )
7021 newGroupIDs = generateGroups( srcNodes, srcElems, groupPostfix, theTargetMesh, false );
7026 //=======================================================================
7028 * \brief Create groups of elements made during transformation
7029 * \param nodeGens - nodes making corresponding myLastCreatedNodes
7030 * \param elemGens - elements making corresponding myLastCreatedElems
7031 * \param postfix - to append to names of new groups
7032 * \param targetMesh - mesh to create groups in
7033 * \param topPresent - is there "top" elements that are created by sweeping
7035 //=======================================================================
7037 SMESH_MeshEditor::PGroupIDs
7038 SMESH_MeshEditor::generateGroups(const SMESH_SequenceOfElemPtr& nodeGens,
7039 const SMESH_SequenceOfElemPtr& elemGens,
7040 const std::string& postfix,
7041 SMESH_Mesh* targetMesh,
7042 const bool topPresent)
7044 PGroupIDs newGroupIDs( new list<int> );
7045 SMESH_Mesh* mesh = targetMesh ? targetMesh : GetMesh();
7047 // Sort existing groups by types and collect their names
7049 // containers to store an old group and generated new ones;
7050 // 1st new group is for result elems of different type than a source one;
7051 // 2nd new group is for same type result elems ("top" group at extrusion)
7053 using boost::make_tuple;
7054 typedef tuple< SMESHDS_GroupBase*, SMESHDS_Group*, SMESHDS_Group* > TOldNewGroup;
7055 vector< list< TOldNewGroup > > groupsByType( SMDSAbs_NbElementTypes );
7056 vector< TOldNewGroup* > orderedOldNewGroups; // in order of old groups
7058 set< string > groupNames;
7060 SMESH_Mesh::GroupIteratorPtr groupIt = GetMesh()->GetGroups();
7061 if ( !groupIt->more() ) return newGroupIDs;
7063 int newGroupID = mesh->GetGroupIds().back()+1;
7064 while ( groupIt->more() )
7066 SMESH_Group * group = groupIt->next();
7067 if ( !group ) continue;
7068 SMESHDS_GroupBase* groupDS = group->GetGroupDS();
7069 if ( !groupDS || groupDS->IsEmpty() ) continue;
7070 groupNames.insert ( group->GetName() );
7071 groupDS->SetStoreName( group->GetName() );
7072 const SMDSAbs_ElementType type = groupDS->GetType();
7073 SMESHDS_Group* newGroup = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
7074 SMESHDS_Group* newTopGroup = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
7075 groupsByType[ type ].push_back( make_tuple( groupDS, newGroup, newTopGroup ));
7076 orderedOldNewGroups.push_back( & groupsByType[ type ].back() );
7079 // Loop on nodes and elements to add them in new groups
7081 vector< const SMDS_MeshElement* > resultElems;
7082 for ( int isNodes = 0; isNodes < 2; ++isNodes )
7084 const SMESH_SequenceOfElemPtr& gens = isNodes ? nodeGens : elemGens;
7085 const SMESH_SequenceOfElemPtr& elems = isNodes ? myLastCreatedNodes : myLastCreatedElems;
7086 if ( gens.Length() != elems.Length() )
7087 throw SALOME_Exception("SMESH_MeshEditor::generateGroups(): invalid args");
7089 // loop on created elements
7090 for (int iElem = 1; iElem <= elems.Length(); ++iElem )
7092 const SMDS_MeshElement* sourceElem = gens( iElem );
7093 if ( !sourceElem ) {
7094 MESSAGE("generateGroups(): NULL source element");
7097 list< TOldNewGroup > & groupsOldNew = groupsByType[ sourceElem->GetType() ];
7098 if ( groupsOldNew.empty() ) { // no groups of this type at all
7099 while ( iElem < gens.Length() && gens( iElem+1 ) == sourceElem )
7100 ++iElem; // skip all elements made by sourceElem
7103 // collect all elements made by the iElem-th sourceElem
7104 resultElems.clear();
7105 if ( const SMDS_MeshElement* resElem = elems( iElem ))
7106 if ( resElem != sourceElem )
7107 resultElems.push_back( resElem );
7108 while ( iElem < gens.Length() && gens( iElem+1 ) == sourceElem )
7109 if ( const SMDS_MeshElement* resElem = elems( ++iElem ))
7110 if ( resElem != sourceElem )
7111 resultElems.push_back( resElem );
7113 const SMDS_MeshElement* topElem = 0;
7114 if ( isNodes ) // there must be a top element
7116 topElem = resultElems.back();
7117 resultElems.pop_back();
7121 vector< const SMDS_MeshElement* >::reverse_iterator resElemIt = resultElems.rbegin();
7122 for ( ; resElemIt != resultElems.rend() ; ++resElemIt )
7123 if ( (*resElemIt)->GetType() == sourceElem->GetType() )
7125 topElem = *resElemIt;
7126 *resElemIt = 0; // erase *resElemIt
7130 // add resultElems to groups originted from ones the sourceElem belongs to
7131 list< TOldNewGroup >::iterator gOldNew, gLast = groupsOldNew.end();
7132 for ( gOldNew = groupsOldNew.begin(); gOldNew != gLast; ++gOldNew )
7134 SMESHDS_GroupBase* oldGroup = gOldNew->get<0>();
7135 if ( oldGroup->Contains( sourceElem )) // sourceElem is in oldGroup
7137 // fill in a new group
7138 SMDS_MeshGroup & newGroup = gOldNew->get<1>()->SMDSGroup();
7139 vector< const SMDS_MeshElement* >::iterator resLast = resultElems.end(), resElemIt;
7140 for ( resElemIt = resultElems.begin(); resElemIt != resLast; ++resElemIt )
7142 newGroup.Add( *resElemIt );
7144 // fill a "top" group
7147 SMDS_MeshGroup & newTopGroup = gOldNew->get<2>()->SMDSGroup();
7148 newTopGroup.Add( topElem );
7152 } // loop on created elements
7153 }// loop on nodes and elements
7155 // Create new SMESH_Groups from SMESHDS_Groups and remove empty SMESHDS_Groups
7157 list<int> topGrouIds;
7158 for ( size_t i = 0; i < orderedOldNewGroups.size(); ++i )
7160 SMESHDS_GroupBase* oldGroupDS = orderedOldNewGroups[i]->get<0>();
7161 SMESHDS_Group* newGroups[2] = { orderedOldNewGroups[i]->get<1>(),
7162 orderedOldNewGroups[i]->get<2>() };
7163 for ( int is2nd = 0; is2nd < 2; ++is2nd )
7165 SMESHDS_Group* newGroupDS = newGroups[ is2nd ];
7166 if ( newGroupDS->IsEmpty() )
7168 mesh->GetMeshDS()->RemoveGroup( newGroupDS );
7173 newGroupDS->SetType( newGroupDS->GetElements()->next()->GetType() );
7176 const bool isTop = ( topPresent &&
7177 newGroupDS->GetType() == oldGroupDS->GetType() &&
7180 string name = oldGroupDS->GetStoreName();
7181 { // remove trailing whitespaces (issue 22599)
7182 size_t size = name.size();
7183 while ( size > 1 && isspace( name[ size-1 ]))
7185 if ( size != name.size() )
7187 name.resize( size );
7188 oldGroupDS->SetStoreName( name.c_str() );
7191 if ( !targetMesh ) {
7192 string suffix = ( isTop ? "top": postfix.c_str() );
7196 while ( !groupNames.insert( name ).second ) // name exists
7197 name = SMESH_Comment( oldGroupDS->GetStoreName() ) << "_" << suffix << "_" << nb++;
7202 newGroupDS->SetStoreName( name.c_str() );
7204 // make a SMESH_Groups
7205 mesh->AddGroup( newGroupDS );
7207 topGrouIds.push_back( newGroupDS->GetID() );
7209 newGroupIDs->push_back( newGroupDS->GetID() );
7213 newGroupIDs->splice( newGroupIDs->end(), topGrouIds );
7218 //================================================================================
7220 * * \brief Return list of group of nodes close to each other within theTolerance
7221 * * Search among theNodes or in the whole mesh if theNodes is empty using
7222 * * an Octree algorithm
7223 * \param [in,out] theNodes - the nodes to treat
7224 * \param [in] theTolerance - the tolerance
7225 * \param [out] theGroupsOfNodes - the result groups of coincident nodes
7226 * \param [in] theSeparateCornersAndMedium - if \c true, in quadratic mesh puts
7227 * corner and medium nodes in separate groups
7229 //================================================================================
7231 void SMESH_MeshEditor::FindCoincidentNodes (TIDSortedNodeSet & theNodes,
7232 const double theTolerance,
7233 TListOfListOfNodes & theGroupsOfNodes,
7234 bool theSeparateCornersAndMedium)
7236 myLastCreatedElems.Clear();
7237 myLastCreatedNodes.Clear();
7239 if ( myMesh->NbEdges ( ORDER_QUADRATIC ) +
7240 myMesh->NbFaces ( ORDER_QUADRATIC ) +
7241 myMesh->NbVolumes( ORDER_QUADRATIC ) == 0 )
7242 theSeparateCornersAndMedium = false;
7244 TIDSortedNodeSet& corners = theNodes;
7245 TIDSortedNodeSet medium;
7247 if ( theNodes.empty() ) // get all nodes in the mesh
7249 TIDSortedNodeSet* nodes[2] = { &corners, &medium };
7250 SMDS_NodeIteratorPtr nIt = GetMeshDS()->nodesIterator(/*idInceasingOrder=*/true);
7251 if ( theSeparateCornersAndMedium )
7252 while ( nIt->more() )
7254 const SMDS_MeshNode* n = nIt->next();
7255 TIDSortedNodeSet* & nodeSet = nodes[ SMESH_MesherHelper::IsMedium( n )];
7256 nodeSet->insert( nodeSet->end(), n );
7259 while ( nIt->more() )
7260 theNodes.insert( theNodes.end(), nIt->next() );
7262 else if ( theSeparateCornersAndMedium ) // separate corners from medium nodes
7264 TIDSortedNodeSet::iterator nIt = corners.begin();
7265 while ( nIt != corners.end() )
7266 if ( SMESH_MesherHelper::IsMedium( *nIt ))
7268 medium.insert( medium.end(), *nIt );
7269 corners.erase( nIt++ );
7277 if ( !corners.empty() )
7278 SMESH_OctreeNode::FindCoincidentNodes ( corners, &theGroupsOfNodes, theTolerance );
7279 if ( !medium.empty() )
7280 SMESH_OctreeNode::FindCoincidentNodes ( medium, &theGroupsOfNodes, theTolerance );
7283 //=======================================================================
7284 //function : SimplifyFace
7285 //purpose : split a chain of nodes into several closed chains
7286 //=======================================================================
7288 int SMESH_MeshEditor::SimplifyFace (const vector<const SMDS_MeshNode *>& faceNodes,
7289 vector<const SMDS_MeshNode *>& poly_nodes,
7290 vector<int>& quantities) const
7292 int nbNodes = faceNodes.size();
7293 while ( faceNodes[ 0 ] == faceNodes[ nbNodes-1 ] && nbNodes > 2 )
7297 size_t prevNbQuant = quantities.size();
7299 vector< const SMDS_MeshNode* > simpleNodes; simpleNodes.reserve( nbNodes );
7300 map< const SMDS_MeshNode*, int > nodeIndices; // indices within simpleNodes
7301 map< const SMDS_MeshNode*, int >::iterator nInd;
7303 nodeIndices.insert( make_pair( faceNodes[0], 0 ));
7304 simpleNodes.push_back( faceNodes[0] );
7305 for ( int iCur = 1; iCur < nbNodes; iCur++ )
7307 if ( faceNodes[ iCur ] != simpleNodes.back() )
7309 int index = simpleNodes.size();
7310 nInd = nodeIndices.insert( make_pair( faceNodes[ iCur ], index )).first;
7311 int prevIndex = nInd->second;
7312 if ( prevIndex < index )
7315 int loopLen = index - prevIndex;
7318 // store the sub-loop
7319 quantities.push_back( loopLen );
7320 for ( int i = prevIndex; i < index; i++ )
7321 poly_nodes.push_back( simpleNodes[ i ]);
7323 simpleNodes.resize( prevIndex+1 );
7327 simpleNodes.push_back( faceNodes[ iCur ]);
7332 if ( simpleNodes.size() > 2 )
7334 quantities.push_back( simpleNodes.size() );
7335 poly_nodes.insert ( poly_nodes.end(), simpleNodes.begin(), simpleNodes.end() );
7338 return quantities.size() - prevNbQuant;
7341 //=======================================================================
7342 //function : MergeNodes
7343 //purpose : In each group, the cdr of nodes are substituted by the first one
7345 //=======================================================================
7347 void SMESH_MeshEditor::MergeNodes (TListOfListOfNodes & theGroupsOfNodes,
7348 const bool theAvoidMakingHoles)
7350 myLastCreatedElems.Clear();
7351 myLastCreatedNodes.Clear();
7353 SMESHDS_Mesh* mesh = GetMeshDS();
7355 TNodeNodeMap nodeNodeMap; // node to replace - new node
7356 set<const SMDS_MeshElement*> elems; // all elements with changed nodes
7357 list< int > rmElemIds, rmNodeIds;
7358 vector< ElemFeatures > newElemDefs;
7360 // Fill nodeNodeMap and elems
7362 TListOfListOfNodes::iterator grIt = theGroupsOfNodes.begin();
7363 for ( ; grIt != theGroupsOfNodes.end(); grIt++ )
7365 list<const SMDS_MeshNode*>& nodes = *grIt;
7366 list<const SMDS_MeshNode*>::iterator nIt = nodes.begin();
7367 const SMDS_MeshNode* nToKeep = *nIt;
7368 for ( ++nIt; nIt != nodes.end(); nIt++ )
7370 const SMDS_MeshNode* nToRemove = *nIt;
7371 nodeNodeMap.insert( make_pair( nToRemove, nToKeep ));
7372 SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
7373 while ( invElemIt->more() ) {
7374 const SMDS_MeshElement* elem = invElemIt->next();
7380 // Apply recursive replacements (BUG 0020185)
7381 TNodeNodeMap::iterator nnIt = nodeNodeMap.begin();
7382 for ( ; nnIt != nodeNodeMap.end(); ++nnIt )
7384 const SMDS_MeshNode* nToKeep = nnIt->second;
7385 TNodeNodeMap::iterator nnIt_i = nodeNodeMap.find( nToKeep );
7386 while ( nnIt_i != nodeNodeMap.end() && nnIt_i->second != nnIt->second )
7387 nToKeep = nnIt_i->second;
7388 nnIt->second = nToKeep;
7391 if ( theAvoidMakingHoles )
7393 // find elements whose topology changes
7395 vector<const SMDS_MeshElement*> pbElems;
7396 set<const SMDS_MeshElement*>::iterator eIt = elems.begin();
7397 for ( ; eIt != elems.end(); ++eIt )
7399 const SMDS_MeshElement* elem = *eIt;
7400 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
7401 while ( itN->more() )
7403 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( itN->next() );
7404 TNodeNodeMap::iterator nnIt = nodeNodeMap.find( n );
7405 if ( nnIt != nodeNodeMap.end() && elem->GetNodeIndex( nnIt->second ) >= 0 )
7407 // several nodes of elem stick
7408 pbElems.push_back( elem );
7413 // exclude from merge nodes causing spoiling element
7414 for ( size_t iLoop = 0; iLoop < pbElems.size(); ++iLoop ) // avoid infinite cycle
7416 bool nodesExcluded = false;
7417 for ( size_t i = 0; i < pbElems.size(); ++i )
7419 size_t prevNbMergeNodes = nodeNodeMap.size();
7420 if ( !applyMerge( pbElems[i], newElemDefs, nodeNodeMap, /*noHoles=*/true ) &&
7421 prevNbMergeNodes < nodeNodeMap.size() )
7422 nodesExcluded = true;
7424 if ( !nodesExcluded )
7429 for ( nnIt = nodeNodeMap.begin(); nnIt != nodeNodeMap.end(); ++nnIt )
7431 const SMDS_MeshNode* nToRemove = nnIt->first;
7432 const SMDS_MeshNode* nToKeep = nnIt->second;
7433 if ( nToRemove != nToKeep )
7435 rmNodeIds.push_back( nToRemove->GetID() );
7436 AddToSameGroups( nToKeep, nToRemove, mesh );
7437 // set _alwaysComputed to a sub-mesh of VERTEX to enable further mesh computing
7438 // w/o creating node in place of merged ones.
7439 const SMDS_PositionPtr& pos = nToRemove->GetPosition();
7440 if ( pos && pos->GetTypeOfPosition() == SMDS_TOP_VERTEX )
7441 if ( SMESH_subMesh* sm = myMesh->GetSubMeshContaining( nToRemove->getshapeId() ))
7442 sm->SetIsAlwaysComputed( true );
7446 // Change element nodes or remove an element
7448 set<const SMDS_MeshElement*>::iterator eIt = elems.begin();
7449 for ( ; eIt != elems.end(); eIt++ )
7451 const SMDS_MeshElement* elem = *eIt;
7452 SMESHDS_SubMesh* sm = mesh->MeshElements( elem->getshapeId() );
7454 bool keepElem = applyMerge( elem, newElemDefs, nodeNodeMap, /*noHoles=*/false );
7456 rmElemIds.push_back( elem->GetID() );
7458 for ( size_t i = 0; i < newElemDefs.size(); ++i )
7460 if ( i > 0 || !mesh->ChangeElementNodes( elem,
7461 & newElemDefs[i].myNodes[0],
7462 newElemDefs[i].myNodes.size() ))
7466 newElemDefs[i].SetID( elem->GetID() );
7467 mesh->RemoveFreeElement(elem, sm, /*fromGroups=*/false);
7468 if ( !keepElem ) rmElemIds.pop_back();
7472 newElemDefs[i].SetID( -1 );
7474 SMDS_MeshElement* newElem = this->AddElement( newElemDefs[i].myNodes, newElemDefs[i] );
7475 if ( sm && newElem )
7476 sm->AddElement( newElem );
7477 if ( elem != newElem )
7478 ReplaceElemInGroups( elem, newElem, mesh );
7483 // Remove bad elements, then equal nodes (order important)
7484 Remove( rmElemIds, /*isNodes=*/false );
7485 Remove( rmNodeIds, /*isNodes=*/true );
7490 //=======================================================================
7491 //function : applyMerge
7492 //purpose : Compute new connectivity of an element after merging nodes
7493 // \param [in] elems - the element
7494 // \param [out] newElemDefs - definition(s) of result element(s)
7495 // \param [inout] nodeNodeMap - nodes to merge
7496 // \param [in] avoidMakingHoles - if true and and the element becomes invalid
7497 // after merging (but not degenerated), removes nodes causing
7498 // the invalidity from \a nodeNodeMap.
7499 // \return bool - true if the element should be removed
7500 //=======================================================================
7502 bool SMESH_MeshEditor::applyMerge( const SMDS_MeshElement* elem,
7503 vector< ElemFeatures >& newElemDefs,
7504 TNodeNodeMap& nodeNodeMap,
7505 const bool avoidMakingHoles )
7507 bool toRemove = false; // to remove elem
7508 int nbResElems = 1; // nb new elements
7510 newElemDefs.resize(nbResElems);
7511 newElemDefs[0].Init( elem );
7512 newElemDefs[0].myNodes.clear();
7514 set<const SMDS_MeshNode*> nodeSet;
7515 vector< const SMDS_MeshNode*> curNodes;
7516 vector< const SMDS_MeshNode*> & uniqueNodes = newElemDefs[0].myNodes;
7519 const int nbNodes = elem->NbNodes();
7520 SMDSAbs_EntityType entity = elem->GetEntityType();
7522 curNodes.resize( nbNodes );
7523 uniqueNodes.resize( nbNodes );
7524 iRepl.resize( nbNodes );
7525 int iUnique = 0, iCur = 0, nbRepl = 0;
7527 // Get new seq of nodes
7529 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
7530 while ( itN->more() )
7532 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( itN->next() );
7534 TNodeNodeMap::iterator nnIt = nodeNodeMap.find( n );
7535 if ( nnIt != nodeNodeMap.end() ) {
7538 curNodes[ iCur ] = n;
7539 bool isUnique = nodeSet.insert( n ).second;
7541 uniqueNodes[ iUnique++ ] = n;
7543 iRepl[ nbRepl++ ] = iCur;
7547 // Analyse element topology after replacement
7549 int nbUniqueNodes = nodeSet.size();
7550 if ( nbNodes != nbUniqueNodes ) // some nodes stick
7555 if ( newElemDefs[0].myIsQuad && newElemDefs[0].myType == SMDSAbs_Face && nbNodes > 6 )
7557 // if corner nodes stick, remove medium nodes between them from uniqueNodes
7558 int nbCorners = nbNodes / 2;
7559 for ( int iCur = 0; iCur < nbCorners; ++iCur )
7561 int iNext = ( iCur + 1 ) % nbCorners;
7562 if ( curNodes[ iCur ] == curNodes[ iNext ] ) // corners stick
7564 int iMedium = iCur + nbCorners;
7565 vector< const SMDS_MeshNode* >::iterator i =
7566 std::find( uniqueNodes.begin() + nbCorners - nbRepl,
7568 curNodes[ iMedium ]);
7569 if ( i != uniqueNodes.end() )
7572 for ( ; i+1 != uniqueNodes.end(); ++i )
7581 case SMDSEntity_Polygon:
7582 case SMDSEntity_Quad_Polygon: // Polygon
7584 ElemFeatures* elemType = & newElemDefs[0];
7585 const bool isQuad = elemType->myIsQuad;
7587 SMDS_MeshCell::applyInterlace // interlace medium and corner nodes
7588 ( SMDS_MeshCell::interlacedSmdsOrder( SMDSEntity_Quad_Polygon, nbNodes ), curNodes );
7590 // a polygon can divide into several elements
7591 vector<const SMDS_MeshNode *> polygons_nodes;
7592 vector<int> quantities;
7593 nbResElems = SimplifyFace( curNodes, polygons_nodes, quantities );
7594 newElemDefs.resize( nbResElems );
7595 for ( int inode = 0, iface = 0; iface < nbResElems; iface++ )
7597 ElemFeatures* elemType = & newElemDefs[iface];
7598 if ( iface ) elemType->Init( elem );
7600 vector<const SMDS_MeshNode *>& face_nodes = elemType->myNodes;
7601 int nbNewNodes = quantities[iface];
7602 face_nodes.assign( polygons_nodes.begin() + inode,
7603 polygons_nodes.begin() + inode + nbNewNodes );
7604 inode += nbNewNodes;
7605 if ( isQuad ) // check if a result elem is a valid quadratic polygon
7607 bool isValid = ( nbNewNodes % 2 == 0 );
7608 for ( int i = 0; i < nbNewNodes && isValid; ++i )
7609 isValid = ( elem->IsMediumNode( face_nodes[i]) == bool( i % 2 ));
7610 elemType->SetQuad( isValid );
7611 if ( isValid ) // put medium nodes after corners
7612 SMDS_MeshCell::applyInterlaceRev
7613 ( SMDS_MeshCell::interlacedSmdsOrder( SMDSEntity_Quad_Polygon,
7614 nbNewNodes ), face_nodes );
7616 elemType->SetPoly(( nbNewNodes / ( elemType->myIsQuad + 1 ) > 4 ));
7618 nbUniqueNodes = newElemDefs[0].myNodes.size();
7622 case SMDSEntity_Polyhedra: // Polyhedral volume
7624 if ( nbUniqueNodes >= 4 )
7626 // each face has to be analyzed in order to check volume validity
7627 if ( const SMDS_VtkVolume* aPolyedre = dynamic_cast<const SMDS_VtkVolume*>( elem ))
7629 int nbFaces = aPolyedre->NbFaces();
7631 vector<const SMDS_MeshNode *>& poly_nodes = newElemDefs[0].myNodes;
7632 vector<int> & quantities = newElemDefs[0].myPolyhedQuantities;
7633 vector<const SMDS_MeshNode *> faceNodes;
7637 for (int iface = 1; iface <= nbFaces; iface++)
7639 int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
7640 faceNodes.resize( nbFaceNodes );
7641 for (int inode = 1; inode <= nbFaceNodes; inode++)
7643 const SMDS_MeshNode * faceNode = aPolyedre->GetFaceNode(iface, inode);
7644 TNodeNodeMap::iterator nnIt = nodeNodeMap.find(faceNode);
7645 if ( nnIt != nodeNodeMap.end() ) // faceNode sticks
7646 faceNode = (*nnIt).second;
7647 faceNodes[inode - 1] = faceNode;
7649 SimplifyFace(faceNodes, poly_nodes, quantities);
7652 if ( quantities.size() > 3 )
7654 // TODO: remove coincident faces
7656 nbUniqueNodes = newElemDefs[0].myNodes.size();
7664 // TODO not all the possible cases are solved. Find something more generic?
7665 case SMDSEntity_Edge: //////// EDGE
7666 case SMDSEntity_Triangle: //// TRIANGLE
7667 case SMDSEntity_Quad_Triangle:
7668 case SMDSEntity_Tetra:
7669 case SMDSEntity_Quad_Tetra: // TETRAHEDRON
7673 case SMDSEntity_Quad_Edge:
7677 case SMDSEntity_Quadrangle: //////////////////////////////////// QUADRANGLE
7679 if ( nbUniqueNodes < 3 )
7681 else if ( nbRepl == 1 && curNodes[ iRepl[0]] == curNodes[( iRepl[0]+2 )%4 ])
7682 toRemove = true; // opposite nodes stick
7687 case SMDSEntity_Quad_Quadrangle: // Quadratic QUADRANGLE
7696 if ( nbUniqueNodes == 6 &&
7698 ( nbRepl == 1 || iRepl[1] >= 4 ))
7704 case SMDSEntity_BiQuad_Quadrangle: // Bi-Quadratic QUADRANGLE
7713 if ( nbUniqueNodes == 7 &&
7715 ( nbRepl == 1 || iRepl[1] != 8 ))
7721 case SMDSEntity_Penta: ///////////////////////////////////// PENTAHEDRON
7723 if ( nbUniqueNodes == 4 ) {
7724 // ---------------------------------> tetrahedron
7725 if ( curNodes[3] == curNodes[4] &&
7726 curNodes[3] == curNodes[5] ) {
7730 else if ( curNodes[0] == curNodes[1] &&
7731 curNodes[0] == curNodes[2] ) {
7732 // bottom nodes stick: set a top before
7733 uniqueNodes[ 3 ] = uniqueNodes [ 0 ];
7734 uniqueNodes[ 0 ] = curNodes [ 5 ];
7735 uniqueNodes[ 1 ] = curNodes [ 4 ];
7736 uniqueNodes[ 2 ] = curNodes [ 3 ];
7739 else if (( curNodes[0] == curNodes[3] ) +
7740 ( curNodes[1] == curNodes[4] ) +
7741 ( curNodes[2] == curNodes[5] ) == 2 ) {
7742 // a lateral face turns into a line
7746 else if ( nbUniqueNodes == 5 ) {
7747 // PENTAHEDRON --------------------> pyramid
7748 if ( curNodes[0] == curNodes[3] )
7750 uniqueNodes[ 0 ] = curNodes[ 1 ];
7751 uniqueNodes[ 1 ] = curNodes[ 4 ];
7752 uniqueNodes[ 2 ] = curNodes[ 5 ];
7753 uniqueNodes[ 3 ] = curNodes[ 2 ];
7754 uniqueNodes[ 4 ] = curNodes[ 0 ];
7757 if ( curNodes[1] == curNodes[4] )
7759 uniqueNodes[ 0 ] = curNodes[ 0 ];
7760 uniqueNodes[ 1 ] = curNodes[ 2 ];
7761 uniqueNodes[ 2 ] = curNodes[ 5 ];
7762 uniqueNodes[ 3 ] = curNodes[ 3 ];
7763 uniqueNodes[ 4 ] = curNodes[ 1 ];
7766 if ( curNodes[2] == curNodes[5] )
7768 uniqueNodes[ 0 ] = curNodes[ 0 ];
7769 uniqueNodes[ 1 ] = curNodes[ 3 ];
7770 uniqueNodes[ 2 ] = curNodes[ 4 ];
7771 uniqueNodes[ 3 ] = curNodes[ 1 ];
7772 uniqueNodes[ 4 ] = curNodes[ 2 ];
7778 case SMDSEntity_Hexa:
7780 //////////////////////////////////// HEXAHEDRON
7781 SMDS_VolumeTool hexa (elem);
7782 hexa.SetExternalNormal();
7783 if ( nbUniqueNodes == 4 && nbRepl == 4 ) {
7784 //////////////////////// HEX ---> tetrahedron
7785 for ( int iFace = 0; iFace < 6; iFace++ ) {
7786 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7787 if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7788 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7789 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7790 // one face turns into a point ...
7791 int pickInd = ind[ 0 ];
7792 int iOppFace = hexa.GetOppFaceIndex( iFace );
7793 ind = hexa.GetFaceNodesIndices( iOppFace );
7795 uniqueNodes.clear();
7796 for ( iCur = 0; iCur < 4 && nbStick < 2; iCur++ ) {
7797 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7800 uniqueNodes.push_back( curNodes[ind[ iCur ]]);
7802 if ( nbStick == 1 ) {
7803 // ... and the opposite one - into a triangle.
7805 uniqueNodes.push_back( curNodes[ pickInd ]);
7812 else if ( nbUniqueNodes == 6 && nbRepl == 2 ) {
7813 //////////////////////// HEX ---> prism
7814 int nbTria = 0, iTria[3];
7815 const int *ind; // indices of face nodes
7816 // look for triangular faces
7817 for ( int iFace = 0; iFace < 6 && nbTria < 3; iFace++ ) {
7818 ind = hexa.GetFaceNodesIndices( iFace );
7819 TIDSortedNodeSet faceNodes;
7820 for ( iCur = 0; iCur < 4; iCur++ )
7821 faceNodes.insert( curNodes[ind[iCur]] );
7822 if ( faceNodes.size() == 3 )
7823 iTria[ nbTria++ ] = iFace;
7825 // check if triangles are opposite
7826 if ( nbTria == 2 && iTria[0] == hexa.GetOppFaceIndex( iTria[1] ))
7828 // set nodes of the bottom triangle
7829 ind = hexa.GetFaceNodesIndices( iTria[ 0 ]);
7831 for ( iCur = 0; iCur < 4; iCur++ )
7832 if ( ind[iCur] != iRepl[0] && ind[iCur] != iRepl[1])
7833 indB.push_back( ind[iCur] );
7834 if ( !hexa.IsForward() )
7835 std::swap( indB[0], indB[2] );
7836 for ( iCur = 0; iCur < 3; iCur++ )
7837 uniqueNodes[ iCur ] = curNodes[indB[iCur]];
7838 // set nodes of the top triangle
7839 const int *indT = hexa.GetFaceNodesIndices( iTria[ 1 ]);
7840 for ( iCur = 0; iCur < 3; ++iCur )
7841 for ( int j = 0; j < 4; ++j )
7842 if ( hexa.IsLinked( indB[ iCur ], indT[ j ] ))
7844 uniqueNodes[ iCur + 3 ] = curNodes[ indT[ j ]];
7851 else if (nbUniqueNodes == 5 && nbRepl == 3 ) {
7852 //////////////////// HEXAHEDRON ---> pyramid
7853 for ( int iFace = 0; iFace < 6; iFace++ ) {
7854 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7855 if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7856 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7857 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7858 // one face turns into a point ...
7859 int iOppFace = hexa.GetOppFaceIndex( iFace );
7860 ind = hexa.GetFaceNodesIndices( iOppFace );
7861 uniqueNodes.clear();
7862 for ( iCur = 0; iCur < 4; iCur++ ) {
7863 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7866 uniqueNodes.push_back( curNodes[ind[ iCur ]]);
7868 if ( uniqueNodes.size() == 4 ) {
7869 // ... and the opposite one is a quadrangle
7871 const int* indTop = hexa.GetFaceNodesIndices( iFace );
7872 uniqueNodes.push_back( curNodes[indTop[ 0 ]]);
7880 if ( toRemove && nbUniqueNodes > 4 ) {
7881 ////////////////// HEXAHEDRON ---> polyhedron
7882 hexa.SetExternalNormal();
7883 vector<const SMDS_MeshNode *>& poly_nodes = newElemDefs[0].myNodes;
7884 vector<int> & quantities = newElemDefs[0].myPolyhedQuantities;
7885 poly_nodes.reserve( 6 * 4 ); poly_nodes.clear();
7886 quantities.reserve( 6 ); quantities.clear();
7887 for ( int iFace = 0; iFace < 6; iFace++ )
7889 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7890 if ( curNodes[ind[0]] == curNodes[ind[2]] ||
7891 curNodes[ind[1]] == curNodes[ind[3]] )
7894 break; // opposite nodes stick
7897 for ( iCur = 0; iCur < 4; iCur++ )
7899 if ( nodeSet.insert( curNodes[ind[ iCur ]] ).second )
7900 poly_nodes.push_back( curNodes[ind[ iCur ]]);
7902 if ( nodeSet.size() < 3 )
7903 poly_nodes.resize( poly_nodes.size() - nodeSet.size() );
7905 quantities.push_back( nodeSet.size() );
7907 if ( quantities.size() >= 4 )
7910 nbUniqueNodes = poly_nodes.size();
7911 newElemDefs[0].SetPoly(true);
7915 } // case HEXAHEDRON
7920 } // switch ( entity )
7922 if ( toRemove && nbResElems == 0 && avoidMakingHoles )
7924 // erase from nodeNodeMap nodes whose merge spoils elem
7925 vector< const SMDS_MeshNode* > noMergeNodes;
7926 SMESH_MeshAlgos::DeMerge( elem, curNodes, noMergeNodes );
7927 for ( size_t i = 0; i < noMergeNodes.size(); ++i )
7928 nodeNodeMap.erase( noMergeNodes[i] );
7931 } // if ( nbNodes != nbUniqueNodes ) // some nodes stick
7933 uniqueNodes.resize( nbUniqueNodes );
7935 if ( !toRemove && nbResElems == 0 )
7938 newElemDefs.resize( nbResElems );
7944 // ========================================================
7945 // class : SortableElement
7946 // purpose : allow sorting elements basing on their nodes
7947 // ========================================================
7948 class SortableElement : public set <const SMDS_MeshElement*>
7952 SortableElement( const SMDS_MeshElement* theElem )
7955 SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator();
7956 while ( nodeIt->more() )
7957 this->insert( nodeIt->next() );
7960 const SMDS_MeshElement* Get() const
7964 mutable const SMDS_MeshElement* myElem;
7967 //=======================================================================
7968 //function : FindEqualElements
7969 //purpose : Return list of group of elements built on the same nodes.
7970 // Search among theElements or in the whole mesh if theElements is empty
7971 //=======================================================================
7973 void SMESH_MeshEditor::FindEqualElements(TIDSortedElemSet & theElements,
7974 TListOfListOfElementsID & theGroupsOfElementsID)
7976 myLastCreatedElems.Clear();
7977 myLastCreatedNodes.Clear();
7979 typedef map< SortableElement, int > TMapOfNodeSet;
7980 typedef list<int> TGroupOfElems;
7982 if ( theElements.empty() )
7983 { // get all elements in the mesh
7984 SMDS_ElemIteratorPtr eIt = GetMeshDS()->elementsIterator();
7985 while ( eIt->more() )
7986 theElements.insert( theElements.end(), eIt->next() );
7989 vector< TGroupOfElems > arrayOfGroups;
7990 TGroupOfElems groupOfElems;
7991 TMapOfNodeSet mapOfNodeSet;
7993 TIDSortedElemSet::iterator elemIt = theElements.begin();
7994 for ( int i = 0; elemIt != theElements.end(); ++elemIt )
7996 const SMDS_MeshElement* curElem = *elemIt;
7997 SortableElement SE(curElem);
7999 pair< TMapOfNodeSet::iterator, bool> pp = mapOfNodeSet.insert(make_pair(SE, i));
8000 if ( !pp.second ) { // one more coincident elem
8001 TMapOfNodeSet::iterator& itSE = pp.first;
8002 int ind = (*itSE).second;
8003 arrayOfGroups[ind].push_back( curElem->GetID() );
8006 arrayOfGroups.push_back( groupOfElems );
8007 arrayOfGroups.back().push_back( curElem->GetID() );
8012 groupOfElems.clear();
8013 vector< TGroupOfElems >::iterator groupIt = arrayOfGroups.begin();
8014 for ( ; groupIt != arrayOfGroups.end(); ++groupIt )
8016 if ( groupIt->size() > 1 ) {
8017 //groupOfElems.sort(); -- theElements is sorted already
8018 theGroupsOfElementsID.push_back( groupOfElems );
8019 theGroupsOfElementsID.back().splice( theGroupsOfElementsID.back().end(), *groupIt );
8024 //=======================================================================
8025 //function : MergeElements
8026 //purpose : In each given group, substitute all elements by the first one.
8027 //=======================================================================
8029 void SMESH_MeshEditor::MergeElements(TListOfListOfElementsID & theGroupsOfElementsID)
8031 myLastCreatedElems.Clear();
8032 myLastCreatedNodes.Clear();
8034 typedef list<int> TListOfIDs;
8035 TListOfIDs rmElemIds; // IDs of elems to remove
8037 SMESHDS_Mesh* aMesh = GetMeshDS();
8039 TListOfListOfElementsID::iterator groupsIt = theGroupsOfElementsID.begin();
8040 while ( groupsIt != theGroupsOfElementsID.end() ) {
8041 TListOfIDs& aGroupOfElemID = *groupsIt;
8042 aGroupOfElemID.sort();
8043 int elemIDToKeep = aGroupOfElemID.front();
8044 const SMDS_MeshElement* elemToKeep = aMesh->FindElement(elemIDToKeep);
8045 aGroupOfElemID.pop_front();
8046 TListOfIDs::iterator idIt = aGroupOfElemID.begin();
8047 while ( idIt != aGroupOfElemID.end() ) {
8048 int elemIDToRemove = *idIt;
8049 const SMDS_MeshElement* elemToRemove = aMesh->FindElement(elemIDToRemove);
8050 // add the kept element in groups of removed one (PAL15188)
8051 AddToSameGroups( elemToKeep, elemToRemove, aMesh );
8052 rmElemIds.push_back( elemIDToRemove );
8058 Remove( rmElemIds, false );
8061 //=======================================================================
8062 //function : MergeEqualElements
8063 //purpose : Remove all but one of elements built on the same nodes.
8064 //=======================================================================
8066 void SMESH_MeshEditor::MergeEqualElements()
8068 TIDSortedElemSet aMeshElements; /* empty input ==
8069 to merge equal elements in the whole mesh */
8070 TListOfListOfElementsID aGroupsOfElementsID;
8071 FindEqualElements(aMeshElements, aGroupsOfElementsID);
8072 MergeElements(aGroupsOfElementsID);
8075 //=======================================================================
8076 //function : findAdjacentFace
8078 //=======================================================================
8080 static const SMDS_MeshElement* findAdjacentFace(const SMDS_MeshNode* n1,
8081 const SMDS_MeshNode* n2,
8082 const SMDS_MeshElement* elem)
8084 TIDSortedElemSet elemSet, avoidSet;
8086 avoidSet.insert ( elem );
8087 return SMESH_MeshAlgos::FindFaceInSet( n1, n2, elemSet, avoidSet );
8090 //=======================================================================
8091 //function : findSegment
8092 //purpose : Return a mesh segment by two nodes one of which can be medium
8093 //=======================================================================
8095 static const SMDS_MeshElement* findSegment(const SMDS_MeshNode* n1,
8096 const SMDS_MeshNode* n2)
8098 SMDS_ElemIteratorPtr it = n1->GetInverseElementIterator( SMDSAbs_Edge );
8099 while ( it->more() )
8101 const SMDS_MeshElement* seg = it->next();
8102 if ( seg->GetNodeIndex( n2 ) >= 0 )
8108 //=======================================================================
8109 //function : FindFreeBorder
8111 //=======================================================================
8113 #define ControlFreeBorder SMESH::Controls::FreeEdges::IsFreeEdge
8115 bool SMESH_MeshEditor::FindFreeBorder (const SMDS_MeshNode* theFirstNode,
8116 const SMDS_MeshNode* theSecondNode,
8117 const SMDS_MeshNode* theLastNode,
8118 list< const SMDS_MeshNode* > & theNodes,
8119 list< const SMDS_MeshElement* >& theFaces)
8121 if ( !theFirstNode || !theSecondNode )
8123 // find border face between theFirstNode and theSecondNode
8124 const SMDS_MeshElement* curElem = findAdjacentFace( theFirstNode, theSecondNode, 0 );
8128 theFaces.push_back( curElem );
8129 theNodes.push_back( theFirstNode );
8130 theNodes.push_back( theSecondNode );
8132 const SMDS_MeshNode *nIgnore = theFirstNode, *nStart = theSecondNode;
8133 TIDSortedElemSet foundElems;
8134 bool needTheLast = ( theLastNode != 0 );
8136 while ( nStart != theLastNode ) {
8137 if ( nStart == theFirstNode )
8138 return !needTheLast;
8140 // find all free border faces sharing form nStart
8142 list< const SMDS_MeshElement* > curElemList;
8143 list< const SMDS_MeshNode* > nStartList;
8144 SMDS_ElemIteratorPtr invElemIt = nStart->GetInverseElementIterator(SMDSAbs_Face);
8145 while ( invElemIt->more() ) {
8146 const SMDS_MeshElement* e = invElemIt->next();
8147 if ( e == curElem || foundElems.insert( e ).second ) {
8149 int iNode = 0, nbNodes = e->NbNodes();
8150 vector<const SMDS_MeshNode*> nodes(nbNodes+1);
8152 if ( e->IsQuadratic() ) {
8153 const SMDS_VtkFace* F =
8154 dynamic_cast<const SMDS_VtkFace*>(e);
8155 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8156 // use special nodes iterator
8157 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8158 while( anIter->more() ) {
8159 nodes[ iNode++ ] = cast2Node(anIter->next());
8163 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
8164 while ( nIt->more() )
8165 nodes[ iNode++ ] = static_cast<const SMDS_MeshNode*>( nIt->next() );
8167 nodes[ iNode ] = nodes[ 0 ];
8169 for ( iNode = 0; iNode < nbNodes; iNode++ )
8170 if (((nodes[ iNode ] == nStart && nodes[ iNode + 1] != nIgnore ) ||
8171 (nodes[ iNode + 1] == nStart && nodes[ iNode ] != nIgnore )) &&
8172 ControlFreeBorder( &nodes[ iNode ], e->GetID() ))
8174 nStartList.push_back( nodes[ iNode + ( nodes[ iNode ] == nStart ? 1 : 0 )]);
8175 curElemList.push_back( e );
8179 // analyse the found
8181 int nbNewBorders = curElemList.size();
8182 if ( nbNewBorders == 0 ) {
8183 // no free border furthermore
8184 return !needTheLast;
8186 else if ( nbNewBorders == 1 ) {
8187 // one more element found
8189 nStart = nStartList.front();
8190 curElem = curElemList.front();
8191 theFaces.push_back( curElem );
8192 theNodes.push_back( nStart );
8195 // several continuations found
8196 list< const SMDS_MeshElement* >::iterator curElemIt;
8197 list< const SMDS_MeshNode* >::iterator nStartIt;
8198 // check if one of them reached the last node
8199 if ( needTheLast ) {
8200 for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
8201 curElemIt!= curElemList.end();
8202 curElemIt++, nStartIt++ )
8203 if ( *nStartIt == theLastNode ) {
8204 theFaces.push_back( *curElemIt );
8205 theNodes.push_back( *nStartIt );
8209 // find the best free border by the continuations
8210 list<const SMDS_MeshNode*> contNodes[ 2 ], *cNL;
8211 list<const SMDS_MeshElement*> contFaces[ 2 ], *cFL;
8212 for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
8213 curElemIt!= curElemList.end();
8214 curElemIt++, nStartIt++ )
8216 cNL = & contNodes[ contNodes[0].empty() ? 0 : 1 ];
8217 cFL = & contFaces[ contFaces[0].empty() ? 0 : 1 ];
8218 // find one more free border
8219 if ( ! SMESH_MeshEditor::FindFreeBorder( nStart, *nStartIt, theLastNode, *cNL, *cFL )) {
8223 else if ( !contNodes[0].empty() && !contNodes[1].empty() ) {
8224 // choice: clear a worse one
8225 int iLongest = ( contNodes[0].size() < contNodes[1].size() ? 1 : 0 );
8226 int iWorse = ( needTheLast ? 1 - iLongest : iLongest );
8227 contNodes[ iWorse ].clear();
8228 contFaces[ iWorse ].clear();
8231 if ( contNodes[0].empty() && contNodes[1].empty() )
8234 // append the best free border
8235 cNL = & contNodes[ contNodes[0].empty() ? 1 : 0 ];
8236 cFL = & contFaces[ contFaces[0].empty() ? 1 : 0 ];
8237 theNodes.pop_back(); // remove nIgnore
8238 theNodes.pop_back(); // remove nStart
8239 theFaces.pop_back(); // remove curElem
8240 list< const SMDS_MeshNode* >::iterator nIt = cNL->begin();
8241 list< const SMDS_MeshElement* >::iterator fIt = cFL->begin();
8242 for ( ; nIt != cNL->end(); nIt++ ) theNodes.push_back( *nIt );
8243 for ( ; fIt != cFL->end(); fIt++ ) theFaces.push_back( *fIt );
8246 } // several continuations found
8247 } // while ( nStart != theLastNode )
8252 //=======================================================================
8253 //function : CheckFreeBorderNodes
8254 //purpose : Return true if the tree nodes are on a free border
8255 //=======================================================================
8257 bool SMESH_MeshEditor::CheckFreeBorderNodes(const SMDS_MeshNode* theNode1,
8258 const SMDS_MeshNode* theNode2,
8259 const SMDS_MeshNode* theNode3)
8261 list< const SMDS_MeshNode* > nodes;
8262 list< const SMDS_MeshElement* > faces;
8263 return FindFreeBorder( theNode1, theNode2, theNode3, nodes, faces);
8266 //=======================================================================
8267 //function : SewFreeBorder
8269 //warning : for border-to-side sewing theSideSecondNode is considered as
8270 // the last side node and theSideThirdNode is not used
8271 //=======================================================================
8273 SMESH_MeshEditor::Sew_Error
8274 SMESH_MeshEditor::SewFreeBorder (const SMDS_MeshNode* theBordFirstNode,
8275 const SMDS_MeshNode* theBordSecondNode,
8276 const SMDS_MeshNode* theBordLastNode,
8277 const SMDS_MeshNode* theSideFirstNode,
8278 const SMDS_MeshNode* theSideSecondNode,
8279 const SMDS_MeshNode* theSideThirdNode,
8280 const bool theSideIsFreeBorder,
8281 const bool toCreatePolygons,
8282 const bool toCreatePolyedrs)
8284 myLastCreatedElems.Clear();
8285 myLastCreatedNodes.Clear();
8287 Sew_Error aResult = SEW_OK;
8289 // ====================================
8290 // find side nodes and elements
8291 // ====================================
8293 list< const SMDS_MeshNode* > nSide[ 2 ];
8294 list< const SMDS_MeshElement* > eSide[ 2 ];
8295 list< const SMDS_MeshNode* >::iterator nIt[ 2 ];
8296 list< const SMDS_MeshElement* >::iterator eIt[ 2 ];
8300 if (!FindFreeBorder(theBordFirstNode,theBordSecondNode,theBordLastNode,
8301 nSide[0], eSide[0])) {
8302 MESSAGE(" Free Border 1 not found " );
8303 aResult = SEW_BORDER1_NOT_FOUND;
8305 if (theSideIsFreeBorder) {
8308 if (!FindFreeBorder(theSideFirstNode, theSideSecondNode, theSideThirdNode,
8309 nSide[1], eSide[1])) {
8310 MESSAGE(" Free Border 2 not found " );
8311 aResult = ( aResult != SEW_OK ? SEW_BOTH_BORDERS_NOT_FOUND : SEW_BORDER2_NOT_FOUND );
8314 if ( aResult != SEW_OK )
8317 if (!theSideIsFreeBorder) {
8321 // -------------------------------------------------------------------------
8323 // 1. If nodes to merge are not coincident, move nodes of the free border
8324 // from the coord sys defined by the direction from the first to last
8325 // nodes of the border to the correspondent sys of the side 2
8326 // 2. On the side 2, find the links most co-directed with the correspondent
8327 // links of the free border
8328 // -------------------------------------------------------------------------
8330 // 1. Since sewing may break if there are volumes to split on the side 2,
8331 // we won't move nodes but just compute new coordinates for them
8332 typedef map<const SMDS_MeshNode*, gp_XYZ> TNodeXYZMap;
8333 TNodeXYZMap nBordXYZ;
8334 list< const SMDS_MeshNode* >& bordNodes = nSide[ 0 ];
8335 list< const SMDS_MeshNode* >::iterator nBordIt;
8337 gp_XYZ Pb1( theBordFirstNode->X(), theBordFirstNode->Y(), theBordFirstNode->Z() );
8338 gp_XYZ Pb2( theBordLastNode->X(), theBordLastNode->Y(), theBordLastNode->Z() );
8339 gp_XYZ Ps1( theSideFirstNode->X(), theSideFirstNode->Y(), theSideFirstNode->Z() );
8340 gp_XYZ Ps2( theSideSecondNode->X(), theSideSecondNode->Y(), theSideSecondNode->Z() );
8341 double tol2 = 1.e-8;
8342 gp_Vec Vbs1( Pb1 - Ps1 ),Vbs2( Pb2 - Ps2 );
8343 if ( Vbs1.SquareMagnitude() > tol2 || Vbs2.SquareMagnitude() > tol2 ) {
8344 // Need node movement.
8346 // find X and Z axes to create trsf
8347 gp_Vec Zb( Pb1 - Pb2 ), Zs( Ps1 - Ps2 );
8349 if ( X.SquareMagnitude() <= gp::Resolution() * gp::Resolution() )
8351 X = gp_Ax2( gp::Origin(), Zb ).XDirection();
8354 gp_Ax3 toBordAx( Pb1, Zb, X );
8355 gp_Ax3 fromSideAx( Ps1, Zs, X );
8356 gp_Ax3 toGlobalAx( gp::Origin(), gp::DZ(), gp::DX() );
8358 gp_Trsf toBordSys, fromSide2Sys;
8359 toBordSys.SetTransformation( toBordAx );
8360 fromSide2Sys.SetTransformation( fromSideAx, toGlobalAx );
8361 fromSide2Sys.SetScaleFactor( Zs.Magnitude() / Zb.Magnitude() );
8364 for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
8365 const SMDS_MeshNode* n = *nBordIt;
8366 gp_XYZ xyz( n->X(),n->Y(),n->Z() );
8367 toBordSys.Transforms( xyz );
8368 fromSide2Sys.Transforms( xyz );
8369 nBordXYZ.insert( TNodeXYZMap::value_type( n, xyz ));
8373 // just insert nodes XYZ in the nBordXYZ map
8374 for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
8375 const SMDS_MeshNode* n = *nBordIt;
8376 nBordXYZ.insert( TNodeXYZMap::value_type( n, gp_XYZ( n->X(),n->Y(),n->Z() )));
8380 // 2. On the side 2, find the links most co-directed with the correspondent
8381 // links of the free border
8383 list< const SMDS_MeshElement* >& sideElems = eSide[ 1 ];
8384 list< const SMDS_MeshNode* >& sideNodes = nSide[ 1 ];
8385 sideNodes.push_back( theSideFirstNode );
8387 bool hasVolumes = false;
8388 LinkID_Gen aLinkID_Gen( GetMeshDS() );
8389 set<long> foundSideLinkIDs, checkedLinkIDs;
8390 SMDS_VolumeTool volume;
8391 //const SMDS_MeshNode* faceNodes[ 4 ];
8393 const SMDS_MeshNode* sideNode;
8394 const SMDS_MeshElement* sideElem = 0;
8395 const SMDS_MeshNode* prevSideNode = theSideFirstNode;
8396 const SMDS_MeshNode* prevBordNode = theBordFirstNode;
8397 nBordIt = bordNodes.begin();
8399 // border node position and border link direction to compare with
8400 gp_XYZ bordPos = nBordXYZ[ *nBordIt ];
8401 gp_XYZ bordDir = bordPos - nBordXYZ[ prevBordNode ];
8402 // choose next side node by link direction or by closeness to
8403 // the current border node:
8404 bool searchByDir = ( *nBordIt != theBordLastNode );
8406 // find the next node on the Side 2
8408 double maxDot = -DBL_MAX, minDist = DBL_MAX;
8410 checkedLinkIDs.clear();
8411 gp_XYZ prevXYZ( prevSideNode->X(), prevSideNode->Y(), prevSideNode->Z() );
8413 // loop on inverse elements of current node (prevSideNode) on the Side 2
8414 SMDS_ElemIteratorPtr invElemIt = prevSideNode->GetInverseElementIterator();
8415 while ( invElemIt->more() )
8417 const SMDS_MeshElement* elem = invElemIt->next();
8418 // prepare data for a loop on links coming to prevSideNode, of a face or a volume
8419 int iPrevNode = 0, iNode = 0, nbNodes = elem->NbNodes();
8420 vector< const SMDS_MeshNode* > faceNodes( nbNodes, (const SMDS_MeshNode*)0 );
8421 bool isVolume = volume.Set( elem );
8422 const SMDS_MeshNode** nodes = isVolume ? volume.GetNodes() : & faceNodes[0];
8423 if ( isVolume ) // --volume
8425 else if ( elem->GetType()==SMDSAbs_Face ) { // --face
8426 // retrieve all face nodes and find iPrevNode - an index of the prevSideNode
8427 if(elem->IsQuadratic()) {
8428 const SMDS_VtkFace* F =
8429 dynamic_cast<const SMDS_VtkFace*>(elem);
8430 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8431 // use special nodes iterator
8432 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8433 while( anIter->more() ) {
8434 nodes[ iNode ] = cast2Node(anIter->next());
8435 if ( nodes[ iNode++ ] == prevSideNode )
8436 iPrevNode = iNode - 1;
8440 SMDS_ElemIteratorPtr nIt = elem->nodesIterator();
8441 while ( nIt->more() ) {
8442 nodes[ iNode ] = cast2Node( nIt->next() );
8443 if ( nodes[ iNode++ ] == prevSideNode )
8444 iPrevNode = iNode - 1;
8447 // there are 2 links to check
8452 // loop on links, to be precise, on the second node of links
8453 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
8454 const SMDS_MeshNode* n = nodes[ iNode ];
8456 if ( !volume.IsLinked( n, prevSideNode ))
8460 if ( iNode ) // a node before prevSideNode
8461 n = nodes[ iPrevNode == 0 ? elem->NbNodes() - 1 : iPrevNode - 1 ];
8462 else // a node after prevSideNode
8463 n = nodes[ iPrevNode + 1 == elem->NbNodes() ? 0 : iPrevNode + 1 ];
8465 // check if this link was already used
8466 long iLink = aLinkID_Gen.GetLinkID( prevSideNode, n );
8467 bool isJustChecked = !checkedLinkIDs.insert( iLink ).second;
8468 if (!isJustChecked &&
8469 foundSideLinkIDs.find( iLink ) == foundSideLinkIDs.end() )
8471 // test a link geometrically
8472 gp_XYZ nextXYZ ( n->X(), n->Y(), n->Z() );
8473 bool linkIsBetter = false;
8474 double dot = 0.0, dist = 0.0;
8475 if ( searchByDir ) { // choose most co-directed link
8476 dot = bordDir * ( nextXYZ - prevXYZ ).Normalized();
8477 linkIsBetter = ( dot > maxDot );
8479 else { // choose link with the node closest to bordPos
8480 dist = ( nextXYZ - bordPos ).SquareModulus();
8481 linkIsBetter = ( dist < minDist );
8483 if ( linkIsBetter ) {
8492 } // loop on inverse elements of prevSideNode
8495 MESSAGE(" Can't find path by links of the Side 2 ");
8496 return SEW_BAD_SIDE_NODES;
8498 sideNodes.push_back( sideNode );
8499 sideElems.push_back( sideElem );
8500 foundSideLinkIDs.insert ( linkID );
8501 prevSideNode = sideNode;
8503 if ( *nBordIt == theBordLastNode )
8504 searchByDir = false;
8506 // find the next border link to compare with
8507 gp_XYZ sidePos( sideNode->X(), sideNode->Y(), sideNode->Z() );
8508 searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8509 // move to next border node if sideNode is before forward border node (bordPos)
8510 while ( *nBordIt != theBordLastNode && !searchByDir ) {
8511 prevBordNode = *nBordIt;
8513 bordPos = nBordXYZ[ *nBordIt ];
8514 bordDir = bordPos - nBordXYZ[ prevBordNode ];
8515 searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8519 while ( sideNode != theSideSecondNode );
8521 if ( hasVolumes && sideNodes.size () != bordNodes.size() && !toCreatePolyedrs) {
8522 MESSAGE("VOLUME SPLITTING IS FORBIDDEN");
8523 return SEW_VOLUMES_TO_SPLIT; // volume splitting is forbidden
8525 } // end nodes search on the side 2
8527 // ============================
8528 // sew the border to the side 2
8529 // ============================
8531 int nbNodes[] = { (int)nSide[0].size(), (int)nSide[1].size() };
8532 int maxNbNodes = Max( nbNodes[0], nbNodes[1] );
8534 bool toMergeConformal = ( nbNodes[0] == nbNodes[1] );
8535 if ( toMergeConformal && toCreatePolygons )
8537 // do not merge quadrangles if polygons are OK (IPAL0052824)
8538 eIt[0] = eSide[0].begin();
8539 eIt[1] = eSide[1].begin();
8540 bool allQuads[2] = { true, true };
8541 for ( int iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8542 for ( ; allQuads[iBord] && eIt[iBord] != eSide[iBord].end(); ++eIt[iBord] )
8543 allQuads[iBord] = ( (*eIt[iBord])->NbCornerNodes() == 4 );
8545 toMergeConformal = ( !allQuads[0] && !allQuads[1] );
8548 TListOfListOfNodes nodeGroupsToMerge;
8549 if (( toMergeConformal ) ||
8550 ( theSideIsFreeBorder && !theSideThirdNode )) {
8552 // all nodes are to be merged
8554 for (nIt[0] = nSide[0].begin(), nIt[1] = nSide[1].begin();
8555 nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end();
8556 nIt[0]++, nIt[1]++ )
8558 nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8559 nodeGroupsToMerge.back().push_back( *nIt[1] ); // to keep
8560 nodeGroupsToMerge.back().push_back( *nIt[0] ); // to remove
8565 // insert new nodes into the border and the side to get equal nb of segments
8567 // get normalized parameters of nodes on the borders
8568 vector< double > param[ 2 ];
8569 param[0].resize( maxNbNodes );
8570 param[1].resize( maxNbNodes );
8572 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8573 list< const SMDS_MeshNode* >& nodes = nSide[ iBord ];
8574 list< const SMDS_MeshNode* >::iterator nIt = nodes.begin();
8575 const SMDS_MeshNode* nPrev = *nIt;
8576 double bordLength = 0;
8577 for ( iNode = 0; nIt != nodes.end(); nIt++, iNode++ ) { // loop on border nodes
8578 const SMDS_MeshNode* nCur = *nIt;
8579 gp_XYZ segment (nCur->X() - nPrev->X(),
8580 nCur->Y() - nPrev->Y(),
8581 nCur->Z() - nPrev->Z());
8582 double segmentLen = segment.Modulus();
8583 bordLength += segmentLen;
8584 param[ iBord ][ iNode ] = bordLength;
8587 // normalize within [0,1]
8588 for ( iNode = 0; iNode < nbNodes[ iBord ]; iNode++ ) {
8589 param[ iBord ][ iNode ] /= bordLength;
8593 // loop on border segments
8594 const SMDS_MeshNode *nPrev[ 2 ] = { 0, 0 };
8595 int i[ 2 ] = { 0, 0 };
8596 nIt[0] = nSide[0].begin(); eIt[0] = eSide[0].begin();
8597 nIt[1] = nSide[1].begin(); eIt[1] = eSide[1].begin();
8599 TElemOfNodeListMap insertMap;
8600 TElemOfNodeListMap::iterator insertMapIt;
8602 // key: elem to insert nodes into
8603 // value: 2 nodes to insert between + nodes to be inserted
8605 bool next[ 2 ] = { false, false };
8607 // find min adjacent segment length after sewing
8608 double nextParam = 10., prevParam = 0;
8609 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8610 if ( i[ iBord ] + 1 < nbNodes[ iBord ])
8611 nextParam = Min( nextParam, param[iBord][ i[iBord] + 1 ]);
8612 if ( i[ iBord ] > 0 )
8613 prevParam = Max( prevParam, param[iBord][ i[iBord] - 1 ]);
8615 double minParam = Min( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8616 double maxParam = Max( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8617 double minSegLen = Min( nextParam - minParam, maxParam - prevParam );
8619 // choose to insert or to merge nodes
8620 double du = param[ 1 ][ i[1] ] - param[ 0 ][ i[0] ];
8621 if ( Abs( du ) <= minSegLen * 0.2 ) {
8624 nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8625 const SMDS_MeshNode* n0 = *nIt[0];
8626 const SMDS_MeshNode* n1 = *nIt[1];
8627 nodeGroupsToMerge.back().push_back( n1 );
8628 nodeGroupsToMerge.back().push_back( n0 );
8629 // position of node of the border changes due to merge
8630 param[ 0 ][ i[0] ] += du;
8631 // move n1 for the sake of elem shape evaluation during insertion.
8632 // n1 will be removed by MergeNodes() anyway
8633 const_cast<SMDS_MeshNode*>( n0 )->setXYZ( n1->X(), n1->Y(), n1->Z() );
8634 next[0] = next[1] = true;
8639 int intoBord = ( du < 0 ) ? 0 : 1;
8640 const SMDS_MeshElement* elem = *eIt [ intoBord ];
8641 const SMDS_MeshNode* n1 = nPrev[ intoBord ];
8642 const SMDS_MeshNode* n2 = *nIt [ intoBord ];
8643 const SMDS_MeshNode* nIns = *nIt [ 1 - intoBord ];
8644 if ( intoBord == 1 ) {
8645 // move node of the border to be on a link of elem of the side
8646 gp_XYZ p1 (n1->X(), n1->Y(), n1->Z());
8647 gp_XYZ p2 (n2->X(), n2->Y(), n2->Z());
8648 double ratio = du / ( param[ 1 ][ i[1] ] - param[ 1 ][ i[1]-1 ]);
8649 gp_XYZ p = p2 * ( 1 - ratio ) + p1 * ratio;
8650 GetMeshDS()->MoveNode( nIns, p.X(), p.Y(), p.Z() );
8652 insertMapIt = insertMap.find( elem );
8653 bool notFound = ( insertMapIt == insertMap.end() );
8654 bool otherLink = ( !notFound && (*insertMapIt).second.front() != n1 );
8656 // insert into another link of the same element:
8657 // 1. perform insertion into the other link of the elem
8658 list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8659 const SMDS_MeshNode* n12 = nodeList.front(); nodeList.pop_front();
8660 const SMDS_MeshNode* n22 = nodeList.front(); nodeList.pop_front();
8661 InsertNodesIntoLink( elem, n12, n22, nodeList, toCreatePolygons );
8662 // 2. perform insertion into the link of adjacent faces
8663 while ( const SMDS_MeshElement* adjElem = findAdjacentFace( n12, n22, elem )) {
8664 InsertNodesIntoLink( adjElem, n12, n22, nodeList, toCreatePolygons );
8666 while ( const SMDS_MeshElement* seg = findSegment( n12, n22 )) {
8667 InsertNodesIntoLink( seg, n12, n22, nodeList );
8669 if (toCreatePolyedrs) {
8670 // perform insertion into the links of adjacent volumes
8671 UpdateVolumes(n12, n22, nodeList);
8673 // 3. find an element appeared on n1 and n2 after the insertion
8674 insertMap.erase( elem );
8675 elem = findAdjacentFace( n1, n2, 0 );
8677 if ( notFound || otherLink ) {
8678 // add element and nodes of the side into the insertMap
8679 insertMapIt = insertMap.insert( make_pair( elem, list<const SMDS_MeshNode*>() )).first;
8680 (*insertMapIt).second.push_back( n1 );
8681 (*insertMapIt).second.push_back( n2 );
8683 // add node to be inserted into elem
8684 (*insertMapIt).second.push_back( nIns );
8685 next[ 1 - intoBord ] = true;
8688 // go to the next segment
8689 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8690 if ( next[ iBord ] ) {
8691 if ( i[ iBord ] != 0 && eIt[ iBord ] != eSide[ iBord ].end())
8693 nPrev[ iBord ] = *nIt[ iBord ];
8694 nIt[ iBord ]++; i[ iBord ]++;
8698 while ( nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end());
8700 // perform insertion of nodes into elements
8702 for (insertMapIt = insertMap.begin();
8703 insertMapIt != insertMap.end();
8706 const SMDS_MeshElement* elem = (*insertMapIt).first;
8707 list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8708 const SMDS_MeshNode* n1 = nodeList.front(); nodeList.pop_front();
8709 const SMDS_MeshNode* n2 = nodeList.front(); nodeList.pop_front();
8711 InsertNodesIntoLink( elem, n1, n2, nodeList, toCreatePolygons );
8713 while ( const SMDS_MeshElement* seg = findSegment( n1, n2 )) {
8714 InsertNodesIntoLink( seg, n1, n2, nodeList );
8717 if ( !theSideIsFreeBorder ) {
8718 // look for and insert nodes into the faces adjacent to elem
8719 while ( const SMDS_MeshElement* adjElem = findAdjacentFace( n1, n2, elem )) {
8720 InsertNodesIntoLink( adjElem, n1, n2, nodeList, toCreatePolygons );
8723 if (toCreatePolyedrs) {
8724 // perform insertion into the links of adjacent volumes
8725 UpdateVolumes(n1, n2, nodeList);
8728 } // end: insert new nodes
8730 MergeNodes ( nodeGroupsToMerge );
8733 // Remove coincident segments
8736 TIDSortedElemSet segments;
8737 SMESH_SequenceOfElemPtr newFaces;
8738 for ( int i = 1; i <= myLastCreatedElems.Length(); ++i )
8740 if ( !myLastCreatedElems(i) ) continue;
8741 if ( myLastCreatedElems(i)->GetType() == SMDSAbs_Edge )
8742 segments.insert( segments.end(), myLastCreatedElems(i) );
8744 newFaces.Append( myLastCreatedElems(i) );
8746 // get segments adjacent to merged nodes
8747 TListOfListOfNodes::iterator groupIt = nodeGroupsToMerge.begin();
8748 for ( ; groupIt != nodeGroupsToMerge.end(); groupIt++ )
8750 const list<const SMDS_MeshNode*>& nodes = *groupIt;
8751 SMDS_ElemIteratorPtr segIt = nodes.front()->GetInverseElementIterator( SMDSAbs_Edge );
8752 while ( segIt->more() )
8753 segments.insert( segIt->next() );
8757 TListOfListOfElementsID equalGroups;
8758 if ( !segments.empty() )
8759 FindEqualElements( segments, equalGroups );
8760 if ( !equalGroups.empty() )
8762 // remove from segments those that will be removed
8763 TListOfListOfElementsID::iterator itGroups = equalGroups.begin();
8764 for ( ; itGroups != equalGroups.end(); ++itGroups )
8766 list< int >& group = *itGroups;
8767 list< int >::iterator id = group.begin();
8768 for ( ++id; id != group.end(); ++id )
8769 if ( const SMDS_MeshElement* seg = GetMeshDS()->FindElement( *id ))
8770 segments.erase( seg );
8772 // remove equal segments
8773 MergeElements( equalGroups );
8775 // restore myLastCreatedElems
8776 myLastCreatedElems = newFaces;
8777 TIDSortedElemSet::iterator seg = segments.begin();
8778 for ( ; seg != segments.end(); ++seg )
8779 myLastCreatedElems.Append( *seg );
8785 //=======================================================================
8786 //function : InsertNodesIntoLink
8787 //purpose : insert theNodesToInsert into theElement between theBetweenNode1
8788 // and theBetweenNode2 and split theElement
8789 //=======================================================================
8791 void SMESH_MeshEditor::InsertNodesIntoLink(const SMDS_MeshElement* theElement,
8792 const SMDS_MeshNode* theBetweenNode1,
8793 const SMDS_MeshNode* theBetweenNode2,
8794 list<const SMDS_MeshNode*>& theNodesToInsert,
8795 const bool toCreatePoly)
8797 if ( !theElement ) return;
8799 SMESHDS_Mesh *aMesh = GetMeshDS();
8800 vector<const SMDS_MeshElement*> newElems;
8802 if ( theElement->GetType() == SMDSAbs_Edge )
8804 theNodesToInsert.push_front( theBetweenNode1 );
8805 theNodesToInsert.push_back ( theBetweenNode2 );
8806 list<const SMDS_MeshNode*>::iterator n = theNodesToInsert.begin();
8807 const SMDS_MeshNode* n1 = *n;
8808 for ( ++n; n != theNodesToInsert.end(); ++n )
8810 const SMDS_MeshNode* n2 = *n;
8811 if ( const SMDS_MeshElement* seg = aMesh->FindEdge( n1, n2 ))
8812 AddToSameGroups( seg, theElement, aMesh );
8814 newElems.push_back( aMesh->AddEdge ( n1, n2 ));
8817 theNodesToInsert.pop_front();
8818 theNodesToInsert.pop_back();
8820 if ( theElement->IsQuadratic() ) // add a not split part
8822 vector<const SMDS_MeshNode*> nodes( theElement->begin_nodes(),
8823 theElement->end_nodes() );
8824 int iOther = 0, nbN = nodes.size();
8825 for ( ; iOther < nbN; ++iOther )
8826 if ( nodes[iOther] != theBetweenNode1 &&
8827 nodes[iOther] != theBetweenNode2 )
8831 if ( const SMDS_MeshElement* seg = aMesh->FindEdge( nodes[0], nodes[1] ))
8832 AddToSameGroups( seg, theElement, aMesh );
8834 newElems.push_back( aMesh->AddEdge ( nodes[0], nodes[1] ));
8836 else if ( iOther == 2 )
8838 if ( const SMDS_MeshElement* seg = aMesh->FindEdge( nodes[1], nodes[2] ))
8839 AddToSameGroups( seg, theElement, aMesh );
8841 newElems.push_back( aMesh->AddEdge ( nodes[1], nodes[2] ));
8844 // treat new elements
8845 for ( size_t i = 0; i < newElems.size(); ++i )
8848 aMesh->SetMeshElementOnShape( newElems[i], theElement->getshapeId() );
8849 myLastCreatedElems.Append( newElems[i] );
8851 ReplaceElemInGroups( theElement, newElems, aMesh );
8852 aMesh->RemoveElement( theElement );
8855 } // if ( theElement->GetType() == SMDSAbs_Edge )
8857 const SMDS_MeshElement* theFace = theElement;
8858 if ( theFace->GetType() != SMDSAbs_Face ) return;
8860 // find indices of 2 link nodes and of the rest nodes
8861 int iNode = 0, il1, il2, i3, i4;
8862 il1 = il2 = i3 = i4 = -1;
8863 vector<const SMDS_MeshNode*> nodes( theFace->NbNodes() );
8865 SMDS_NodeIteratorPtr nodeIt = theFace->interlacedNodesIterator();
8866 while ( nodeIt->more() ) {
8867 const SMDS_MeshNode* n = nodeIt->next();
8868 if ( n == theBetweenNode1 )
8870 else if ( n == theBetweenNode2 )
8876 nodes[ iNode++ ] = n;
8878 if ( il1 < 0 || il2 < 0 || i3 < 0 )
8881 // arrange link nodes to go one after another regarding the face orientation
8882 bool reverse = ( Abs( il2 - il1 ) == 1 ? il2 < il1 : il1 < il2 );
8883 list<const SMDS_MeshNode *> aNodesToInsert = theNodesToInsert;
8888 aNodesToInsert.reverse();
8890 // check that not link nodes of a quadrangles are in good order
8891 int nbFaceNodes = theFace->NbNodes();
8892 if ( nbFaceNodes == 4 && i4 - i3 != 1 ) {
8898 if (toCreatePoly || theFace->IsPoly()) {
8901 vector<const SMDS_MeshNode *> poly_nodes (nbFaceNodes + aNodesToInsert.size());
8903 // add nodes of face up to first node of link
8906 if ( theFace->IsQuadratic() ) {
8907 const SMDS_VtkFace* F = dynamic_cast<const SMDS_VtkFace*>(theFace);
8908 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8909 // use special nodes iterator
8910 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8911 while( anIter->more() && !isFLN ) {
8912 const SMDS_MeshNode* n = cast2Node(anIter->next());
8913 poly_nodes[iNode++] = n;
8914 if (n == nodes[il1]) {
8918 // add nodes to insert
8919 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8920 for (; nIt != aNodesToInsert.end(); nIt++) {
8921 poly_nodes[iNode++] = *nIt;
8923 // add nodes of face starting from last node of link
8924 while ( anIter->more() ) {
8925 poly_nodes[iNode++] = cast2Node(anIter->next());
8929 SMDS_ElemIteratorPtr nodeIt = theFace->nodesIterator();
8930 while ( nodeIt->more() && !isFLN ) {
8931 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8932 poly_nodes[iNode++] = n;
8933 if (n == nodes[il1]) {
8937 // add nodes to insert
8938 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8939 for (; nIt != aNodesToInsert.end(); nIt++) {
8940 poly_nodes[iNode++] = *nIt;
8942 // add nodes of face starting from last node of link
8943 while ( nodeIt->more() ) {
8944 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8945 poly_nodes[iNode++] = n;
8950 newElems.push_back( aMesh->AddPolygonalFace( poly_nodes ));
8953 else if ( !theFace->IsQuadratic() )
8955 // put aNodesToInsert between theBetweenNode1 and theBetweenNode2
8956 int nbLinkNodes = 2 + aNodesToInsert.size();
8957 //const SMDS_MeshNode* linkNodes[ nbLinkNodes ];
8958 vector<const SMDS_MeshNode*> linkNodes( nbLinkNodes );
8959 linkNodes[ 0 ] = nodes[ il1 ];
8960 linkNodes[ nbLinkNodes - 1 ] = nodes[ il2 ];
8961 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8962 for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
8963 linkNodes[ iNode++ ] = *nIt;
8965 // decide how to split a quadrangle: compare possible variants
8966 // and choose which of splits to be a quadrangle
8967 int i1, i2, iSplit, nbSplits = nbLinkNodes - 1, iBestQuad = 0;
8968 if ( nbFaceNodes == 3 ) {
8969 iBestQuad = nbSplits;
8972 else if ( nbFaceNodes == 4 ) {
8973 SMESH::Controls::NumericalFunctorPtr aCrit( new SMESH::Controls::AspectRatio);
8974 double aBestRate = DBL_MAX;
8975 for ( int iQuad = 0; iQuad < nbSplits; iQuad++ ) {
8977 double aBadRate = 0;
8978 // evaluate elements quality
8979 for ( iSplit = 0; iSplit < nbSplits; iSplit++ ) {
8980 if ( iSplit == iQuad ) {
8981 SMDS_FaceOfNodes quad (linkNodes[ i1++ ],
8985 aBadRate += getBadRate( &quad, aCrit );
8988 SMDS_FaceOfNodes tria (linkNodes[ i1++ ],
8990 nodes[ iSplit < iQuad ? i4 : i3 ]);
8991 aBadRate += getBadRate( &tria, aCrit );
8995 if ( aBadRate < aBestRate ) {
8997 aBestRate = aBadRate;
9002 // create new elements
9004 for ( iSplit = 0; iSplit < nbSplits - 1; iSplit++ )
9006 if ( iSplit == iBestQuad )
9007 newElems.push_back( aMesh->AddFace (linkNodes[ i1++ ],
9012 newElems.push_back( aMesh->AddFace (linkNodes[ i1++ ],
9014 nodes[ iSplit < iBestQuad ? i4 : i3 ]));
9017 const SMDS_MeshNode* newNodes[ 4 ];
9018 newNodes[ 0 ] = linkNodes[ i1 ];
9019 newNodes[ 1 ] = linkNodes[ i2 ];
9020 newNodes[ 2 ] = nodes[ iSplit >= iBestQuad ? i3 : i4 ];
9021 newNodes[ 3 ] = nodes[ i4 ];
9022 if (iSplit == iBestQuad)
9023 newElems.push_back( aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2], newNodes[3] ));
9025 newElems.push_back( aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2] ));
9027 } // end if(!theFace->IsQuadratic())
9029 else { // theFace is quadratic
9030 // we have to split theFace on simple triangles and one simple quadrangle
9032 int nbshift = tmp*2;
9033 // shift nodes in nodes[] by nbshift
9035 for(i=0; i<nbshift; i++) {
9036 const SMDS_MeshNode* n = nodes[0];
9037 for(j=0; j<nbFaceNodes-1; j++) {
9038 nodes[j] = nodes[j+1];
9040 nodes[nbFaceNodes-1] = n;
9042 il1 = il1 - nbshift;
9043 // now have to insert nodes between n0 and n1 or n1 and n2 (see below)
9044 // n0 n1 n2 n0 n1 n2
9045 // +-----+-----+ +-----+-----+
9054 // create new elements
9056 if ( nbFaceNodes == 6 ) { // quadratic triangle
9057 newElems.push_back( aMesh->AddFace( nodes[3], nodes[4], nodes[5] ));
9058 if ( theFace->IsMediumNode(nodes[il1]) ) {
9059 // create quadrangle
9060 newElems.push_back( aMesh->AddFace( nodes[0], nodes[1], nodes[3], nodes[5] ));
9066 // create quadrangle
9067 newElems.push_back( aMesh->AddFace( nodes[1], nodes[2], nodes[3], nodes[5] ));
9073 else { // nbFaceNodes==8 - quadratic quadrangle
9074 newElems.push_back( aMesh->AddFace( nodes[3], nodes[4], nodes[5] ));
9075 newElems.push_back( aMesh->AddFace( nodes[5], nodes[6], nodes[7] ));
9076 newElems.push_back( aMesh->AddFace( nodes[5], nodes[7], nodes[3] ));
9077 if ( theFace->IsMediumNode( nodes[ il1 ])) {
9078 // create quadrangle
9079 newElems.push_back( aMesh->AddFace( nodes[0], nodes[1], nodes[3], nodes[7] ));
9085 // create quadrangle
9086 newElems.push_back( aMesh->AddFace( nodes[1], nodes[2], nodes[3], nodes[7] ));
9092 // create needed triangles using n1,n2,n3 and inserted nodes
9093 int nbn = 2 + aNodesToInsert.size();
9094 vector<const SMDS_MeshNode*> aNodes(nbn);
9095 aNodes[0 ] = nodes[n1];
9096 aNodes[nbn-1] = nodes[n2];
9097 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
9098 for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
9099 aNodes[iNode++] = *nIt;
9101 for ( i = 1; i < nbn; i++ )
9102 newElems.push_back( aMesh->AddFace( aNodes[i-1], aNodes[i], nodes[n3] ));
9105 // remove the old face
9106 for ( size_t i = 0; i < newElems.size(); ++i )
9109 aMesh->SetMeshElementOnShape( newElems[i], theFace->getshapeId() );
9110 myLastCreatedElems.Append( newElems[i] );
9112 ReplaceElemInGroups( theFace, newElems, aMesh );
9113 aMesh->RemoveElement(theFace);
9115 } // InsertNodesIntoLink()
9117 //=======================================================================
9118 //function : UpdateVolumes
9120 //=======================================================================
9122 void SMESH_MeshEditor::UpdateVolumes (const SMDS_MeshNode* theBetweenNode1,
9123 const SMDS_MeshNode* theBetweenNode2,
9124 list<const SMDS_MeshNode*>& theNodesToInsert)
9126 myLastCreatedElems.Clear();
9127 myLastCreatedNodes.Clear();
9129 SMDS_ElemIteratorPtr invElemIt = theBetweenNode1->GetInverseElementIterator(SMDSAbs_Volume);
9130 while (invElemIt->more()) { // loop on inverse elements of theBetweenNode1
9131 const SMDS_MeshElement* elem = invElemIt->next();
9133 // check, if current volume has link theBetweenNode1 - theBetweenNode2
9134 SMDS_VolumeTool aVolume (elem);
9135 if (!aVolume.IsLinked(theBetweenNode1, theBetweenNode2))
9138 // insert new nodes in all faces of the volume, sharing link theBetweenNode1 - theBetweenNode2
9139 int iface, nbFaces = aVolume.NbFaces();
9140 vector<const SMDS_MeshNode *> poly_nodes;
9141 vector<int> quantities (nbFaces);
9143 for (iface = 0; iface < nbFaces; iface++) {
9144 int nbFaceNodes = aVolume.NbFaceNodes(iface), nbInserted = 0;
9145 // faceNodes will contain (nbFaceNodes + 1) nodes, last = first
9146 const SMDS_MeshNode** faceNodes = aVolume.GetFaceNodes(iface);
9148 for (int inode = 0; inode < nbFaceNodes; inode++) {
9149 poly_nodes.push_back(faceNodes[inode]);
9151 if (nbInserted == 0) {
9152 if (faceNodes[inode] == theBetweenNode1) {
9153 if (faceNodes[inode + 1] == theBetweenNode2) {
9154 nbInserted = theNodesToInsert.size();
9156 // add nodes to insert
9157 list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.begin();
9158 for (; nIt != theNodesToInsert.end(); nIt++) {
9159 poly_nodes.push_back(*nIt);
9163 else if (faceNodes[inode] == theBetweenNode2) {
9164 if (faceNodes[inode + 1] == theBetweenNode1) {
9165 nbInserted = theNodesToInsert.size();
9167 // add nodes to insert in reversed order
9168 list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.end();
9170 for (; nIt != theNodesToInsert.begin(); nIt--) {
9171 poly_nodes.push_back(*nIt);
9173 poly_nodes.push_back(*nIt);
9180 quantities[iface] = nbFaceNodes + nbInserted;
9183 // Replace the volume
9184 SMESHDS_Mesh *aMesh = GetMeshDS();
9186 if ( SMDS_MeshElement* newElem = aMesh->AddPolyhedralVolume( poly_nodes, quantities ))
9188 aMesh->SetMeshElementOnShape( newElem, elem->getshapeId() );
9189 myLastCreatedElems.Append( newElem );
9190 ReplaceElemInGroups( elem, newElem, aMesh );
9192 aMesh->RemoveElement( elem );
9198 //================================================================================
9200 * \brief Transform any volume into data of SMDSEntity_Polyhedra
9202 //================================================================================
9204 void volumeToPolyhedron( const SMDS_MeshElement* elem,
9205 vector<const SMDS_MeshNode *> & nodes,
9206 vector<int> & nbNodeInFaces )
9209 nbNodeInFaces.clear();
9210 SMDS_VolumeTool vTool ( elem );
9211 for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
9213 const SMDS_MeshNode** fNodes = vTool.GetFaceNodes( iF );
9214 nodes.insert( nodes.end(), fNodes, fNodes + vTool.NbFaceNodes( iF ));
9215 nbNodeInFaces.push_back( vTool.NbFaceNodes( iF ));
9220 //=======================================================================
9222 * \brief Convert elements contained in a sub-mesh to quadratic
9223 * \return int - nb of checked elements
9225 //=======================================================================
9227 int SMESH_MeshEditor::convertElemToQuadratic(SMESHDS_SubMesh * theSm,
9228 SMESH_MesherHelper& theHelper,
9229 const bool theForce3d)
9231 //MESSAGE("convertElemToQuadratic");
9233 if( !theSm ) return nbElem;
9235 vector<int> nbNodeInFaces;
9236 vector<const SMDS_MeshNode *> nodes;
9237 SMDS_ElemIteratorPtr ElemItr = theSm->GetElements();
9238 while(ElemItr->more())
9241 const SMDS_MeshElement* elem = ElemItr->next();
9242 if( !elem ) continue;
9244 // analyse a necessity of conversion
9245 const SMDSAbs_ElementType aType = elem->GetType();
9246 if ( aType < SMDSAbs_Edge || aType > SMDSAbs_Volume )
9248 const SMDSAbs_EntityType aGeomType = elem->GetEntityType();
9249 bool hasCentralNodes = false;
9250 if ( elem->IsQuadratic() )
9253 switch ( aGeomType ) {
9254 case SMDSEntity_Quad_Triangle:
9255 case SMDSEntity_Quad_Quadrangle:
9256 case SMDSEntity_Quad_Hexa:
9257 case SMDSEntity_Quad_Penta:
9258 alreadyOK = !theHelper.GetIsBiQuadratic(); break;
9260 case SMDSEntity_BiQuad_Triangle:
9261 case SMDSEntity_BiQuad_Quadrangle:
9262 case SMDSEntity_TriQuad_Hexa:
9263 case SMDSEntity_BiQuad_Penta:
9264 alreadyOK = theHelper.GetIsBiQuadratic();
9265 hasCentralNodes = true;
9270 // take into account already present medium nodes
9272 case SMDSAbs_Volume:
9273 theHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( elem )); break;
9275 theHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( elem )); break;
9277 theHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( elem )); break;
9283 // get elem data needed to re-create it
9285 const int id = elem->GetID();
9286 const int nbNodes = elem->NbCornerNodes();
9287 nodes.assign(elem->begin_nodes(), elem->end_nodes());
9288 if ( aGeomType == SMDSEntity_Polyhedra )
9289 nbNodeInFaces = static_cast<const SMDS_VtkVolume* >( elem )->GetQuantities();
9290 else if ( aGeomType == SMDSEntity_Hexagonal_Prism )
9291 volumeToPolyhedron( elem, nodes, nbNodeInFaces );
9293 // remove a linear element
9294 GetMeshDS()->RemoveFreeElement(elem, theSm, /*fromGroups=*/false);
9296 // remove central nodes of biquadratic elements (biquad->quad conversion)
9297 if ( hasCentralNodes )
9298 for ( size_t i = nbNodes * 2; i < nodes.size(); ++i )
9299 if ( nodes[i]->NbInverseElements() == 0 )
9300 GetMeshDS()->RemoveFreeNode( nodes[i], theSm, /*fromGroups=*/true );
9302 const SMDS_MeshElement* NewElem = 0;
9308 NewElem = theHelper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9316 NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
9319 NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9322 NewElem = theHelper.AddPolygonalFace(nodes, id, theForce3d);
9326 case SMDSAbs_Volume :
9330 case SMDSEntity_Tetra:
9331 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9333 case SMDSEntity_Pyramid:
9334 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], id, theForce3d);
9336 case SMDSEntity_Penta:
9337 case SMDSEntity_Quad_Penta:
9338 case SMDSEntity_BiQuad_Penta:
9339 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], nodes[5], id, theForce3d);
9341 case SMDSEntity_Hexa:
9342 case SMDSEntity_Quad_Hexa:
9343 case SMDSEntity_TriQuad_Hexa:
9344 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9345 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9347 case SMDSEntity_Hexagonal_Prism:
9349 NewElem = theHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9356 ReplaceElemInGroups( elem, NewElem, GetMeshDS());
9357 if( NewElem && NewElem->getshapeId() < 1 )
9358 theSm->AddElement( NewElem );
9362 //=======================================================================
9363 //function : ConvertToQuadratic
9365 //=======================================================================
9367 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d, const bool theToBiQuad)
9369 //MESSAGE("ConvertToQuadratic "<< theForce3d << " " << theToBiQuad);
9370 SMESHDS_Mesh* meshDS = GetMeshDS();
9372 SMESH_MesherHelper aHelper(*myMesh);
9374 aHelper.SetIsQuadratic( true );
9375 aHelper.SetIsBiQuadratic( theToBiQuad );
9376 aHelper.SetElementsOnShape(true);
9377 aHelper.ToFixNodeParameters( true );
9379 // convert elements assigned to sub-meshes
9380 int nbCheckedElems = 0;
9381 if ( myMesh->HasShapeToMesh() )
9383 if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9385 SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9386 while ( smIt->more() ) {
9387 SMESH_subMesh* sm = smIt->next();
9388 if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() ) {
9389 aHelper.SetSubShape( sm->GetSubShape() );
9390 nbCheckedElems += convertElemToQuadratic(smDS, aHelper, theForce3d);
9396 // convert elements NOT assigned to sub-meshes
9397 int totalNbElems = meshDS->NbEdges() + meshDS->NbFaces() + meshDS->NbVolumes();
9398 if ( nbCheckedElems < totalNbElems ) // not all elements are in sub-meshes
9400 aHelper.SetElementsOnShape(false);
9401 SMESHDS_SubMesh *smDS = 0;
9404 SMDS_EdgeIteratorPtr aEdgeItr = meshDS->edgesIterator();
9405 while( aEdgeItr->more() )
9407 const SMDS_MeshEdge* edge = aEdgeItr->next();
9408 if ( !edge->IsQuadratic() )
9410 int id = edge->GetID();
9411 const SMDS_MeshNode* n1 = edge->GetNode(0);
9412 const SMDS_MeshNode* n2 = edge->GetNode(1);
9414 meshDS->RemoveFreeElement(edge, smDS, /*fromGroups=*/false);
9416 const SMDS_MeshEdge* NewEdge = aHelper.AddEdge(n1, n2, id, theForce3d);
9417 ReplaceElemInGroups( edge, NewEdge, GetMeshDS());
9421 aHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( edge ));
9426 SMDS_FaceIteratorPtr aFaceItr = meshDS->facesIterator();
9427 while( aFaceItr->more() )
9429 const SMDS_MeshFace* face = aFaceItr->next();
9430 if ( !face ) continue;
9432 const SMDSAbs_EntityType type = face->GetEntityType();
9436 case SMDSEntity_Quad_Triangle:
9437 case SMDSEntity_Quad_Quadrangle:
9438 alreadyOK = !theToBiQuad;
9439 aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
9441 case SMDSEntity_BiQuad_Triangle:
9442 case SMDSEntity_BiQuad_Quadrangle:
9443 alreadyOK = theToBiQuad;
9444 aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
9446 default: alreadyOK = false;
9451 const int id = face->GetID();
9452 vector<const SMDS_MeshNode *> nodes ( face->begin_nodes(), face->end_nodes());
9454 meshDS->RemoveFreeElement(face, smDS, /*fromGroups=*/false);
9456 SMDS_MeshFace * NewFace = 0;
9459 case SMDSEntity_Triangle:
9460 case SMDSEntity_Quad_Triangle:
9461 case SMDSEntity_BiQuad_Triangle:
9462 NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
9463 if ( nodes.size() == 7 && nodes[6]->NbInverseElements() == 0 ) // rm a central node
9464 GetMeshDS()->RemoveFreeNode( nodes[6], /*sm=*/0, /*fromGroups=*/true );
9467 case SMDSEntity_Quadrangle:
9468 case SMDSEntity_Quad_Quadrangle:
9469 case SMDSEntity_BiQuad_Quadrangle:
9470 NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9471 if ( nodes.size() == 9 && nodes[8]->NbInverseElements() == 0 ) // rm a central node
9472 GetMeshDS()->RemoveFreeNode( nodes[8], /*sm=*/0, /*fromGroups=*/true );
9476 NewFace = aHelper.AddPolygonalFace(nodes, id, theForce3d);
9478 ReplaceElemInGroups( face, NewFace, GetMeshDS());
9482 vector<int> nbNodeInFaces;
9483 SMDS_VolumeIteratorPtr aVolumeItr = meshDS->volumesIterator();
9484 while(aVolumeItr->more())
9486 const SMDS_MeshVolume* volume = aVolumeItr->next();
9487 if ( !volume ) continue;
9489 const SMDSAbs_EntityType type = volume->GetEntityType();
9490 if ( volume->IsQuadratic() )
9495 case SMDSEntity_Quad_Hexa: alreadyOK = !theToBiQuad; break;
9496 case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; break;
9497 case SMDSEntity_Quad_Penta: alreadyOK = !theToBiQuad; break;
9498 case SMDSEntity_BiQuad_Penta: alreadyOK = theToBiQuad; break;
9499 default: alreadyOK = true;
9503 aHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( volume ));
9507 const int id = volume->GetID();
9508 vector<const SMDS_MeshNode *> nodes (volume->begin_nodes(), volume->end_nodes());
9509 if ( type == SMDSEntity_Polyhedra )
9510 nbNodeInFaces = static_cast<const SMDS_VtkVolume* >(volume)->GetQuantities();
9511 else if ( type == SMDSEntity_Hexagonal_Prism )
9512 volumeToPolyhedron( volume, nodes, nbNodeInFaces );
9514 meshDS->RemoveFreeElement(volume, smDS, /*fromGroups=*/false);
9516 SMDS_MeshVolume * NewVolume = 0;
9519 case SMDSEntity_Tetra:
9520 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d );
9522 case SMDSEntity_Hexa:
9523 case SMDSEntity_Quad_Hexa:
9524 case SMDSEntity_TriQuad_Hexa:
9525 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9526 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9527 for ( size_t i = 20; i < nodes.size(); ++i ) // rm central nodes
9528 if ( nodes[i]->NbInverseElements() == 0 )
9529 GetMeshDS()->RemoveFreeNode( nodes[i], /*sm=*/0, /*fromGroups=*/true );
9531 case SMDSEntity_Pyramid:
9532 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9533 nodes[3], nodes[4], id, theForce3d);
9535 case SMDSEntity_Penta:
9536 case SMDSEntity_Quad_Penta:
9537 case SMDSEntity_BiQuad_Penta:
9538 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9539 nodes[3], nodes[4], nodes[5], id, theForce3d);
9540 for ( size_t i = 15; i < nodes.size(); ++i ) // rm central nodes
9541 if ( nodes[i]->NbInverseElements() == 0 )
9542 GetMeshDS()->RemoveFreeNode( nodes[i], /*sm=*/0, /*fromGroups=*/true );
9544 case SMDSEntity_Hexagonal_Prism:
9546 NewVolume = aHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9548 ReplaceElemInGroups(volume, NewVolume, meshDS);
9553 { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9554 // aHelper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9555 // aHelper.FixQuadraticElements(myError);
9556 SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
9560 //================================================================================
9562 * \brief Makes given elements quadratic
9563 * \param theForce3d - if true, the medium nodes will be placed in the middle of link
9564 * \param theElements - elements to make quadratic
9566 //================================================================================
9568 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d,
9569 TIDSortedElemSet& theElements,
9570 const bool theToBiQuad)
9572 if ( theElements.empty() ) return;
9574 // we believe that all theElements are of the same type
9575 const SMDSAbs_ElementType elemType = (*theElements.begin())->GetType();
9577 // get all nodes shared by theElements
9578 TIDSortedNodeSet allNodes;
9579 TIDSortedElemSet::iterator eIt = theElements.begin();
9580 for ( ; eIt != theElements.end(); ++eIt )
9581 allNodes.insert( (*eIt)->begin_nodes(), (*eIt)->end_nodes() );
9583 // complete theElements with elements of lower dim whose all nodes are in allNodes
9585 TIDSortedElemSet quadAdjacentElems [ SMDSAbs_NbElementTypes ]; // quadratic adjacent elements
9586 TIDSortedElemSet checkedAdjacentElems [ SMDSAbs_NbElementTypes ];
9587 TIDSortedNodeSet::iterator nIt = allNodes.begin();
9588 for ( ; nIt != allNodes.end(); ++nIt )
9590 const SMDS_MeshNode* n = *nIt;
9591 SMDS_ElemIteratorPtr invIt = n->GetInverseElementIterator();
9592 while ( invIt->more() )
9594 const SMDS_MeshElement* e = invIt->next();
9595 const SMDSAbs_ElementType type = e->GetType();
9596 if ( e->IsQuadratic() )
9598 quadAdjacentElems[ type ].insert( e );
9601 switch ( e->GetEntityType() ) {
9602 case SMDSEntity_Quad_Triangle:
9603 case SMDSEntity_Quad_Quadrangle:
9604 case SMDSEntity_Quad_Hexa: alreadyOK = !theToBiQuad; break;
9605 case SMDSEntity_BiQuad_Triangle:
9606 case SMDSEntity_BiQuad_Quadrangle:
9607 case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; break;
9608 default: alreadyOK = true;
9613 if ( type >= elemType )
9614 continue; // same type or more complex linear element
9616 if ( !checkedAdjacentElems[ type ].insert( e ).second )
9617 continue; // e is already checked
9621 SMDS_NodeIteratorPtr nodeIt = e->nodeIterator();
9622 while ( nodeIt->more() && allIn )
9623 allIn = allNodes.count( nodeIt->next() );
9625 theElements.insert(e );
9629 SMESH_MesherHelper helper(*myMesh);
9630 helper.SetIsQuadratic( true );
9631 helper.SetIsBiQuadratic( theToBiQuad );
9633 // add links of quadratic adjacent elements to the helper
9635 if ( !quadAdjacentElems[SMDSAbs_Edge].empty() )
9636 for ( eIt = quadAdjacentElems[SMDSAbs_Edge].begin();
9637 eIt != quadAdjacentElems[SMDSAbs_Edge].end(); ++eIt )
9639 helper.AddTLinks( static_cast< const SMDS_MeshEdge*> (*eIt) );
9641 if ( !quadAdjacentElems[SMDSAbs_Face].empty() )
9642 for ( eIt = quadAdjacentElems[SMDSAbs_Face].begin();
9643 eIt != quadAdjacentElems[SMDSAbs_Face].end(); ++eIt )
9645 helper.AddTLinks( static_cast< const SMDS_MeshFace*> (*eIt) );
9647 if ( !quadAdjacentElems[SMDSAbs_Volume].empty() )
9648 for ( eIt = quadAdjacentElems[SMDSAbs_Volume].begin();
9649 eIt != quadAdjacentElems[SMDSAbs_Volume].end(); ++eIt )
9651 helper.AddTLinks( static_cast< const SMDS_MeshVolume*> (*eIt) );
9654 // make quadratic (or bi-tri-quadratic) elements instead of linear ones
9656 SMESHDS_Mesh* meshDS = GetMeshDS();
9657 SMESHDS_SubMesh* smDS = 0;
9658 for ( eIt = theElements.begin(); eIt != theElements.end(); ++eIt )
9660 const SMDS_MeshElement* elem = *eIt;
9663 int nbCentralNodes = 0;
9664 switch ( elem->GetEntityType() ) {
9665 // linear convertible
9666 case SMDSEntity_Edge:
9667 case SMDSEntity_Triangle:
9668 case SMDSEntity_Quadrangle:
9669 case SMDSEntity_Tetra:
9670 case SMDSEntity_Pyramid:
9671 case SMDSEntity_Hexa:
9672 case SMDSEntity_Penta: alreadyOK = false; nbCentralNodes = 0; break;
9673 // quadratic that can become bi-quadratic
9674 case SMDSEntity_Quad_Triangle:
9675 case SMDSEntity_Quad_Quadrangle:
9676 case SMDSEntity_Quad_Hexa: alreadyOK =!theToBiQuad; nbCentralNodes = 0; break;
9678 case SMDSEntity_BiQuad_Triangle:
9679 case SMDSEntity_BiQuad_Quadrangle: alreadyOK = theToBiQuad; nbCentralNodes = 1; break;
9680 case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; nbCentralNodes = 7; break;
9682 default: alreadyOK = true;
9684 if ( alreadyOK ) continue;
9686 const SMDSAbs_ElementType type = elem->GetType();
9687 const int id = elem->GetID();
9688 const int nbNodes = elem->NbCornerNodes();
9689 vector<const SMDS_MeshNode *> nodes ( elem->begin_nodes(), elem->end_nodes());
9691 helper.SetSubShape( elem->getshapeId() );
9693 if ( !smDS || !smDS->Contains( elem ))
9694 smDS = meshDS->MeshElements( elem->getshapeId() );
9695 meshDS->RemoveFreeElement(elem, smDS, /*fromGroups=*/false);
9697 SMDS_MeshElement * newElem = 0;
9700 case 4: // cases for most frequently used element types go first (for optimization)
9701 if ( type == SMDSAbs_Volume )
9702 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9704 newElem = helper.AddFace (nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9707 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9708 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9711 newElem = helper.AddFace (nodes[0], nodes[1], nodes[2], id, theForce3d);
9714 newElem = helper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9717 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9718 nodes[4], id, theForce3d);
9721 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9722 nodes[4], nodes[5], id, theForce3d);
9726 ReplaceElemInGroups( elem, newElem, meshDS);
9727 if( newElem && smDS )
9728 smDS->AddElement( newElem );
9730 // remove central nodes
9731 for ( size_t i = nodes.size() - nbCentralNodes; i < nodes.size(); ++i )
9732 if ( nodes[i]->NbInverseElements() == 0 )
9733 meshDS->RemoveFreeNode( nodes[i], smDS, /*fromGroups=*/true );
9735 } // loop on theElements
9738 { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9739 // helper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9740 // helper.FixQuadraticElements( myError );
9741 SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
9745 //=======================================================================
9747 * \brief Convert quadratic elements to linear ones and remove quadratic nodes
9748 * \return int - nb of checked elements
9750 //=======================================================================
9752 int SMESH_MeshEditor::removeQuadElem(SMESHDS_SubMesh * theSm,
9753 SMDS_ElemIteratorPtr theItr,
9754 const int theShapeID)
9757 SMESHDS_Mesh* meshDS = GetMeshDS();
9758 ElemFeatures elemType;
9759 vector<const SMDS_MeshNode *> nodes;
9761 while( theItr->more() )
9763 const SMDS_MeshElement* elem = theItr->next();
9765 if( elem && elem->IsQuadratic())
9768 int nbCornerNodes = elem->NbCornerNodes();
9769 nodes.assign( elem->begin_nodes(), elem->end_nodes() );
9771 elemType.Init( elem, /*basicOnly=*/false ).SetID( elem->GetID() ).SetQuad( false );
9773 //remove a quadratic element
9774 if ( !theSm || !theSm->Contains( elem ))
9775 theSm = meshDS->MeshElements( elem->getshapeId() );
9776 meshDS->RemoveFreeElement( elem, theSm, /*fromGroups=*/false );
9778 // remove medium nodes
9779 for ( size_t i = nbCornerNodes; i < nodes.size(); ++i )
9780 if ( nodes[i]->NbInverseElements() == 0 )
9781 meshDS->RemoveFreeNode( nodes[i], theSm );
9783 // add a linear element
9784 nodes.resize( nbCornerNodes );
9785 SMDS_MeshElement * newElem = AddElement( nodes, elemType );
9786 ReplaceElemInGroups(elem, newElem, meshDS);
9787 if( theSm && newElem )
9788 theSm->AddElement( newElem );
9794 //=======================================================================
9795 //function : ConvertFromQuadratic
9797 //=======================================================================
9799 bool SMESH_MeshEditor::ConvertFromQuadratic()
9801 int nbCheckedElems = 0;
9802 if ( myMesh->HasShapeToMesh() )
9804 if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9806 SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9807 while ( smIt->more() ) {
9808 SMESH_subMesh* sm = smIt->next();
9809 if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() )
9810 nbCheckedElems += removeQuadElem( smDS, smDS->GetElements(), sm->GetId() );
9816 GetMeshDS()->NbEdges() + GetMeshDS()->NbFaces() + GetMeshDS()->NbVolumes();
9817 if ( nbCheckedElems < totalNbElems ) // not all elements are in submeshes
9819 SMESHDS_SubMesh *aSM = 0;
9820 removeQuadElem( aSM, GetMeshDS()->elementsIterator(), 0 );
9828 //================================================================================
9830 * \brief Return true if all medium nodes of the element are in the node set
9832 //================================================================================
9834 bool allMediumNodesIn(const SMDS_MeshElement* elem, TIDSortedNodeSet& nodeSet )
9836 for ( int i = elem->NbCornerNodes(); i < elem->NbNodes(); ++i )
9837 if ( !nodeSet.count( elem->GetNode(i) ))
9843 //================================================================================
9845 * \brief Makes given elements linear
9847 //================================================================================
9849 void SMESH_MeshEditor::ConvertFromQuadratic(TIDSortedElemSet& theElements)
9851 if ( theElements.empty() ) return;
9853 // collect IDs of medium nodes of theElements; some of these nodes will be removed
9854 set<int> mediumNodeIDs;
9855 TIDSortedElemSet::iterator eIt = theElements.begin();
9856 for ( ; eIt != theElements.end(); ++eIt )
9858 const SMDS_MeshElement* e = *eIt;
9859 for ( int i = e->NbCornerNodes(); i < e->NbNodes(); ++i )
9860 mediumNodeIDs.insert( e->GetNode(i)->GetID() );
9863 // replace given elements by linear ones
9864 SMDS_ElemIteratorPtr elemIt = elemSetIterator( theElements );
9865 removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9867 // we need to convert remaining elements whose all medium nodes are in mediumNodeIDs
9868 // except those elements sharing medium nodes of quadratic element whose medium nodes
9869 // are not all in mediumNodeIDs
9871 // get remaining medium nodes
9872 TIDSortedNodeSet mediumNodes;
9873 set<int>::iterator nIdsIt = mediumNodeIDs.begin();
9874 for ( ; nIdsIt != mediumNodeIDs.end(); ++nIdsIt )
9875 if ( const SMDS_MeshNode* n = GetMeshDS()->FindNode( *nIdsIt ))
9876 mediumNodes.insert( mediumNodes.end(), n );
9878 // find more quadratic elements to convert
9879 TIDSortedElemSet moreElemsToConvert;
9880 TIDSortedNodeSet::iterator nIt = mediumNodes.begin();
9881 for ( ; nIt != mediumNodes.end(); ++nIt )
9883 SMDS_ElemIteratorPtr invIt = (*nIt)->GetInverseElementIterator();
9884 while ( invIt->more() )
9886 const SMDS_MeshElement* e = invIt->next();
9887 if ( e->IsQuadratic() && allMediumNodesIn( e, mediumNodes ))
9889 // find a more complex element including e and
9890 // whose medium nodes are not in mediumNodes
9891 bool complexFound = false;
9892 for ( int type = e->GetType() + 1; type < SMDSAbs_0DElement; ++type )
9894 SMDS_ElemIteratorPtr invIt2 =
9895 (*nIt)->GetInverseElementIterator( SMDSAbs_ElementType( type ));
9896 while ( invIt2->more() )
9898 const SMDS_MeshElement* eComplex = invIt2->next();
9899 if ( eComplex->IsQuadratic() && !allMediumNodesIn( eComplex, mediumNodes))
9901 int nbCommonNodes = SMESH_MeshAlgos::GetCommonNodes( e, eComplex ).size();
9902 if ( nbCommonNodes == e->NbNodes())
9904 complexFound = true;
9905 type = SMDSAbs_NbElementTypes; // to quit from the outer loop
9911 if ( !complexFound )
9912 moreElemsToConvert.insert( e );
9916 elemIt = elemSetIterator( moreElemsToConvert );
9917 removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9920 //=======================================================================
9921 //function : SewSideElements
9923 //=======================================================================
9925 SMESH_MeshEditor::Sew_Error
9926 SMESH_MeshEditor::SewSideElements (TIDSortedElemSet& theSide1,
9927 TIDSortedElemSet& theSide2,
9928 const SMDS_MeshNode* theFirstNode1,
9929 const SMDS_MeshNode* theFirstNode2,
9930 const SMDS_MeshNode* theSecondNode1,
9931 const SMDS_MeshNode* theSecondNode2)
9933 myLastCreatedElems.Clear();
9934 myLastCreatedNodes.Clear();
9936 if ( theSide1.size() != theSide2.size() )
9937 return SEW_DIFF_NB_OF_ELEMENTS;
9939 Sew_Error aResult = SEW_OK;
9941 // 1. Build set of faces representing each side
9942 // 2. Find which nodes of the side 1 to merge with ones on the side 2
9943 // 3. Replace nodes in elements of the side 1 and remove replaced nodes
9945 // =======================================================================
9946 // 1. Build set of faces representing each side:
9947 // =======================================================================
9948 // a. build set of nodes belonging to faces
9949 // b. complete set of faces: find missing faces whose nodes are in set of nodes
9950 // c. create temporary faces representing side of volumes if correspondent
9951 // face does not exist
9953 SMESHDS_Mesh* aMesh = GetMeshDS();
9954 // TODO algorithm not OK with vtkUnstructuredGrid: 2 meshes can't share nodes
9955 //SMDS_Mesh aTmpFacesMesh; // try to use the same mesh
9956 TIDSortedElemSet faceSet1, faceSet2;
9957 set<const SMDS_MeshElement*> volSet1, volSet2;
9958 set<const SMDS_MeshNode*> nodeSet1, nodeSet2;
9959 TIDSortedElemSet * faceSetPtr[] = { &faceSet1, &faceSet2 };
9960 set<const SMDS_MeshElement*> * volSetPtr[] = { &volSet1, &volSet2 };
9961 set<const SMDS_MeshNode*> * nodeSetPtr[] = { &nodeSet1, &nodeSet2 };
9962 TIDSortedElemSet * elemSetPtr[] = { &theSide1, &theSide2 };
9963 int iSide, iFace, iNode;
9965 list<const SMDS_MeshElement* > tempFaceList;
9966 for ( iSide = 0; iSide < 2; iSide++ ) {
9967 set<const SMDS_MeshNode*> * nodeSet = nodeSetPtr[ iSide ];
9968 TIDSortedElemSet * elemSet = elemSetPtr[ iSide ];
9969 TIDSortedElemSet * faceSet = faceSetPtr[ iSide ];
9970 set<const SMDS_MeshElement*> * volSet = volSetPtr [ iSide ];
9971 set<const SMDS_MeshElement*>::iterator vIt;
9972 TIDSortedElemSet::iterator eIt;
9973 set<const SMDS_MeshNode*>::iterator nIt;
9975 // check that given nodes belong to given elements
9976 const SMDS_MeshNode* n1 = ( iSide == 0 ) ? theFirstNode1 : theFirstNode2;
9977 const SMDS_MeshNode* n2 = ( iSide == 0 ) ? theSecondNode1 : theSecondNode2;
9978 int firstIndex = -1, secondIndex = -1;
9979 for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9980 const SMDS_MeshElement* elem = *eIt;
9981 if ( firstIndex < 0 ) firstIndex = elem->GetNodeIndex( n1 );
9982 if ( secondIndex < 0 ) secondIndex = elem->GetNodeIndex( n2 );
9983 if ( firstIndex > -1 && secondIndex > -1 ) break;
9985 if ( firstIndex < 0 || secondIndex < 0 ) {
9986 // we can simply return until temporary faces created
9987 return (iSide == 0 ) ? SEW_BAD_SIDE1_NODES : SEW_BAD_SIDE2_NODES;
9990 // -----------------------------------------------------------
9991 // 1a. Collect nodes of existing faces
9992 // and build set of face nodes in order to detect missing
9993 // faces corresponding to sides of volumes
9994 // -----------------------------------------------------------
9996 set< set <const SMDS_MeshNode*> > setOfFaceNodeSet;
9998 // loop on the given element of a side
9999 for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
10000 //const SMDS_MeshElement* elem = *eIt;
10001 const SMDS_MeshElement* elem = *eIt;
10002 if ( elem->GetType() == SMDSAbs_Face ) {
10003 faceSet->insert( elem );
10004 set <const SMDS_MeshNode*> faceNodeSet;
10005 SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
10006 while ( nodeIt->more() ) {
10007 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10008 nodeSet->insert( n );
10009 faceNodeSet.insert( n );
10011 setOfFaceNodeSet.insert( faceNodeSet );
10013 else if ( elem->GetType() == SMDSAbs_Volume )
10014 volSet->insert( elem );
10016 // ------------------------------------------------------------------------------
10017 // 1b. Complete set of faces: find missing faces whose nodes are in set of nodes
10018 // ------------------------------------------------------------------------------
10020 for ( nIt = nodeSet->begin(); nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
10021 SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
10022 while ( fIt->more() ) { // loop on faces sharing a node
10023 const SMDS_MeshElement* f = fIt->next();
10024 if ( faceSet->find( f ) == faceSet->end() ) {
10025 // check if all nodes are in nodeSet and
10026 // complete setOfFaceNodeSet if they are
10027 set <const SMDS_MeshNode*> faceNodeSet;
10028 SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
10029 bool allInSet = true;
10030 while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
10031 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10032 if ( nodeSet->find( n ) == nodeSet->end() )
10035 faceNodeSet.insert( n );
10038 faceSet->insert( f );
10039 setOfFaceNodeSet.insert( faceNodeSet );
10045 // -------------------------------------------------------------------------
10046 // 1c. Create temporary faces representing sides of volumes if correspondent
10047 // face does not exist
10048 // -------------------------------------------------------------------------
10050 if ( !volSet->empty() ) {
10051 //int nodeSetSize = nodeSet->size();
10053 // loop on given volumes
10054 for ( vIt = volSet->begin(); vIt != volSet->end(); vIt++ ) {
10055 SMDS_VolumeTool vol (*vIt);
10056 // loop on volume faces: find free faces
10057 // --------------------------------------
10058 list<const SMDS_MeshElement* > freeFaceList;
10059 for ( iFace = 0; iFace < vol.NbFaces(); iFace++ ) {
10060 if ( !vol.IsFreeFace( iFace ))
10062 // check if there is already a face with same nodes in a face set
10063 const SMDS_MeshElement* aFreeFace = 0;
10064 const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iFace );
10065 int nbNodes = vol.NbFaceNodes( iFace );
10066 set <const SMDS_MeshNode*> faceNodeSet;
10067 vol.GetFaceNodes( iFace, faceNodeSet );
10068 bool isNewFace = setOfFaceNodeSet.insert( faceNodeSet ).second;
10070 // no such a face is given but it still can exist, check it
10071 vector<const SMDS_MeshNode *> nodes ( fNodes, fNodes + nbNodes);
10072 aFreeFace = aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false );
10074 if ( !aFreeFace ) {
10075 // create a temporary face
10076 if ( nbNodes == 3 ) {
10077 //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2] );
10078 aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2] );
10080 else if ( nbNodes == 4 ) {
10081 //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
10082 aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
10085 vector<const SMDS_MeshNode *> poly_nodes ( fNodes, & fNodes[nbNodes]);
10086 //aFreeFace = aTmpFacesMesh.AddPolygonalFace(poly_nodes);
10087 aFreeFace = aMesh->AddPolygonalFace(poly_nodes);
10090 tempFaceList.push_back( aFreeFace );
10094 freeFaceList.push_back( aFreeFace );
10096 } // loop on faces of a volume
10098 // choose one of several free faces of a volume
10099 // --------------------------------------------
10100 if ( freeFaceList.size() > 1 ) {
10101 // choose a face having max nb of nodes shared by other elems of a side
10102 int maxNbNodes = -1;
10103 list<const SMDS_MeshElement* >::iterator fIt = freeFaceList.begin();
10104 while ( fIt != freeFaceList.end() ) { // loop on free faces
10105 int nbSharedNodes = 0;
10106 SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
10107 while ( nodeIt->more() ) { // loop on free face nodes
10108 const SMDS_MeshNode* n =
10109 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10110 SMDS_ElemIteratorPtr invElemIt = n->GetInverseElementIterator();
10111 while ( invElemIt->more() ) {
10112 const SMDS_MeshElement* e = invElemIt->next();
10113 nbSharedNodes += faceSet->count( e );
10114 nbSharedNodes += elemSet->count( e );
10117 if ( nbSharedNodes > maxNbNodes ) {
10118 maxNbNodes = nbSharedNodes;
10119 freeFaceList.erase( freeFaceList.begin(), fIt++ );
10121 else if ( nbSharedNodes == maxNbNodes ) {
10125 freeFaceList.erase( fIt++ ); // here fIt++ occurs before erase
10128 if ( freeFaceList.size() > 1 )
10130 // could not choose one face, use another way
10131 // choose a face most close to the bary center of the opposite side
10132 gp_XYZ aBC( 0., 0., 0. );
10133 set <const SMDS_MeshNode*> addedNodes;
10134 TIDSortedElemSet * elemSet2 = elemSetPtr[ 1 - iSide ];
10135 eIt = elemSet2->begin();
10136 for ( eIt = elemSet2->begin(); eIt != elemSet2->end(); eIt++ ) {
10137 SMDS_ElemIteratorPtr nodeIt = (*eIt)->nodesIterator();
10138 while ( nodeIt->more() ) { // loop on free face nodes
10139 const SMDS_MeshNode* n =
10140 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10141 if ( addedNodes.insert( n ).second )
10142 aBC += gp_XYZ( n->X(),n->Y(),n->Z() );
10145 aBC /= addedNodes.size();
10146 double minDist = DBL_MAX;
10147 fIt = freeFaceList.begin();
10148 while ( fIt != freeFaceList.end() ) { // loop on free faces
10150 SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
10151 while ( nodeIt->more() ) { // loop on free face nodes
10152 const SMDS_MeshNode* n =
10153 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10154 gp_XYZ p( n->X(),n->Y(),n->Z() );
10155 dist += ( aBC - p ).SquareModulus();
10157 if ( dist < minDist ) {
10159 freeFaceList.erase( freeFaceList.begin(), fIt++ );
10162 fIt = freeFaceList.erase( fIt++ );
10165 } // choose one of several free faces of a volume
10167 if ( freeFaceList.size() == 1 ) {
10168 const SMDS_MeshElement* aFreeFace = freeFaceList.front();
10169 faceSet->insert( aFreeFace );
10170 // complete a node set with nodes of a found free face
10171 // for ( iNode = 0; iNode < ; iNode++ )
10172 // nodeSet->insert( fNodes[ iNode ] );
10175 } // loop on volumes of a side
10177 // // complete a set of faces if new nodes in a nodeSet appeared
10178 // // ----------------------------------------------------------
10179 // if ( nodeSetSize != nodeSet->size() ) {
10180 // for ( ; nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
10181 // SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
10182 // while ( fIt->more() ) { // loop on faces sharing a node
10183 // const SMDS_MeshElement* f = fIt->next();
10184 // if ( faceSet->find( f ) == faceSet->end() ) {
10185 // // check if all nodes are in nodeSet and
10186 // // complete setOfFaceNodeSet if they are
10187 // set <const SMDS_MeshNode*> faceNodeSet;
10188 // SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
10189 // bool allInSet = true;
10190 // while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
10191 // const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
10192 // if ( nodeSet->find( n ) == nodeSet->end() )
10193 // allInSet = false;
10195 // faceNodeSet.insert( n );
10197 // if ( allInSet ) {
10198 // faceSet->insert( f );
10199 // setOfFaceNodeSet.insert( faceNodeSet );
10205 } // Create temporary faces, if there are volumes given
10208 if ( faceSet1.size() != faceSet2.size() ) {
10209 // delete temporary faces: they are in reverseElements of actual nodes
10210 // SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
10211 // while ( tmpFaceIt->more() )
10212 // aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
10213 // list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
10214 // for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
10215 // aMesh->RemoveElement(*tmpFaceIt);
10216 MESSAGE("Diff nb of faces");
10217 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10220 // ============================================================
10221 // 2. Find nodes to merge:
10222 // bind a node to remove to a node to put instead
10223 // ============================================================
10225 TNodeNodeMap nReplaceMap; // bind a node to remove to a node to put instead
10226 if ( theFirstNode1 != theFirstNode2 )
10227 nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
10228 if ( theSecondNode1 != theSecondNode2 )
10229 nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
10231 LinkID_Gen aLinkID_Gen( GetMeshDS() );
10232 set< long > linkIdSet; // links to process
10233 linkIdSet.insert( aLinkID_Gen.GetLinkID( theFirstNode1, theSecondNode1 ));
10235 typedef pair< const SMDS_MeshNode*, const SMDS_MeshNode* > NLink;
10236 list< NLink > linkList[2];
10237 linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
10238 linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
10239 // loop on links in linkList; find faces by links and append links
10240 // of the found faces to linkList
10241 list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
10242 for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ )
10244 NLink link[] = { *linkIt[0], *linkIt[1] };
10245 long linkID = aLinkID_Gen.GetLinkID( link[0].first, link[0].second );
10246 if ( !linkIdSet.count( linkID ) )
10249 // by links, find faces in the face sets,
10250 // and find indices of link nodes in the found faces;
10251 // in a face set, there is only one or no face sharing a link
10252 // ---------------------------------------------------------------
10254 const SMDS_MeshElement* face[] = { 0, 0 };
10255 vector<const SMDS_MeshNode*> fnodes[2];
10256 int iLinkNode[2][2];
10257 TIDSortedElemSet avoidSet;
10258 for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
10259 const SMDS_MeshNode* n1 = link[iSide].first;
10260 const SMDS_MeshNode* n2 = link[iSide].second;
10261 //cout << "Side " << iSide << " ";
10262 //cout << "L( " << n1->GetID() << ", " << n2->GetID() << " ) " << endl;
10263 // find a face by two link nodes
10264 face[ iSide ] = SMESH_MeshAlgos::FindFaceInSet( n1, n2,
10265 *faceSetPtr[ iSide ], avoidSet,
10266 &iLinkNode[iSide][0],
10267 &iLinkNode[iSide][1] );
10268 if ( face[ iSide ])
10270 //cout << " F " << face[ iSide]->GetID() <<endl;
10271 faceSetPtr[ iSide ]->erase( face[ iSide ]);
10272 // put face nodes to fnodes
10273 if ( face[ iSide ]->IsQuadratic() )
10275 // use interlaced nodes iterator
10276 const SMDS_VtkFace* F = dynamic_cast<const SMDS_VtkFace*>( face[ iSide ]);
10277 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
10278 SMDS_ElemIteratorPtr nIter = F->interlacedNodesElemIterator();
10279 while ( nIter->more() )
10280 fnodes[ iSide ].push_back( cast2Node( nIter->next() ));
10284 fnodes[ iSide ].assign( face[ iSide ]->begin_nodes(),
10285 face[ iSide ]->end_nodes() );
10287 fnodes[ iSide ].push_back( fnodes[ iSide ].front());
10291 // check similarity of elements of the sides
10292 if (aResult == SEW_OK && (( face[0] && !face[1] ) || ( !face[0] && face[1] ))) {
10293 MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
10294 if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
10295 aResult = ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10298 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10300 break; // do not return because it's necessary to remove tmp faces
10303 // set nodes to merge
10304 // -------------------
10306 if ( face[0] && face[1] ) {
10307 const int nbNodes = face[0]->NbNodes();
10308 if ( nbNodes != face[1]->NbNodes() ) {
10309 MESSAGE("Diff nb of face nodes");
10310 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10311 break; // do not return because it s necessary to remove tmp faces
10313 bool reverse[] = { false, false }; // order of nodes in the link
10314 for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
10315 // analyse link orientation in faces
10316 int i1 = iLinkNode[ iSide ][ 0 ];
10317 int i2 = iLinkNode[ iSide ][ 1 ];
10318 reverse[ iSide ] = Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1;
10320 int di1 = reverse[0] ? -1 : +1, i1 = iLinkNode[0][1] + di1;
10321 int di2 = reverse[1] ? -1 : +1, i2 = iLinkNode[1][1] + di2;
10322 for ( int i = nbNodes - 2; i > 0; --i, i1 += di1, i2 += di2 )
10324 nReplaceMap.insert ( make_pair ( fnodes[0][ ( i1 + nbNodes ) % nbNodes ],
10325 fnodes[1][ ( i2 + nbNodes ) % nbNodes ]));
10328 // add other links of the faces to linkList
10329 // -----------------------------------------
10331 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
10332 linkID = aLinkID_Gen.GetLinkID( fnodes[0][iNode], fnodes[0][iNode+1] );
10333 pair< set<long>::iterator, bool > iter_isnew = linkIdSet.insert( linkID );
10334 if ( !iter_isnew.second ) { // already in a set: no need to process
10335 linkIdSet.erase( iter_isnew.first );
10337 else // new in set == encountered for the first time: add
10339 const SMDS_MeshNode* n1 = fnodes[0][ iNode ];
10340 const SMDS_MeshNode* n2 = fnodes[0][ iNode + 1];
10341 linkList[0].push_back ( NLink( n1, n2 ));
10342 linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10347 if ( faceSetPtr[0]->empty() || faceSetPtr[1]->empty() )
10350 } // loop on link lists
10352 if ( aResult == SEW_OK &&
10353 ( //linkIt[0] != linkList[0].end() ||
10354 !faceSetPtr[0]->empty() || !faceSetPtr[1]->empty() )) {
10355 MESSAGE( (linkIt[0] != linkList[0].end()) <<" "<< (faceSetPtr[0]->empty()) <<
10356 " " << (faceSetPtr[1]->empty()));
10357 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10360 // ====================================================================
10361 // 3. Replace nodes in elements of the side 1 and remove replaced nodes
10362 // ====================================================================
10364 // delete temporary faces
10365 // SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
10366 // while ( tmpFaceIt->more() )
10367 // aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
10368 list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
10369 for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
10370 aMesh->RemoveElement(*tmpFaceIt);
10372 if ( aResult != SEW_OK)
10375 list< int > nodeIDsToRemove;
10376 vector< const SMDS_MeshNode*> nodes;
10377 ElemFeatures elemType;
10379 // loop on nodes replacement map
10380 TNodeNodeMap::iterator nReplaceMapIt = nReplaceMap.begin(), nnIt;
10381 for ( ; nReplaceMapIt != nReplaceMap.end(); nReplaceMapIt++ )
10382 if ( (*nReplaceMapIt).first != (*nReplaceMapIt).second )
10384 const SMDS_MeshNode* nToRemove = (*nReplaceMapIt).first;
10385 nodeIDsToRemove.push_back( nToRemove->GetID() );
10386 // loop on elements sharing nToRemove
10387 SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
10388 while ( invElemIt->more() ) {
10389 const SMDS_MeshElement* e = invElemIt->next();
10390 // get a new suite of nodes: make replacement
10391 int nbReplaced = 0, i = 0, nbNodes = e->NbNodes();
10392 nodes.resize( nbNodes );
10393 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
10394 while ( nIt->more() ) {
10395 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nIt->next() );
10396 nnIt = nReplaceMap.find( n );
10397 if ( nnIt != nReplaceMap.end() ) {
10399 n = (*nnIt).second;
10403 // if ( nbReplaced == nbNodes && e->GetType() == SMDSAbs_Face )
10404 // elemIDsToRemove.push_back( e->GetID() );
10408 elemType.Init( e, /*basicOnly=*/false ).SetID( e->GetID() );
10409 aMesh->RemoveElement( e );
10411 if ( SMDS_MeshElement* newElem = this->AddElement( nodes, elemType ))
10413 AddToSameGroups( newElem, e, aMesh );
10414 if ( int aShapeId = e->getshapeId() )
10415 aMesh->SetMeshElementOnShape( newElem, aShapeId );
10421 Remove( nodeIDsToRemove, true );
10426 //================================================================================
10428 * \brief Find corresponding nodes in two sets of faces
10429 * \param theSide1 - first face set
10430 * \param theSide2 - second first face
10431 * \param theFirstNode1 - a boundary node of set 1
10432 * \param theFirstNode2 - a node of set 2 corresponding to theFirstNode1
10433 * \param theSecondNode1 - a boundary node of set 1 linked with theFirstNode1
10434 * \param theSecondNode2 - a node of set 2 corresponding to theSecondNode1
10435 * \param nReplaceMap - output map of corresponding nodes
10436 * \return bool - is a success or not
10438 //================================================================================
10441 //#define DEBUG_MATCHING_NODES
10444 SMESH_MeshEditor::Sew_Error
10445 SMESH_MeshEditor::FindMatchingNodes(set<const SMDS_MeshElement*>& theSide1,
10446 set<const SMDS_MeshElement*>& theSide2,
10447 const SMDS_MeshNode* theFirstNode1,
10448 const SMDS_MeshNode* theFirstNode2,
10449 const SMDS_MeshNode* theSecondNode1,
10450 const SMDS_MeshNode* theSecondNode2,
10451 TNodeNodeMap & nReplaceMap)
10453 set<const SMDS_MeshElement*> * faceSetPtr[] = { &theSide1, &theSide2 };
10455 nReplaceMap.clear();
10456 if ( theFirstNode1 != theFirstNode2 )
10457 nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
10458 if ( theSecondNode1 != theSecondNode2 )
10459 nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
10461 set< SMESH_TLink > linkSet; // set of nodes where order of nodes is ignored
10462 linkSet.insert( SMESH_TLink( theFirstNode1, theSecondNode1 ));
10464 list< NLink > linkList[2];
10465 linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
10466 linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
10468 // loop on links in linkList; find faces by links and append links
10469 // of the found faces to linkList
10470 list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
10471 for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ ) {
10472 NLink link[] = { *linkIt[0], *linkIt[1] };
10473 if ( linkSet.find( link[0] ) == linkSet.end() )
10476 // by links, find faces in the face sets,
10477 // and find indices of link nodes in the found faces;
10478 // in a face set, there is only one or no face sharing a link
10479 // ---------------------------------------------------------------
10481 const SMDS_MeshElement* face[] = { 0, 0 };
10482 list<const SMDS_MeshNode*> notLinkNodes[2];
10483 //bool reverse[] = { false, false }; // order of notLinkNodes
10485 for ( int iSide = 0; iSide < 2; iSide++ ) // loop on 2 sides
10487 const SMDS_MeshNode* n1 = link[iSide].first;
10488 const SMDS_MeshNode* n2 = link[iSide].second;
10489 set<const SMDS_MeshElement*> * faceSet = faceSetPtr[ iSide ];
10490 set< const SMDS_MeshElement* > facesOfNode1;
10491 for ( int iNode = 0; iNode < 2; iNode++ ) // loop on 2 nodes of a link
10493 // during a loop of the first node, we find all faces around n1,
10494 // during a loop of the second node, we find one face sharing both n1 and n2
10495 const SMDS_MeshNode* n = iNode ? n1 : n2; // a node of a link
10496 SMDS_ElemIteratorPtr fIt = n->GetInverseElementIterator(SMDSAbs_Face);
10497 while ( fIt->more() ) { // loop on faces sharing a node
10498 const SMDS_MeshElement* f = fIt->next();
10499 if (faceSet->find( f ) != faceSet->end() && // f is in face set
10500 ! facesOfNode1.insert( f ).second ) // f encounters twice
10502 if ( face[ iSide ] ) {
10503 MESSAGE( "2 faces per link " );
10504 return ( iSide ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10507 faceSet->erase( f );
10509 // get not link nodes
10510 int nbN = f->NbNodes();
10511 if ( f->IsQuadratic() )
10513 nbNodes[ iSide ] = nbN;
10514 list< const SMDS_MeshNode* > & nodes = notLinkNodes[ iSide ];
10515 int i1 = f->GetNodeIndex( n1 );
10516 int i2 = f->GetNodeIndex( n2 );
10517 int iEnd = nbN, iBeg = -1, iDelta = 1;
10518 bool reverse = ( Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1 );
10520 std::swap( iEnd, iBeg ); iDelta = -1;
10525 if ( i == iEnd ) i = iBeg + iDelta;
10526 if ( i == i1 ) break;
10527 nodes.push_back ( f->GetNode( i ) );
10533 // check similarity of elements of the sides
10534 if (( face[0] && !face[1] ) || ( !face[0] && face[1] )) {
10535 MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
10536 if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
10537 return ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10540 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10544 // set nodes to merge
10545 // -------------------
10547 if ( face[0] && face[1] ) {
10548 if ( nbNodes[0] != nbNodes[1] ) {
10549 MESSAGE("Diff nb of face nodes");
10550 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10552 #ifdef DEBUG_MATCHING_NODES
10553 MESSAGE ( " Link 1: " << link[0].first->GetID() <<" "<< link[0].second->GetID()
10554 << " F 1: " << face[0] << "| Link 2: " << link[1].first->GetID() <<" "
10555 << link[1].second->GetID() << " F 2: " << face[1] << " | Bind: " ) ;
10557 int nbN = nbNodes[0];
10559 list<const SMDS_MeshNode*>::iterator n1 = notLinkNodes[0].begin();
10560 list<const SMDS_MeshNode*>::iterator n2 = notLinkNodes[1].begin();
10561 for ( int i = 0 ; i < nbN - 2; ++i ) {
10562 #ifdef DEBUG_MATCHING_NODES
10563 MESSAGE ( (*n1)->GetID() << " to " << (*n2)->GetID() );
10565 nReplaceMap.insert( make_pair( *(n1++), *(n2++) ));
10569 // add other links of the face 1 to linkList
10570 // -----------------------------------------
10572 const SMDS_MeshElement* f0 = face[0];
10573 const SMDS_MeshNode* n1 = f0->GetNode( nbN - 1 );
10574 for ( int i = 0; i < nbN; i++ )
10576 const SMDS_MeshNode* n2 = f0->GetNode( i );
10577 pair< set< SMESH_TLink >::iterator, bool > iter_isnew =
10578 linkSet.insert( SMESH_TLink( n1, n2 ));
10579 if ( !iter_isnew.second ) { // already in a set: no need to process
10580 linkSet.erase( iter_isnew.first );
10582 else // new in set == encountered for the first time: add
10584 #ifdef DEBUG_MATCHING_NODES
10585 MESSAGE ( "Add link 1: " << n1->GetID() << " " << n2->GetID() << " "
10586 << " | link 2: " << nReplaceMap[n1]->GetID() << " " << nReplaceMap[n2]->GetID() << " " );
10588 linkList[0].push_back ( NLink( n1, n2 ));
10589 linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10594 } // loop on link lists
10599 //================================================================================
10601 * \brief Create elements equal (on same nodes) to given ones
10602 * \param [in] theElements - a set of elems to duplicate. If it is empty, all
10603 * elements of the uppest dimension are duplicated.
10605 //================================================================================
10607 void SMESH_MeshEditor::DoubleElements( const TIDSortedElemSet& theElements )
10609 ClearLastCreated();
10610 SMESHDS_Mesh* mesh = GetMeshDS();
10612 // get an element type and an iterator over elements
10614 SMDSAbs_ElementType type = SMDSAbs_All;
10615 SMDS_ElemIteratorPtr elemIt;
10616 vector< const SMDS_MeshElement* > allElems;
10617 if ( theElements.empty() )
10619 if ( mesh->NbNodes() == 0 )
10621 // get most complex type
10622 SMDSAbs_ElementType types[SMDSAbs_NbElementTypes] = {
10623 SMDSAbs_Volume, SMDSAbs_Face, SMDSAbs_Edge,
10624 SMDSAbs_0DElement, SMDSAbs_Ball, SMDSAbs_Node
10626 for ( int i = 0; i < SMDSAbs_NbElementTypes; ++i )
10627 if ( mesh->GetMeshInfo().NbElements( types[i] ))
10632 // put all elements in the vector <allElems>
10633 allElems.reserve( mesh->GetMeshInfo().NbElements( type ));
10634 elemIt = mesh->elementsIterator( type );
10635 while ( elemIt->more() )
10636 allElems.push_back( elemIt->next());
10637 elemIt = elemSetIterator( allElems );
10641 type = (*theElements.begin())->GetType();
10642 elemIt = elemSetIterator( theElements );
10645 // duplicate elements
10647 ElemFeatures elemType;
10649 vector< const SMDS_MeshNode* > nodes;
10650 while ( elemIt->more() )
10652 const SMDS_MeshElement* elem = elemIt->next();
10653 if ( elem->GetType() != type )
10656 elemType.Init( elem, /*basicOnly=*/false );
10657 nodes.assign( elem->begin_nodes(), elem->end_nodes() );
10659 AddElement( nodes, elemType );
10663 //================================================================================
10665 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10666 \param theElems - the list of elements (edges or faces) to be replicated
10667 The nodes for duplication could be found from these elements
10668 \param theNodesNot - list of nodes to NOT replicate
10669 \param theAffectedElems - the list of elements (cells and edges) to which the
10670 replicated nodes should be associated to.
10671 \return TRUE if operation has been completed successfully, FALSE otherwise
10673 //================================================================================
10675 bool SMESH_MeshEditor::DoubleNodes( const TIDSortedElemSet& theElems,
10676 const TIDSortedElemSet& theNodesNot,
10677 const TIDSortedElemSet& theAffectedElems )
10679 myLastCreatedElems.Clear();
10680 myLastCreatedNodes.Clear();
10682 if ( theElems.size() == 0 )
10685 SMESHDS_Mesh* aMeshDS = GetMeshDS();
10690 TNodeNodeMap anOldNodeToNewNode;
10691 // duplicate elements and nodes
10692 res = doubleNodes( aMeshDS, theElems, theNodesNot, anOldNodeToNewNode, true );
10693 // replce nodes by duplications
10694 res = doubleNodes( aMeshDS, theAffectedElems, theNodesNot, anOldNodeToNewNode, false );
10698 //================================================================================
10700 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10701 \param theMeshDS - mesh instance
10702 \param theElems - the elements replicated or modified (nodes should be changed)
10703 \param theNodesNot - nodes to NOT replicate
10704 \param theNodeNodeMap - relation of old node to new created node
10705 \param theIsDoubleElem - flag os to replicate element or modify
10706 \return TRUE if operation has been completed successfully, FALSE otherwise
10708 //================================================================================
10710 bool SMESH_MeshEditor::doubleNodes(SMESHDS_Mesh* theMeshDS,
10711 const TIDSortedElemSet& theElems,
10712 const TIDSortedElemSet& theNodesNot,
10713 TNodeNodeMap& theNodeNodeMap,
10714 const bool theIsDoubleElem )
10716 // iterate through element and duplicate them (by nodes duplication)
10718 std::vector<const SMDS_MeshNode*> newNodes;
10719 ElemFeatures elemType;
10721 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10722 for ( ; elemItr != theElems.end(); ++elemItr )
10724 const SMDS_MeshElement* anElem = *elemItr;
10728 // duplicate nodes to duplicate element
10729 bool isDuplicate = false;
10730 newNodes.resize( anElem->NbNodes() );
10731 SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
10733 while ( anIter->more() )
10735 const SMDS_MeshNode* aCurrNode = static_cast<const SMDS_MeshNode*>( anIter->next() );
10736 const SMDS_MeshNode* aNewNode = aCurrNode;
10737 TNodeNodeMap::iterator n2n = theNodeNodeMap.find( aCurrNode );
10738 if ( n2n != theNodeNodeMap.end() )
10740 aNewNode = n2n->second;
10742 else if ( theIsDoubleElem && !theNodesNot.count( aCurrNode ))
10745 aNewNode = theMeshDS->AddNode( aCurrNode->X(), aCurrNode->Y(), aCurrNode->Z() );
10746 copyPosition( aCurrNode, aNewNode );
10747 theNodeNodeMap[ aCurrNode ] = aNewNode;
10748 myLastCreatedNodes.Append( aNewNode );
10750 isDuplicate |= (aCurrNode != aNewNode);
10751 newNodes[ ind++ ] = aNewNode;
10753 if ( !isDuplicate )
10756 if ( theIsDoubleElem )
10757 AddElement( newNodes, elemType.Init( anElem, /*basicOnly=*/false ));
10759 theMeshDS->ChangeElementNodes( anElem, &newNodes[ 0 ], newNodes.size() );
10766 //================================================================================
10768 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10769 \param theNodes - identifiers of nodes to be doubled
10770 \param theModifiedElems - identifiers of elements to be updated by the new (doubled)
10771 nodes. If list of element identifiers is empty then nodes are doubled but
10772 they not assigned to elements
10773 \return TRUE if operation has been completed successfully, FALSE otherwise
10775 //================================================================================
10777 bool SMESH_MeshEditor::DoubleNodes( const std::list< int >& theListOfNodes,
10778 const std::list< int >& theListOfModifiedElems )
10780 myLastCreatedElems.Clear();
10781 myLastCreatedNodes.Clear();
10783 if ( theListOfNodes.size() == 0 )
10786 SMESHDS_Mesh* aMeshDS = GetMeshDS();
10790 // iterate through nodes and duplicate them
10792 std::map< const SMDS_MeshNode*, const SMDS_MeshNode* > anOldNodeToNewNode;
10794 std::list< int >::const_iterator aNodeIter;
10795 for ( aNodeIter = theListOfNodes.begin(); aNodeIter != theListOfNodes.end(); ++aNodeIter )
10797 int aCurr = *aNodeIter;
10798 SMDS_MeshNode* aNode = (SMDS_MeshNode*)aMeshDS->FindNode( aCurr );
10804 const SMDS_MeshNode* aNewNode = aMeshDS->AddNode( aNode->X(), aNode->Y(), aNode->Z() );
10807 copyPosition( aNode, aNewNode );
10808 anOldNodeToNewNode[ aNode ] = aNewNode;
10809 myLastCreatedNodes.Append( aNewNode );
10813 // Create map of new nodes for modified elements
10815 std::map< SMDS_MeshElement*, vector<const SMDS_MeshNode*> > anElemToNodes;
10817 std::list< int >::const_iterator anElemIter;
10818 for ( anElemIter = theListOfModifiedElems.begin();
10819 anElemIter != theListOfModifiedElems.end(); ++anElemIter )
10821 int aCurr = *anElemIter;
10822 SMDS_MeshElement* anElem = (SMDS_MeshElement*)aMeshDS->FindElement( aCurr );
10826 vector<const SMDS_MeshNode*> aNodeArr( anElem->NbNodes() );
10828 SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
10830 while ( anIter->more() )
10832 SMDS_MeshNode* aCurrNode = (SMDS_MeshNode*)anIter->next();
10833 if ( aCurr && anOldNodeToNewNode.find( aCurrNode ) != anOldNodeToNewNode.end() )
10835 const SMDS_MeshNode* aNewNode = anOldNodeToNewNode[ aCurrNode ];
10836 aNodeArr[ ind++ ] = aNewNode;
10839 aNodeArr[ ind++ ] = aCurrNode;
10841 anElemToNodes[ anElem ] = aNodeArr;
10844 // Change nodes of elements
10846 std::map< SMDS_MeshElement*, vector<const SMDS_MeshNode*> >::iterator
10847 anElemToNodesIter = anElemToNodes.begin();
10848 for ( ; anElemToNodesIter != anElemToNodes.end(); ++anElemToNodesIter )
10850 const SMDS_MeshElement* anElem = anElemToNodesIter->first;
10851 vector<const SMDS_MeshNode*> aNodeArr = anElemToNodesIter->second;
10854 aMeshDS->ChangeElementNodes( anElem, &aNodeArr[ 0 ], anElem->NbNodes() );
10863 //================================================================================
10865 \brief Check if element located inside shape
10866 \return TRUE if IN or ON shape, FALSE otherwise
10868 //================================================================================
10870 template<class Classifier>
10871 bool isInside(const SMDS_MeshElement* theElem,
10872 Classifier& theClassifier,
10873 const double theTol)
10875 gp_XYZ centerXYZ (0, 0, 0);
10876 SMDS_ElemIteratorPtr aNodeItr = theElem->nodesIterator();
10877 while (aNodeItr->more())
10878 centerXYZ += SMESH_TNodeXYZ(cast2Node( aNodeItr->next()));
10880 gp_Pnt aPnt = centerXYZ / theElem->NbNodes();
10881 theClassifier.Perform(aPnt, theTol);
10882 TopAbs_State aState = theClassifier.State();
10883 return (aState == TopAbs_IN || aState == TopAbs_ON );
10886 //================================================================================
10888 * \brief Classifier of the 3D point on the TopoDS_Face
10889 * with interaface suitable for isInside()
10891 //================================================================================
10893 struct _FaceClassifier
10895 Extrema_ExtPS _extremum;
10896 BRepAdaptor_Surface _surface;
10897 TopAbs_State _state;
10899 _FaceClassifier(const TopoDS_Face& face):_extremum(),_surface(face),_state(TopAbs_OUT)
10901 _extremum.Initialize( _surface,
10902 _surface.FirstUParameter(), _surface.LastUParameter(),
10903 _surface.FirstVParameter(), _surface.LastVParameter(),
10904 _surface.Tolerance(), _surface.Tolerance() );
10906 void Perform(const gp_Pnt& aPnt, double theTol)
10909 _state = TopAbs_OUT;
10910 _extremum.Perform(aPnt);
10911 if ( _extremum.IsDone() )
10912 for ( int iSol = 1; iSol <= _extremum.NbExt() && _state == TopAbs_OUT; ++iSol)
10913 _state = ( _extremum.SquareDistance(iSol) <= theTol ? TopAbs_IN : TopAbs_OUT );
10915 TopAbs_State State() const
10922 //================================================================================
10924 \brief Identify the elements that will be affected by node duplication (actual duplication is not performed).
10925 This method is the first step of DoubleNodeElemGroupsInRegion.
10926 \param theElems - list of groups of elements (edges or faces) to be replicated
10927 \param theNodesNot - list of groups of nodes not to replicated
10928 \param theShape - shape to detect affected elements (element which geometric center
10929 located on or inside shape). If the shape is null, detection is done on faces orientations
10930 (select elements with a gravity center on the side given by faces normals).
10931 This mode (null shape) is faster, but works only when theElems are faces, with coherents orientations.
10932 The replicated nodes should be associated to affected elements.
10933 \return groups of affected elements
10934 \sa DoubleNodeElemGroupsInRegion()
10936 //================================================================================
10938 bool SMESH_MeshEditor::AffectedElemGroupsInRegion( const TIDSortedElemSet& theElems,
10939 const TIDSortedElemSet& theNodesNot,
10940 const TopoDS_Shape& theShape,
10941 TIDSortedElemSet& theAffectedElems)
10943 if ( theShape.IsNull() )
10945 std::set<const SMDS_MeshNode*> alreadyCheckedNodes;
10946 std::set<const SMDS_MeshElement*> alreadyCheckedElems;
10947 std::set<const SMDS_MeshElement*> edgesToCheck;
10948 alreadyCheckedNodes.clear();
10949 alreadyCheckedElems.clear();
10950 edgesToCheck.clear();
10952 // --- iterates on elements to be replicated and get elements by back references from their nodes
10954 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10955 for ( ; elemItr != theElems.end(); ++elemItr )
10957 SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
10958 if (!anElem || (anElem->GetType() != SMDSAbs_Face))
10961 SMESH_MeshAlgos::FaceNormal( anElem, normal, /*normalized=*/true );
10962 std::set<const SMDS_MeshNode*> nodesElem;
10964 SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
10965 while ( nodeItr->more() )
10967 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10968 nodesElem.insert(aNode);
10970 std::set<const SMDS_MeshNode*>::iterator nodit = nodesElem.begin();
10971 for (; nodit != nodesElem.end(); nodit++)
10973 const SMDS_MeshNode* aNode = *nodit;
10974 if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
10976 if (alreadyCheckedNodes.find(aNode) != alreadyCheckedNodes.end())
10978 alreadyCheckedNodes.insert(aNode);
10979 SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
10980 while ( backElemItr->more() )
10982 const SMDS_MeshElement* curElem = backElemItr->next();
10983 if (alreadyCheckedElems.find(curElem) != alreadyCheckedElems.end())
10985 if (theElems.find(curElem) != theElems.end())
10987 alreadyCheckedElems.insert(curElem);
10988 double x=0, y=0, z=0;
10990 SMDS_ElemIteratorPtr nodeItr2 = curElem->nodesIterator();
10991 while ( nodeItr2->more() )
10993 const SMDS_MeshNode* anotherNode = cast2Node(nodeItr2->next());
10994 x += anotherNode->X();
10995 y += anotherNode->Y();
10996 z += anotherNode->Z();
11000 p.SetCoord( x/nb -aNode->X(),
11002 z/nb -aNode->Z() );
11005 theAffectedElems.insert( curElem );
11007 else if (curElem->GetType() == SMDSAbs_Edge)
11008 edgesToCheck.insert(curElem);
11012 // --- add also edges lying on the set of faces (all nodes in alreadyCheckedNodes)
11013 std::set<const SMDS_MeshElement*>::iterator eit = edgesToCheck.begin();
11014 for( ; eit != edgesToCheck.end(); eit++)
11016 bool onside = true;
11017 const SMDS_MeshElement* anEdge = *eit;
11018 SMDS_ElemIteratorPtr nodeItr = anEdge->nodesIterator();
11019 while ( nodeItr->more() )
11021 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
11022 if (alreadyCheckedNodes.find(aNode) == alreadyCheckedNodes.end())
11030 theAffectedElems.insert(anEdge);
11036 const double aTol = Precision::Confusion();
11037 auto_ptr< BRepClass3d_SolidClassifier> bsc3d;
11038 auto_ptr<_FaceClassifier> aFaceClassifier;
11039 if ( theShape.ShapeType() == TopAbs_SOLID )
11041 bsc3d.reset( new BRepClass3d_SolidClassifier(theShape));;
11042 bsc3d->PerformInfinitePoint(aTol);
11044 else if (theShape.ShapeType() == TopAbs_FACE )
11046 aFaceClassifier.reset( new _FaceClassifier(TopoDS::Face(theShape)));
11049 // iterates on indicated elements and get elements by back references from their nodes
11050 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
11051 for ( ; elemItr != theElems.end(); ++elemItr )
11053 SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
11056 SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
11057 while ( nodeItr->more() )
11059 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
11060 if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
11062 SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
11063 while ( backElemItr->more() )
11065 const SMDS_MeshElement* curElem = backElemItr->next();
11066 if ( curElem && theElems.find(curElem) == theElems.end() &&
11068 isInside( curElem, *bsc3d, aTol ) :
11069 isInside( curElem, *aFaceClassifier, aTol )))
11070 theAffectedElems.insert( curElem );
11078 //================================================================================
11080 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
11081 \param theElems - group of of elements (edges or faces) to be replicated
11082 \param theNodesNot - group of nodes not to replicate
11083 \param theShape - shape to detect affected elements (element which geometric center
11084 located on or inside shape).
11085 The replicated nodes should be associated to affected elements.
11086 \return TRUE if operation has been completed successfully, FALSE otherwise
11088 //================================================================================
11090 bool SMESH_MeshEditor::DoubleNodesInRegion( const TIDSortedElemSet& theElems,
11091 const TIDSortedElemSet& theNodesNot,
11092 const TopoDS_Shape& theShape )
11094 if ( theShape.IsNull() )
11097 const double aTol = Precision::Confusion();
11098 SMESHUtils::Deleter< BRepClass3d_SolidClassifier> bsc3d;
11099 SMESHUtils::Deleter<_FaceClassifier> aFaceClassifier;
11100 if ( theShape.ShapeType() == TopAbs_SOLID )
11102 bsc3d._obj = new BRepClass3d_SolidClassifier( theShape );
11103 bsc3d->PerformInfinitePoint(aTol);
11105 else if (theShape.ShapeType() == TopAbs_FACE )
11107 aFaceClassifier._obj = new _FaceClassifier( TopoDS::Face( theShape ));
11110 // iterates on indicated elements and get elements by back references from their nodes
11111 TIDSortedElemSet anAffected;
11112 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
11113 for ( ; elemItr != theElems.end(); ++elemItr )
11115 SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
11119 SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
11120 while ( nodeItr->more() )
11122 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
11123 if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
11125 SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
11126 while ( backElemItr->more() )
11128 const SMDS_MeshElement* curElem = backElemItr->next();
11129 if ( curElem && theElems.find(curElem) == theElems.end() &&
11131 isInside( curElem, *bsc3d, aTol ) :
11132 isInside( curElem, *aFaceClassifier, aTol )))
11133 anAffected.insert( curElem );
11137 return DoubleNodes( theElems, theNodesNot, anAffected );
11141 * \brief compute an oriented angle between two planes defined by four points.
11142 * The vector (p0,p1) defines the intersection of the 2 planes (p0,p1,g1) and (p0,p1,g2)
11143 * @param p0 base of the rotation axe
11144 * @param p1 extremity of the rotation axe
11145 * @param g1 belongs to the first plane
11146 * @param g2 belongs to the second plane
11148 double SMESH_MeshEditor::OrientedAngle(const gp_Pnt& p0, const gp_Pnt& p1, const gp_Pnt& g1, const gp_Pnt& g2)
11150 gp_Vec vref(p0, p1);
11153 gp_Vec n1 = vref.Crossed(v1);
11154 gp_Vec n2 = vref.Crossed(v2);
11156 return n2.AngleWithRef(n1, vref);
11158 catch ( Standard_Failure ) {
11160 return Max( v1.Magnitude(), v2.Magnitude() );
11164 * \brief Double nodes on shared faces between groups of volumes and create flat elements on demand.
11165 * The list of groups must contain at least two groups. The groups have to be disjoint: no common element into two different groups.
11166 * The nodes of the internal faces at the boundaries of the groups are doubled. Optionally, the internal faces are replaced by flat elements.
11167 * Triangles are transformed into prisms, and quadrangles into hexahedrons.
11168 * The flat elements are stored in groups of volumes. These groups are named according to the position of the group in the list:
11169 * 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.
11170 * 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.
11171 * All the flat elements are gathered into the group named "joints3D" (or "joints2D" in 2D situation).
11172 * The flat element of the multiple junctions between the simple junction are stored in a group named "jointsMultiples".
11173 * \param theElems - list of groups of volumes, where a group of volume is a set of
11174 * SMDS_MeshElements sorted by Id.
11175 * \param createJointElems - if TRUE, create the elements
11176 * \param onAllBoundaries - if TRUE, the nodes and elements are also created on
11177 * the boundary between \a theDomains and the rest mesh
11178 * \return TRUE if operation has been completed successfully, FALSE otherwise
11180 bool SMESH_MeshEditor::DoubleNodesOnGroupBoundaries( const std::vector<TIDSortedElemSet>& theElems,
11181 bool createJointElems,
11182 bool onAllBoundaries)
11184 // MESSAGE("----------------------------------------------");
11185 // MESSAGE("SMESH_MeshEditor::doubleNodesOnGroupBoundaries");
11186 // MESSAGE("----------------------------------------------");
11188 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11189 meshDS->BuildDownWardConnectivity(true);
11191 SMDS_UnstructuredGrid *grid = meshDS->getGrid();
11193 // --- build the list of faces shared by 2 domains (group of elements), with their domain and volume indexes
11194 // build the list of cells with only a node or an edge on the border, with their domain and volume indexes
11195 // build the list of nodes shared by 2 or more domains, with their domain indexes
11197 std::map<DownIdType, std::map<int,int>, DownIdCompare> faceDomains; // face --> (id domain --> id volume)
11198 std::map<int,int>celldom; // cell vtkId --> domain
11199 std::map<DownIdType, std::map<int,int>, DownIdCompare> cellDomains; // oldNode --> (id domain --> id cell)
11200 std::map<int, std::map<int,int> > nodeDomains; // oldId --> (domainId --> newId)
11201 faceDomains.clear();
11203 cellDomains.clear();
11204 nodeDomains.clear();
11205 std::map<int,int> emptyMap;
11206 std::set<int> emptySet;
11209 //MESSAGE(".. Number of domains :"<<theElems.size());
11211 TIDSortedElemSet theRestDomElems;
11212 const int iRestDom = -1;
11213 const int idom0 = onAllBoundaries ? iRestDom : 0;
11214 const int nbDomains = theElems.size();
11216 // Check if the domains do not share an element
11217 for (int idom = 0; idom < nbDomains-1; idom++)
11219 // MESSAGE("... Check of domain #" << idom);
11220 const TIDSortedElemSet& domain = theElems[idom];
11221 TIDSortedElemSet::const_iterator elemItr = domain.begin();
11222 for (; elemItr != domain.end(); ++elemItr)
11224 const SMDS_MeshElement* anElem = *elemItr;
11225 int idombisdeb = idom + 1 ;
11226 // check if the element belongs to a domain further in the list
11227 for ( size_t idombis = idombisdeb; idombis < theElems.size(); idombis++ )
11229 const TIDSortedElemSet& domainbis = theElems[idombis];
11230 if ( domainbis.count( anElem ))
11232 MESSAGE(".... Domain #" << idom);
11233 MESSAGE(".... Domain #" << idombis);
11234 throw SALOME_Exception("The domains are not disjoint.");
11241 for (int idom = 0; idom < nbDomains; idom++)
11244 // --- build a map (face to duplicate --> volume to modify)
11245 // with all the faces shared by 2 domains (group of elements)
11246 // and corresponding volume of this domain, for each shared face.
11247 // a volume has a face shared by 2 domains if it has a neighbor which is not in his domain.
11249 //MESSAGE("... Neighbors of domain #" << idom);
11250 const TIDSortedElemSet& domain = theElems[idom];
11251 TIDSortedElemSet::const_iterator elemItr = domain.begin();
11252 for (; elemItr != domain.end(); ++elemItr)
11254 const SMDS_MeshElement* anElem = *elemItr;
11257 int vtkId = anElem->getVtkId();
11258 //MESSAGE(" vtkId " << vtkId << " smdsId " << anElem->GetID());
11259 int neighborsVtkIds[NBMAXNEIGHBORS];
11260 int downIds[NBMAXNEIGHBORS];
11261 unsigned char downTypes[NBMAXNEIGHBORS];
11262 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
11263 for (int n = 0; n < nbNeighbors; n++)
11265 int smdsId = meshDS->fromVtkToSmds(neighborsVtkIds[n]);
11266 const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
11267 if (elem && ! domain.count(elem)) // neighbor is in another domain : face is shared
11270 for ( size_t idombis = 0; idombis < theElems.size() && !ok; idombis++) // check if the neighbor belongs to another domain of the list
11272 // MESSAGE("Domain " << idombis);
11273 const TIDSortedElemSet& domainbis = theElems[idombis];
11274 if ( domainbis.count(elem)) ok = true ; // neighbor is in a correct domain : face is kept
11276 if ( ok || onAllBoundaries ) // the characteristics of the face is stored
11278 DownIdType face(downIds[n], downTypes[n]);
11279 if (!faceDomains[face].count(idom))
11281 faceDomains[face][idom] = vtkId; // volume associated to face in this domain
11282 celldom[vtkId] = idom;
11283 //MESSAGE(" cell with a border " << vtkId << " domain " << idom);
11287 theRestDomElems.insert( elem );
11288 faceDomains[face][iRestDom] = neighborsVtkIds[n];
11289 celldom[neighborsVtkIds[n]] = iRestDom;
11297 //MESSAGE("Number of shared faces " << faceDomains.size());
11298 std::map<DownIdType, std::map<int, int>, DownIdCompare>::iterator itface;
11300 // --- explore the shared faces domain by domain,
11301 // explore the nodes of the face and see if they belong to a cell in the domain,
11302 // which has only a node or an edge on the border (not a shared face)
11304 for (int idomain = idom0; idomain < nbDomains; idomain++)
11306 //MESSAGE("Domain " << idomain);
11307 const TIDSortedElemSet& domain = (idomain == iRestDom) ? theRestDomElems : theElems[idomain];
11308 itface = faceDomains.begin();
11309 for (; itface != faceDomains.end(); ++itface)
11311 const std::map<int, int>& domvol = itface->second;
11312 if (!domvol.count(idomain))
11314 DownIdType face = itface->first;
11315 //MESSAGE(" --- face " << face.cellId);
11316 std::set<int> oldNodes;
11318 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11319 std::set<int>::iterator itn = oldNodes.begin();
11320 for (; itn != oldNodes.end(); ++itn)
11323 //MESSAGE(" node " << oldId);
11324 vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
11325 for (int i=0; i<l.ncells; i++)
11327 int vtkId = l.cells[i];
11328 const SMDS_MeshElement* anElem = GetMeshDS()->FindElement(GetMeshDS()->fromVtkToSmds(vtkId));
11329 if (!domain.count(anElem))
11331 int vtkType = grid->GetCellType(vtkId);
11332 int downId = grid->CellIdToDownId(vtkId);
11335 MESSAGE("doubleNodesOnGroupBoundaries: internal algorithm problem");
11336 continue; // not OK at this stage of the algorithm:
11337 //no cells created after BuildDownWardConnectivity
11339 DownIdType aCell(downId, vtkType);
11340 cellDomains[aCell][idomain] = vtkId;
11341 celldom[vtkId] = idomain;
11342 //MESSAGE(" cell " << vtkId << " domain " << idomain);
11348 // --- explore the shared faces domain by domain, to duplicate the nodes in a coherent way
11349 // for each shared face, get the nodes
11350 // for each node, for each domain of the face, create a clone of the node
11352 // --- edges at the intersection of 3 or 4 domains, with the order of domains to build
11353 // junction elements of type prism or hexa. the key is the pair of nodesId (lower first)
11354 // the value is the ordered domain ids. (more than 4 domains not taken into account)
11356 std::map<std::vector<int>, std::vector<int> > edgesMultiDomains; // nodes of edge --> ordered domains
11357 std::map<int, std::vector<int> > mutipleNodes; // nodes multi domains with domain order
11358 std::map<int, std::vector<int> > mutipleNodesToFace; // nodes multi domains with domain order to transform in Face (junction between 3 or more 2D domains)
11360 //MESSAGE(".. Duplication of the nodes");
11361 for (int idomain = idom0; idomain < nbDomains; idomain++)
11363 itface = faceDomains.begin();
11364 for (; itface != faceDomains.end(); ++itface)
11366 const std::map<int, int>& domvol = itface->second;
11367 if (!domvol.count(idomain))
11369 DownIdType face = itface->first;
11370 //MESSAGE(" --- face " << face.cellId);
11371 std::set<int> oldNodes;
11373 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11374 std::set<int>::iterator itn = oldNodes.begin();
11375 for (; itn != oldNodes.end(); ++itn)
11378 if (nodeDomains[oldId].empty())
11380 nodeDomains[oldId][idomain] = oldId; // keep the old node in the first domain
11381 //MESSAGE("-+-+-b oldNode " << oldId << " domain " << idomain);
11383 std::map<int, int>::const_iterator itdom = domvol.begin();
11384 for (; itdom != domvol.end(); ++itdom)
11386 int idom = itdom->first;
11387 //MESSAGE(" domain " << idom);
11388 if (!nodeDomains[oldId].count(idom)) // --- node to clone
11390 if (nodeDomains[oldId].size() >= 2) // a multiple node
11392 vector<int> orderedDoms;
11393 //MESSAGE("multiple node " << oldId);
11394 if (mutipleNodes.count(oldId))
11395 orderedDoms = mutipleNodes[oldId];
11398 map<int,int>::iterator it = nodeDomains[oldId].begin();
11399 for (; it != nodeDomains[oldId].end(); ++it)
11400 orderedDoms.push_back(it->first);
11402 orderedDoms.push_back(idom); // TODO order ==> push_front or back
11403 //stringstream txt;
11404 //for (int i=0; i<orderedDoms.size(); i++)
11405 // txt << orderedDoms[i] << " ";
11406 //MESSAGE("orderedDoms " << txt.str());
11407 mutipleNodes[oldId] = orderedDoms;
11409 double *coords = grid->GetPoint(oldId);
11410 SMDS_MeshNode *newNode = meshDS->AddNode(coords[0], coords[1], coords[2]);
11411 copyPosition( meshDS->FindNodeVtk( oldId ), newNode );
11412 int newId = newNode->getVtkId();
11413 nodeDomains[oldId][idom] = newId; // cloned node for other domains
11414 //MESSAGE("-+-+-c oldNode " << oldId << " domain " << idomain << " newNode " << newId << " domain " << idom << " size=" <<nodeDomains[oldId].size());
11421 //MESSAGE(".. Creation of elements");
11422 for (int idomain = idom0; idomain < nbDomains; idomain++)
11424 itface = faceDomains.begin();
11425 for (; itface != faceDomains.end(); ++itface)
11427 std::map<int, int> domvol = itface->second;
11428 if (!domvol.count(idomain))
11430 DownIdType face = itface->first;
11431 //MESSAGE(" --- face " << face.cellId);
11432 std::set<int> oldNodes;
11434 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11435 int nbMultipleNodes = 0;
11436 std::set<int>::iterator itn = oldNodes.begin();
11437 for (; itn != oldNodes.end(); ++itn)
11440 if (mutipleNodes.count(oldId))
11443 if (nbMultipleNodes > 1) // check if an edge of the face is shared between 3 or more domains
11445 //MESSAGE("multiple Nodes detected on a shared face");
11446 int downId = itface->first.cellId;
11447 unsigned char cellType = itface->first.cellType;
11448 // --- shared edge or shared face ?
11449 if ((cellType == VTK_LINE) || (cellType == VTK_QUADRATIC_EDGE)) // shared edge (between two faces)
11452 int nbNodes = grid->getDownArray(cellType)->getNodes(downId, nodes);
11453 for (int i=0; i< nbNodes; i=i+nbNodes-1) // i=0 , i=nbNodes-1
11454 if (mutipleNodes.count(nodes[i]))
11455 if (!mutipleNodesToFace.count(nodes[i]))
11456 mutipleNodesToFace[nodes[i]] = mutipleNodes[nodes[i]];
11458 else // shared face (between two volumes)
11460 int nbEdges = grid->getDownArray(cellType)->getNumberOfDownCells(downId);
11461 const int* downEdgeIds = grid->getDownArray(cellType)->getDownCells(downId);
11462 const unsigned char* edgeType = grid->getDownArray(cellType)->getDownTypes(downId);
11463 for (int ie =0; ie < nbEdges; ie++)
11466 int nbNodes = grid->getDownArray(edgeType[ie])->getNodes(downEdgeIds[ie], nodes);
11467 if ( mutipleNodes.count(nodes[0]) && mutipleNodes.count( nodes[ nbNodes-1 ]))
11469 vector<int> vn0 = mutipleNodes[nodes[0]];
11470 vector<int> vn1 = mutipleNodes[nodes[nbNodes - 1]];
11472 for ( size_t i0 = 0; i0 < vn0.size(); i0++ )
11473 for ( size_t i1 = 0; i1 < vn1.size(); i1++ )
11474 if ( vn0[i0] == vn1[i1] )
11475 doms.push_back( vn0[ i0 ]);
11476 if ( doms.size() > 2 )
11478 //MESSAGE(" detect edgesMultiDomains " << nodes[0] << " " << nodes[nbNodes - 1]);
11479 double *coords = grid->GetPoint(nodes[0]);
11480 gp_Pnt p0(coords[0], coords[1], coords[2]);
11481 coords = grid->GetPoint(nodes[nbNodes - 1]);
11482 gp_Pnt p1(coords[0], coords[1], coords[2]);
11484 int vtkVolIds[1000]; // an edge can belong to a lot of volumes
11485 map<int, SMDS_VtkVolume*> domvol; // domain --> a volume with the edge
11486 map<int, double> angleDom; // oriented angles between planes defined by edge and volume centers
11487 int nbvol = grid->GetParentVolumes(vtkVolIds, downEdgeIds[ie], edgeType[ie]);
11488 for ( size_t id = 0; id < doms.size(); id++ )
11490 int idom = doms[id];
11491 const TIDSortedElemSet& domain = (idom == iRestDom) ? theRestDomElems : theElems[idom];
11492 for ( int ivol = 0; ivol < nbvol; ivol++ )
11494 int smdsId = meshDS->fromVtkToSmds(vtkVolIds[ivol]);
11495 SMDS_MeshElement* elem = (SMDS_MeshElement*)meshDS->FindElement(smdsId);
11496 if (domain.count(elem))
11498 SMDS_VtkVolume* svol = dynamic_cast<SMDS_VtkVolume*>(elem);
11499 domvol[idom] = svol;
11500 //MESSAGE(" domain " << idom << " volume " << elem->GetID());
11502 vtkIdType npts = 0;
11503 vtkIdType* pts = 0;
11504 grid->GetCellPoints(vtkVolIds[ivol], npts, pts);
11505 SMDS_VtkVolume::gravityCenter(grid, pts, npts, values);
11508 gref.SetXYZ(gp_XYZ(values[0], values[1], values[2]));
11509 angleDom[idom] = 0;
11513 gp_Pnt g(values[0], values[1], values[2]);
11514 angleDom[idom] = OrientedAngle(p0, p1, gref, g); // -pi<angle<+pi
11515 //MESSAGE(" angle=" << angleDom[idom]);
11521 map<double, int> sortedDom; // sort domains by angle
11522 for (map<int, double>::iterator ia = angleDom.begin(); ia != angleDom.end(); ++ia)
11523 sortedDom[ia->second] = ia->first;
11524 vector<int> vnodes;
11526 for (map<double, int>::iterator ib = sortedDom.begin(); ib != sortedDom.end(); ++ib)
11528 vdom.push_back(ib->second);
11529 //MESSAGE(" ordered domain " << ib->second << " angle " << ib->first);
11531 for (int ino = 0; ino < nbNodes; ino++)
11532 vnodes.push_back(nodes[ino]);
11533 edgesMultiDomains[vnodes] = vdom; // nodes vector --> ordered domains
11542 // --- iterate on shared faces (volumes to modify, face to extrude)
11543 // get node id's of the face (id SMDS = id VTK)
11544 // create flat element with old and new nodes if requested
11546 // --- new quad nodes on flat quad elements: oldId --> ((domain1 X domain2) --> newId)
11547 // (domain1 X domain2) = domain1 + MAXINT*domain2
11549 std::map<int, std::map<long,int> > nodeQuadDomains;
11550 std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11552 //MESSAGE(".. Creation of elements: simple junction");
11553 if (createJointElems)
11556 string joints2DName = "joints2D";
11557 mapOfJunctionGroups[joints2DName] = this->myMesh->AddGroup(SMDSAbs_Face, joints2DName.c_str(), idg);
11558 SMESHDS_Group *joints2DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints2DName]->GetGroupDS());
11559 string joints3DName = "joints3D";
11560 mapOfJunctionGroups[joints3DName] = this->myMesh->AddGroup(SMDSAbs_Volume, joints3DName.c_str(), idg);
11561 SMESHDS_Group *joints3DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints3DName]->GetGroupDS());
11563 itface = faceDomains.begin();
11564 for (; itface != faceDomains.end(); ++itface)
11566 DownIdType face = itface->first;
11567 std::set<int> oldNodes;
11568 std::set<int>::iterator itn;
11570 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11572 std::map<int, int> domvol = itface->second;
11573 std::map<int, int>::iterator itdom = domvol.begin();
11574 int dom1 = itdom->first;
11575 int vtkVolId = itdom->second;
11577 int dom2 = itdom->first;
11578 SMDS_MeshCell *vol = grid->extrudeVolumeFromFace(vtkVolId, dom1, dom2, oldNodes, nodeDomains,
11580 stringstream grpname;
11583 grpname << dom1 << "_" << dom2;
11585 grpname << dom2 << "_" << dom1;
11586 string namegrp = grpname.str();
11587 if (!mapOfJunctionGroups.count(namegrp))
11588 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(vol->GetType(), namegrp.c_str(), idg);
11589 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11591 sgrp->Add(vol->GetID());
11592 if (vol->GetType() == SMDSAbs_Volume)
11593 joints3DGrp->Add(vol->GetID());
11594 else if (vol->GetType() == SMDSAbs_Face)
11595 joints2DGrp->Add(vol->GetID());
11599 // --- create volumes on multiple domain intersection if requested
11600 // iterate on mutipleNodesToFace
11601 // iterate on edgesMultiDomains
11603 //MESSAGE(".. Creation of elements: multiple junction");
11604 if (createJointElems)
11606 // --- iterate on mutipleNodesToFace
11608 std::map<int, std::vector<int> >::iterator itn = mutipleNodesToFace.begin();
11609 for (; itn != mutipleNodesToFace.end(); ++itn)
11611 int node = itn->first;
11612 vector<int> orderDom = itn->second;
11613 vector<vtkIdType> orderedNodes;
11614 for ( size_t idom = 0; idom < orderDom.size(); idom++ )
11615 orderedNodes.push_back( nodeDomains[ node ][ orderDom[ idom ]]);
11616 SMDS_MeshFace* face = this->GetMeshDS()->AddFaceFromVtkIds(orderedNodes);
11618 stringstream grpname;
11620 grpname << 0 << "_" << 0;
11622 string namegrp = grpname.str();
11623 if (!mapOfJunctionGroups.count(namegrp))
11624 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Face, namegrp.c_str(), idg);
11625 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11627 sgrp->Add(face->GetID());
11630 // --- iterate on edgesMultiDomains
11632 std::map<std::vector<int>, std::vector<int> >::iterator ite = edgesMultiDomains.begin();
11633 for (; ite != edgesMultiDomains.end(); ++ite)
11635 vector<int> nodes = ite->first;
11636 vector<int> orderDom = ite->second;
11637 vector<vtkIdType> orderedNodes;
11638 if (nodes.size() == 2)
11640 //MESSAGE(" use edgesMultiDomains " << nodes[0] << " " << nodes[1]);
11641 for ( size_t ino = 0; ino < nodes.size(); ino++ )
11642 if ( orderDom.size() == 3 )
11643 for ( size_t idom = 0; idom < orderDom.size(); idom++ )
11644 orderedNodes.push_back( nodeDomains[ nodes[ ino ]][ orderDom[ idom ]]);
11646 for (int idom = orderDom.size()-1; idom >=0; idom--)
11647 orderedNodes.push_back( nodeDomains[ nodes[ ino ]][ orderDom[ idom ]]);
11648 SMDS_MeshVolume* vol = this->GetMeshDS()->AddVolumeFromVtkIds(orderedNodes);
11651 string namegrp = "jointsMultiples";
11652 if (!mapOfJunctionGroups.count(namegrp))
11653 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
11654 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11656 sgrp->Add(vol->GetID());
11660 //INFOS("Quadratic multiple joints not implemented");
11661 // TODO quadratic nodes
11666 // --- list the explicit faces and edges of the mesh that need to be modified,
11667 // i.e. faces and edges built with one or more duplicated nodes.
11668 // associate these faces or edges to their corresponding domain.
11669 // only the first domain found is kept when a face or edge is shared
11671 std::map<DownIdType, std::map<int,int>, DownIdCompare> faceOrEdgeDom; // cellToModify --> (id domain --> id cell)
11672 std::map<int,int> feDom; // vtk id of cell to modify --> id domain
11673 faceOrEdgeDom.clear();
11676 //MESSAGE(".. Modification of elements");
11677 for (int idomain = idom0; idomain < nbDomains; idomain++)
11679 std::map<int, std::map<int, int> >::const_iterator itnod = nodeDomains.begin();
11680 for (; itnod != nodeDomains.end(); ++itnod)
11682 int oldId = itnod->first;
11683 //MESSAGE(" node " << oldId);
11684 vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
11685 for (int i = 0; i < l.ncells; i++)
11687 int vtkId = l.cells[i];
11688 int vtkType = grid->GetCellType(vtkId);
11689 int downId = grid->CellIdToDownId(vtkId);
11691 continue; // new cells: not to be modified
11692 DownIdType aCell(downId, vtkType);
11693 int volParents[1000];
11694 int nbvol = grid->GetParentVolumes(volParents, vtkId);
11695 for (int j = 0; j < nbvol; j++)
11696 if (celldom.count(volParents[j]) && (celldom[volParents[j]] == idomain))
11697 if (!feDom.count(vtkId))
11699 feDom[vtkId] = idomain;
11700 faceOrEdgeDom[aCell] = emptyMap;
11701 faceOrEdgeDom[aCell][idomain] = vtkId; // affect face or edge to the first domain only
11702 //MESSAGE("affect cell " << this->GetMeshDS()->fromVtkToSmds(vtkId) << " domain " << idomain
11703 // << " type " << vtkType << " downId " << downId);
11709 // --- iterate on shared faces (volumes to modify, face to extrude)
11710 // get node id's of the face
11711 // replace old nodes by new nodes in volumes, and update inverse connectivity
11713 std::map<DownIdType, std::map<int,int>, DownIdCompare>* maps[3] = {&faceDomains, &cellDomains, &faceOrEdgeDom};
11714 for (int m=0; m<3; m++)
11716 std::map<DownIdType, std::map<int,int>, DownIdCompare>* amap = maps[m];
11717 itface = (*amap).begin();
11718 for (; itface != (*amap).end(); ++itface)
11720 DownIdType face = itface->first;
11721 std::set<int> oldNodes;
11722 std::set<int>::iterator itn;
11724 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11725 //MESSAGE("examine cell, downId " << face.cellId << " type " << int(face.cellType));
11726 std::map<int, int> localClonedNodeIds;
11728 std::map<int, int> domvol = itface->second;
11729 std::map<int, int>::iterator itdom = domvol.begin();
11730 for (; itdom != domvol.end(); ++itdom)
11732 int idom = itdom->first;
11733 int vtkVolId = itdom->second;
11734 //MESSAGE("modify nodes of cell " << this->GetMeshDS()->fromVtkToSmds(vtkVolId) << " domain " << idom);
11735 localClonedNodeIds.clear();
11736 for (itn = oldNodes.begin(); itn != oldNodes.end(); ++itn)
11739 if (nodeDomains[oldId].count(idom))
11741 localClonedNodeIds[oldId] = nodeDomains[oldId][idom];
11742 //MESSAGE(" node " << oldId << " --> " << localClonedNodeIds[oldId]);
11745 meshDS->ModifyCellNodes(vtkVolId, localClonedNodeIds);
11750 // Remove empty groups (issue 0022812)
11751 std::map<std::string, SMESH_Group*>::iterator name_group = mapOfJunctionGroups.begin();
11752 for ( ; name_group != mapOfJunctionGroups.end(); ++name_group )
11754 if ( name_group->second && name_group->second->GetGroupDS()->IsEmpty() )
11755 myMesh->RemoveGroup( name_group->second->GetGroupDS()->GetID() );
11758 meshDS->CleanDownWardConnectivity(); // Mesh has been modified, downward connectivity is no more usable, free memory
11759 grid->DeleteLinks();
11767 * \brief Double nodes on some external faces and create flat elements.
11768 * Flat elements are mainly used by some types of mechanic calculations.
11770 * Each group of the list must be constituted of faces.
11771 * Triangles are transformed in prisms, and quadrangles in hexahedrons.
11772 * @param theElems - list of groups of faces, where a group of faces is a set of
11773 * SMDS_MeshElements sorted by Id.
11774 * @return TRUE if operation has been completed successfully, FALSE otherwise
11776 bool SMESH_MeshEditor::CreateFlatElementsOnFacesGroups(const std::vector<TIDSortedElemSet>& theElems)
11778 // MESSAGE("-------------------------------------------------");
11779 // MESSAGE("SMESH_MeshEditor::CreateFlatElementsOnFacesGroups");
11780 // MESSAGE("-------------------------------------------------");
11782 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11784 // --- For each group of faces
11785 // duplicate the nodes, create a flat element based on the face
11786 // replace the nodes of the faces by their clones
11788 std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> clonedNodes;
11789 std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> intermediateNodes;
11790 clonedNodes.clear();
11791 intermediateNodes.clear();
11792 std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11793 mapOfJunctionGroups.clear();
11795 for ( size_t idom = 0; idom < theElems.size(); idom++ )
11797 const TIDSortedElemSet& domain = theElems[idom];
11798 TIDSortedElemSet::const_iterator elemItr = domain.begin();
11799 for ( ; elemItr != domain.end(); ++elemItr )
11801 SMDS_MeshElement* anElem = (SMDS_MeshElement*) *elemItr;
11802 SMDS_MeshFace* aFace = dynamic_cast<SMDS_MeshFace*> (anElem);
11805 // MESSAGE("aFace=" << aFace->GetID());
11806 bool isQuad = aFace->IsQuadratic();
11807 vector<const SMDS_MeshNode*> ln0, ln1, ln2, ln3, ln4;
11809 // --- clone the nodes, create intermediate nodes for non medium nodes of a quad face
11811 SMDS_ElemIteratorPtr nodeIt = aFace->nodesIterator();
11812 while (nodeIt->more())
11814 const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*> (nodeIt->next());
11815 bool isMedium = isQuad && (aFace->IsMediumNode(node));
11817 ln2.push_back(node);
11819 ln0.push_back(node);
11821 const SMDS_MeshNode* clone = 0;
11822 if (!clonedNodes.count(node))
11824 clone = meshDS->AddNode(node->X(), node->Y(), node->Z());
11825 copyPosition( node, clone );
11826 clonedNodes[node] = clone;
11829 clone = clonedNodes[node];
11832 ln3.push_back(clone);
11834 ln1.push_back(clone);
11836 const SMDS_MeshNode* inter = 0;
11837 if (isQuad && (!isMedium))
11839 if (!intermediateNodes.count(node))
11841 inter = meshDS->AddNode(node->X(), node->Y(), node->Z());
11842 copyPosition( node, inter );
11843 intermediateNodes[node] = inter;
11846 inter = intermediateNodes[node];
11847 ln4.push_back(inter);
11851 // --- extrude the face
11853 vector<const SMDS_MeshNode*> ln;
11854 SMDS_MeshVolume* vol = 0;
11855 vtkIdType aType = aFace->GetVtkType();
11859 vol = meshDS->AddVolume(ln0[2], ln0[1], ln0[0], ln1[2], ln1[1], ln1[0]);
11860 // MESSAGE("vol prism " << vol->GetID());
11861 ln.push_back(ln1[0]);
11862 ln.push_back(ln1[1]);
11863 ln.push_back(ln1[2]);
11866 vol = meshDS->AddVolume(ln0[3], ln0[2], ln0[1], ln0[0], ln1[3], ln1[2], ln1[1], ln1[0]);
11867 // MESSAGE("vol hexa " << vol->GetID());
11868 ln.push_back(ln1[0]);
11869 ln.push_back(ln1[1]);
11870 ln.push_back(ln1[2]);
11871 ln.push_back(ln1[3]);
11873 case VTK_QUADRATIC_TRIANGLE:
11874 vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln0[0], ln0[1], ln0[2], ln3[0], ln3[1], ln3[2],
11875 ln2[0], ln2[1], ln2[2], ln4[0], ln4[1], ln4[2]);
11876 // MESSAGE("vol quad prism " << vol->GetID());
11877 ln.push_back(ln1[0]);
11878 ln.push_back(ln1[1]);
11879 ln.push_back(ln1[2]);
11880 ln.push_back(ln3[0]);
11881 ln.push_back(ln3[1]);
11882 ln.push_back(ln3[2]);
11884 case VTK_QUADRATIC_QUAD:
11885 // vol = meshDS->AddVolume(ln0[0], ln0[1], ln0[2], ln0[3], ln1[0], ln1[1], ln1[2], ln1[3],
11886 // ln2[0], ln2[1], ln2[2], ln2[3], ln3[0], ln3[1], ln3[2], ln3[3],
11887 // ln4[0], ln4[1], ln4[2], ln4[3]);
11888 vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln1[3], ln0[0], ln0[1], ln0[2], ln0[3],
11889 ln3[0], ln3[1], ln3[2], ln3[3], ln2[0], ln2[1], ln2[2], ln2[3],
11890 ln4[0], ln4[1], ln4[2], ln4[3]);
11891 // MESSAGE("vol quad hexa " << vol->GetID());
11892 ln.push_back(ln1[0]);
11893 ln.push_back(ln1[1]);
11894 ln.push_back(ln1[2]);
11895 ln.push_back(ln1[3]);
11896 ln.push_back(ln3[0]);
11897 ln.push_back(ln3[1]);
11898 ln.push_back(ln3[2]);
11899 ln.push_back(ln3[3]);
11909 stringstream grpname;
11913 string namegrp = grpname.str();
11914 if (!mapOfJunctionGroups.count(namegrp))
11915 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
11916 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11918 sgrp->Add(vol->GetID());
11921 // --- modify the face
11923 aFace->ChangeNodes(&ln[0], ln.size());
11930 * \brief identify all the elements around a geom shape, get the faces delimiting the hole
11931 * Build groups of volume to remove, groups of faces to replace on the skin of the object,
11932 * groups of faces to remove inside the object, (idem edges).
11933 * Build ordered list of nodes at the border of each group of faces to replace (to be used to build a geom subshape)
11935 void SMESH_MeshEditor::CreateHoleSkin(double radius,
11936 const TopoDS_Shape& theShape,
11937 SMESH_NodeSearcher* theNodeSearcher,
11938 const char* groupName,
11939 std::vector<double>& nodesCoords,
11940 std::vector<std::vector<int> >& listOfListOfNodes)
11942 // MESSAGE("--------------------------------");
11943 // MESSAGE("SMESH_MeshEditor::CreateHoleSkin");
11944 // MESSAGE("--------------------------------");
11946 // --- zone of volumes to remove is given :
11947 // 1 either by a geom shape (one or more vertices) and a radius,
11948 // 2 either by a group of nodes (representative of the shape)to use with the radius,
11949 // 3 either by a group of nodes where all the elements build on one of this nodes are to remove,
11950 // In the case 2, the group of nodes is an external group of nodes from another mesh,
11951 // In the case 3, the group of nodes is an internal group of the mesh (obtained for instance by a filter),
11952 // defined by it's name.
11954 SMESHDS_GroupBase* groupDS = 0;
11955 SMESH_Mesh::GroupIteratorPtr groupIt = this->myMesh->GetGroups();
11956 while ( groupIt->more() )
11959 SMESH_Group * group = groupIt->next();
11960 if ( !group ) continue;
11961 groupDS = group->GetGroupDS();
11962 if ( !groupDS || groupDS->IsEmpty() ) continue;
11963 std::string grpName = group->GetName();
11964 //MESSAGE("grpName=" << grpName);
11965 if (grpName == groupName)
11971 bool isNodeGroup = false;
11972 bool isNodeCoords = false;
11975 if (groupDS->GetType() != SMDSAbs_Node)
11977 isNodeGroup = true; // a group of nodes exists and it is in this mesh
11980 if (nodesCoords.size() > 0)
11981 isNodeCoords = true; // a list o nodes given by their coordinates
11982 //MESSAGE("---" << isNodeGroup << " " << isNodeCoords);
11984 // --- define groups to build
11986 int idg; // --- group of SMDS volumes
11987 string grpvName = groupName;
11988 grpvName += "_vol";
11989 SMESH_Group *grp = this->myMesh->AddGroup(SMDSAbs_Volume, grpvName.c_str(), idg);
11992 MESSAGE("group not created " << grpvName);
11995 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(grp->GetGroupDS());
11997 int idgs; // --- group of SMDS faces on the skin
11998 string grpsName = groupName;
11999 grpsName += "_skin";
12000 SMESH_Group *grps = this->myMesh->AddGroup(SMDSAbs_Face, grpsName.c_str(), idgs);
12003 MESSAGE("group not created " << grpsName);
12006 SMESHDS_Group *sgrps = dynamic_cast<SMESHDS_Group*>(grps->GetGroupDS());
12008 int idgi; // --- group of SMDS faces internal (several shapes)
12009 string grpiName = groupName;
12010 grpiName += "_internalFaces";
12011 SMESH_Group *grpi = this->myMesh->AddGroup(SMDSAbs_Face, grpiName.c_str(), idgi);
12014 MESSAGE("group not created " << grpiName);
12017 SMESHDS_Group *sgrpi = dynamic_cast<SMESHDS_Group*>(grpi->GetGroupDS());
12019 int idgei; // --- group of SMDS faces internal (several shapes)
12020 string grpeiName = groupName;
12021 grpeiName += "_internalEdges";
12022 SMESH_Group *grpei = this->myMesh->AddGroup(SMDSAbs_Edge, grpeiName.c_str(), idgei);
12025 MESSAGE("group not created " << grpeiName);
12028 SMESHDS_Group *sgrpei = dynamic_cast<SMESHDS_Group*>(grpei->GetGroupDS());
12030 // --- build downward connectivity
12032 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
12033 meshDS->BuildDownWardConnectivity(true);
12034 SMDS_UnstructuredGrid* grid = meshDS->getGrid();
12036 // --- set of volumes detected inside
12038 std::set<int> setOfInsideVol;
12039 std::set<int> setOfVolToCheck;
12041 std::vector<gp_Pnt> gpnts;
12044 if (isNodeGroup) // --- a group of nodes is provided : find all the volumes using one or more of this nodes
12046 //MESSAGE("group of nodes provided");
12047 SMDS_ElemIteratorPtr elemIt = groupDS->GetElements();
12048 while ( elemIt->more() )
12050 const SMDS_MeshElement* elem = elemIt->next();
12053 const SMDS_MeshNode* node = dynamic_cast<const SMDS_MeshNode*>(elem);
12056 SMDS_MeshElement* vol = 0;
12057 SMDS_ElemIteratorPtr volItr = node->GetInverseElementIterator(SMDSAbs_Volume);
12058 while (volItr->more())
12060 vol = (SMDS_MeshElement*)volItr->next();
12061 setOfInsideVol.insert(vol->getVtkId());
12062 sgrp->Add(vol->GetID());
12066 else if (isNodeCoords)
12068 //MESSAGE("list of nodes coordinates provided");
12071 while ( i < nodesCoords.size()-2 )
12073 double x = nodesCoords[i++];
12074 double y = nodesCoords[i++];
12075 double z = nodesCoords[i++];
12076 gp_Pnt p = gp_Pnt(x, y ,z);
12077 gpnts.push_back(p);
12078 //MESSAGE("TopoDS_Vertex " << k << " " << p.X() << " " << p.Y() << " " << p.Z());
12082 else // --- no group, no coordinates : use the vertices of the geom shape provided, and radius
12084 //MESSAGE("no group of nodes provided, using vertices from geom shape, and radius");
12085 TopTools_IndexedMapOfShape vertexMap;
12086 TopExp::MapShapes( theShape, TopAbs_VERTEX, vertexMap );
12087 gp_Pnt p = gp_Pnt(0,0,0);
12088 if (vertexMap.Extent() < 1)
12091 for ( int i = 1; i <= vertexMap.Extent(); ++i )
12093 const TopoDS_Vertex& vertex = TopoDS::Vertex( vertexMap( i ));
12094 p = BRep_Tool::Pnt(vertex);
12095 gpnts.push_back(p);
12096 //MESSAGE("TopoDS_Vertex " << i << " " << p.X() << " " << p.Y() << " " << p.Z());
12100 if (gpnts.size() > 0)
12102 const SMDS_MeshNode* startNode = theNodeSearcher->FindClosestTo(gpnts[0]);
12103 //MESSAGE("startNode->nodeId " << nodeId);
12105 double radius2 = radius*radius;
12106 //MESSAGE("radius2 " << radius2);
12108 // --- volumes on start node
12110 setOfVolToCheck.clear();
12111 SMDS_MeshElement* startVol = 0;
12112 SMDS_ElemIteratorPtr volItr = startNode->GetInverseElementIterator(SMDSAbs_Volume);
12113 while (volItr->more())
12115 startVol = (SMDS_MeshElement*)volItr->next();
12116 setOfVolToCheck.insert(startVol->getVtkId());
12118 if (setOfVolToCheck.empty())
12120 MESSAGE("No volumes found");
12124 // --- starting with central volumes then their neighbors, check if they are inside
12125 // or outside the domain, until no more new neighbor volume is inside.
12126 // Fill the group of inside volumes
12128 std::map<int, double> mapOfNodeDistance2;
12129 mapOfNodeDistance2.clear();
12130 std::set<int> setOfOutsideVol;
12131 while (!setOfVolToCheck.empty())
12133 std::set<int>::iterator it = setOfVolToCheck.begin();
12135 //MESSAGE("volume to check, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12136 bool volInside = false;
12137 vtkIdType npts = 0;
12138 vtkIdType* pts = 0;
12139 grid->GetCellPoints(vtkId, npts, pts);
12140 for (int i=0; i<npts; i++)
12142 double distance2 = 0;
12143 if (mapOfNodeDistance2.count(pts[i]))
12145 distance2 = mapOfNodeDistance2[pts[i]];
12146 //MESSAGE("point " << pts[i] << " distance2 " << distance2);
12150 double *coords = grid->GetPoint(pts[i]);
12151 gp_Pnt aPoint = gp_Pnt(coords[0], coords[1], coords[2]);
12153 for ( size_t j = 0; j < gpnts.size(); j++ )
12155 double d2 = aPoint.SquareDistance( gpnts[ j ]);
12156 if (d2 < distance2)
12159 if (distance2 < radius2)
12163 mapOfNodeDistance2[pts[i]] = distance2;
12164 //MESSAGE(" point " << pts[i] << " distance2 " << distance2 << " coords " << coords[0] << " " << coords[1] << " " << coords[2]);
12166 if (distance2 < radius2)
12168 volInside = true; // one or more nodes inside the domain
12169 sgrp->Add(meshDS->fromVtkToSmds(vtkId));
12175 setOfInsideVol.insert(vtkId);
12176 //MESSAGE(" volume inside, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12177 int neighborsVtkIds[NBMAXNEIGHBORS];
12178 int downIds[NBMAXNEIGHBORS];
12179 unsigned char downTypes[NBMAXNEIGHBORS];
12180 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12181 for (int n = 0; n < nbNeighbors; n++)
12182 if (!setOfInsideVol.count(neighborsVtkIds[n]) ||setOfOutsideVol.count(neighborsVtkIds[n]))
12183 setOfVolToCheck.insert(neighborsVtkIds[n]);
12187 setOfOutsideVol.insert(vtkId);
12188 //MESSAGE(" volume outside, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12190 setOfVolToCheck.erase(vtkId);
12194 // --- for outside hexahedrons, check if they have more than one neighbor volume inside
12195 // If yes, add the volume to the inside set
12197 bool addedInside = true;
12198 std::set<int> setOfVolToReCheck;
12199 while (addedInside)
12201 //MESSAGE(" --------------------------- re check");
12202 addedInside = false;
12203 std::set<int>::iterator itv = setOfInsideVol.begin();
12204 for (; itv != setOfInsideVol.end(); ++itv)
12207 int neighborsVtkIds[NBMAXNEIGHBORS];
12208 int downIds[NBMAXNEIGHBORS];
12209 unsigned char downTypes[NBMAXNEIGHBORS];
12210 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12211 for (int n = 0; n < nbNeighbors; n++)
12212 if (!setOfInsideVol.count(neighborsVtkIds[n]))
12213 setOfVolToReCheck.insert(neighborsVtkIds[n]);
12215 setOfVolToCheck = setOfVolToReCheck;
12216 setOfVolToReCheck.clear();
12217 while (!setOfVolToCheck.empty())
12219 std::set<int>::iterator it = setOfVolToCheck.begin();
12221 if (grid->GetCellType(vtkId) == VTK_HEXAHEDRON)
12223 //MESSAGE("volume to recheck, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12224 int countInside = 0;
12225 int neighborsVtkIds[NBMAXNEIGHBORS];
12226 int downIds[NBMAXNEIGHBORS];
12227 unsigned char downTypes[NBMAXNEIGHBORS];
12228 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12229 for (int n = 0; n < nbNeighbors; n++)
12230 if (setOfInsideVol.count(neighborsVtkIds[n]))
12232 //MESSAGE("countInside " << countInside);
12233 if (countInside > 1)
12235 //MESSAGE(" volume inside, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12236 setOfInsideVol.insert(vtkId);
12237 sgrp->Add(meshDS->fromVtkToSmds(vtkId));
12238 addedInside = true;
12241 setOfVolToReCheck.insert(vtkId);
12243 setOfVolToCheck.erase(vtkId);
12247 // --- map of Downward faces at the boundary, inside the global volume
12248 // map of Downward faces on the skin of the global volume (equivalent to SMDS faces on the skin)
12249 // fill group of SMDS faces inside the volume (when several volume shapes)
12250 // fill group of SMDS faces on the skin of the global volume (if skin)
12252 std::map<DownIdType, int, DownIdCompare> boundaryFaces; // boundary faces inside the volume --> corresponding cell
12253 std::map<DownIdType, int, DownIdCompare> skinFaces; // faces on the skin of the global volume --> corresponding cell
12254 std::set<int>::iterator it = setOfInsideVol.begin();
12255 for (; it != setOfInsideVol.end(); ++it)
12258 //MESSAGE(" vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
12259 int neighborsVtkIds[NBMAXNEIGHBORS];
12260 int downIds[NBMAXNEIGHBORS];
12261 unsigned char downTypes[NBMAXNEIGHBORS];
12262 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId, true);
12263 for (int n = 0; n < nbNeighbors; n++)
12265 int neighborDim = SMDS_Downward::getCellDimension(grid->GetCellType(neighborsVtkIds[n]));
12266 if (neighborDim == 3)
12268 if (! setOfInsideVol.count(neighborsVtkIds[n])) // neighbor volume is not inside : face is boundary
12270 DownIdType face(downIds[n], downTypes[n]);
12271 boundaryFaces[face] = vtkId;
12273 // if the face between to volumes is in the mesh, get it (internal face between shapes)
12274 int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12275 if (vtkFaceId >= 0)
12277 sgrpi->Add(meshDS->fromVtkToSmds(vtkFaceId));
12278 // find also the smds edges on this face
12279 int nbEdges = grid->getDownArray(downTypes[n])->getNumberOfDownCells(downIds[n]);
12280 const int* dEdges = grid->getDownArray(downTypes[n])->getDownCells(downIds[n]);
12281 const unsigned char* dTypes = grid->getDownArray(downTypes[n])->getDownTypes(downIds[n]);
12282 for (int i = 0; i < nbEdges; i++)
12284 int vtkEdgeId = grid->getDownArray(dTypes[i])->getVtkCellId(dEdges[i]);
12285 if (vtkEdgeId >= 0)
12286 sgrpei->Add(meshDS->fromVtkToSmds(vtkEdgeId));
12290 else if (neighborDim == 2) // skin of the volume
12292 DownIdType face(downIds[n], downTypes[n]);
12293 skinFaces[face] = vtkId;
12294 int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12295 if (vtkFaceId >= 0)
12296 sgrps->Add(meshDS->fromVtkToSmds(vtkFaceId));
12301 // --- identify the edges constituting the wire of each subshape on the skin
12302 // define polylines with the nodes of edges, equivalent to wires
12303 // project polylines on subshapes, and partition, to get geom faces
12305 std::map<int, std::set<int> > shapeIdToVtkIdSet; // shapeId --> set of vtkId on skin
12306 std::set<int> emptySet;
12308 std::set<int> shapeIds;
12310 SMDS_ElemIteratorPtr itelem = sgrps->GetElements();
12311 while (itelem->more())
12313 const SMDS_MeshElement *elem = itelem->next();
12314 int shapeId = elem->getshapeId();
12315 int vtkId = elem->getVtkId();
12316 if (!shapeIdToVtkIdSet.count(shapeId))
12318 shapeIdToVtkIdSet[shapeId] = emptySet;
12319 shapeIds.insert(shapeId);
12321 shapeIdToVtkIdSet[shapeId].insert(vtkId);
12324 std::map<int, std::set<DownIdType, DownIdCompare> > shapeIdToEdges; // shapeId --> set of downward edges
12325 std::set<DownIdType, DownIdCompare> emptyEdges;
12326 emptyEdges.clear();
12328 std::map<int, std::set<int> >::iterator itShape = shapeIdToVtkIdSet.begin();
12329 for (; itShape != shapeIdToVtkIdSet.end(); ++itShape)
12331 int shapeId = itShape->first;
12332 //MESSAGE(" --- Shape ID --- "<< shapeId);
12333 shapeIdToEdges[shapeId] = emptyEdges;
12335 std::vector<int> nodesEdges;
12337 std::set<int>::iterator its = itShape->second.begin();
12338 for (; its != itShape->second.end(); ++its)
12341 //MESSAGE(" " << vtkId);
12342 int neighborsVtkIds[NBMAXNEIGHBORS];
12343 int downIds[NBMAXNEIGHBORS];
12344 unsigned char downTypes[NBMAXNEIGHBORS];
12345 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12346 for (int n = 0; n < nbNeighbors; n++)
12348 if (neighborsVtkIds[n]<0) // only smds faces are considered as neighbors here
12350 int smdsId = meshDS->fromVtkToSmds(neighborsVtkIds[n]);
12351 const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
12352 if ( shapeIds.count(elem->getshapeId()) && !sgrps->Contains(elem)) // edge : neighbor in the set of shape, not in the group
12354 DownIdType edge(downIds[n], downTypes[n]);
12355 if (!shapeIdToEdges[shapeId].count(edge))
12357 shapeIdToEdges[shapeId].insert(edge);
12359 int nbNodes = grid->getDownArray(downTypes[n])->getNodes(downIds[n],vtkNodeId);
12360 nodesEdges.push_back(vtkNodeId[0]);
12361 nodesEdges.push_back(vtkNodeId[nbNodes-1]);
12362 //MESSAGE(" --- nodes " << vtkNodeId[0]+1 << " " << vtkNodeId[nbNodes-1]+1);
12368 std::list<int> order;
12370 if (nodesEdges.size() > 0)
12372 order.push_back(nodesEdges[0]); //MESSAGE(" --- back " << order.back()+1); // SMDS id = VTK id + 1;
12373 nodesEdges[0] = -1;
12374 order.push_back(nodesEdges[1]); //MESSAGE(" --- back " << order.back()+1);
12375 nodesEdges[1] = -1; // do not reuse this edge
12379 int nodeTofind = order.back(); // try first to push back
12381 for ( i = 0; i < (int)nodesEdges.size(); i++ )
12382 if (nodesEdges[i] == nodeTofind)
12384 if ( i == (int) nodesEdges.size() )
12385 found = false; // no follower found on back
12388 if (i%2) // odd ==> use the previous one
12389 if (nodesEdges[i-1] < 0)
12393 order.push_back(nodesEdges[i-1]); //MESSAGE(" --- back " << order.back()+1);
12394 nodesEdges[i-1] = -1;
12396 else // even ==> use the next one
12397 if (nodesEdges[i+1] < 0)
12401 order.push_back(nodesEdges[i+1]); //MESSAGE(" --- back " << order.back()+1);
12402 nodesEdges[i+1] = -1;
12407 // try to push front
12409 nodeTofind = order.front(); // try to push front
12410 for ( i = 0; i < (int)nodesEdges.size(); i++ )
12411 if ( nodesEdges[i] == nodeTofind )
12413 if ( i == (int)nodesEdges.size() )
12415 found = false; // no predecessor found on front
12418 if (i%2) // odd ==> use the previous one
12419 if (nodesEdges[i-1] < 0)
12423 order.push_front(nodesEdges[i-1]); //MESSAGE(" --- front " << order.front()+1);
12424 nodesEdges[i-1] = -1;
12426 else // even ==> use the next one
12427 if (nodesEdges[i+1] < 0)
12431 order.push_front(nodesEdges[i+1]); //MESSAGE(" --- front " << order.front()+1);
12432 nodesEdges[i+1] = -1;
12438 std::vector<int> nodes;
12439 nodes.push_back(shapeId);
12440 std::list<int>::iterator itl = order.begin();
12441 for (; itl != order.end(); itl++)
12443 nodes.push_back((*itl) + 1); // SMDS id = VTK id + 1;
12444 //MESSAGE(" ordered node " << nodes[nodes.size()-1]);
12446 listOfListOfNodes.push_back(nodes);
12449 // partition geom faces with blocFissure
12450 // mesh blocFissure and geom faces of the skin (external wires given, triangle algo to choose)
12451 // mesh volume around blocFissure (skin triangles and quadrangle given, tetra algo to choose)
12457 //================================================================================
12459 * \brief Generates skin mesh (containing 2D cells) from 3D mesh
12460 * The created 2D mesh elements based on nodes of free faces of boundary volumes
12461 * \return TRUE if operation has been completed successfully, FALSE otherwise
12463 //================================================================================
12465 bool SMESH_MeshEditor::Make2DMeshFrom3D()
12467 // iterates on volume elements and detect all free faces on them
12468 SMESHDS_Mesh* aMesh = GetMeshDS();
12472 ElemFeatures faceType( SMDSAbs_Face );
12473 int nbFree = 0, nbExisted = 0, nbCreated = 0;
12474 SMDS_VolumeIteratorPtr vIt = aMesh->volumesIterator();
12477 const SMDS_MeshVolume* volume = vIt->next();
12478 SMDS_VolumeTool vTool( volume, /*ignoreCentralNodes=*/false );
12479 vTool.SetExternalNormal();
12480 const int iQuad = volume->IsQuadratic();
12481 faceType.SetQuad( iQuad );
12482 for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12484 if (!vTool.IsFreeFace(iface))
12487 vector<const SMDS_MeshNode *> nodes;
12488 int nbFaceNodes = vTool.NbFaceNodes(iface);
12489 const SMDS_MeshNode** faceNodes = vTool.GetFaceNodes(iface);
12491 for ( ; inode < nbFaceNodes; inode += iQuad+1)
12492 nodes.push_back(faceNodes[inode]);
12494 if (iQuad) // add medium nodes
12496 for ( inode = 1; inode < nbFaceNodes; inode += 2)
12497 nodes.push_back(faceNodes[inode]);
12498 if ( nbFaceNodes == 9 ) // bi-quadratic quad
12499 nodes.push_back(faceNodes[8]);
12501 // add new face based on volume nodes
12502 if (aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false) )
12504 nbExisted++; // face already exsist
12508 AddElement( nodes, faceType.SetPoly( nbFaceNodes/(iQuad+1) > 4 ));
12513 return ( nbFree == ( nbExisted + nbCreated ));
12518 inline const SMDS_MeshNode* getNodeWithSameID(SMESHDS_Mesh* mesh, const SMDS_MeshNode* node)
12520 if ( const SMDS_MeshNode* n = mesh->FindNode( node->GetID() ))
12522 return mesh->AddNodeWithID( node->X(),node->Y(),node->Z(), node->GetID() );
12525 //================================================================================
12527 * \brief Creates missing boundary elements
12528 * \param elements - elements whose boundary is to be checked
12529 * \param dimension - defines type of boundary elements to create
12530 * \param group - a group to store created boundary elements in
12531 * \param targetMesh - a mesh to store created boundary elements in
12532 * \param toCopyElements - if true, the checked elements will be copied into the targetMesh
12533 * \param toCopyExistingBoundary - if true, not only new but also pre-existing
12534 * boundary elements will be copied into the targetMesh
12535 * \param toAddExistingBondary - if true, not only new but also pre-existing
12536 * boundary elements will be added into the new group
12537 * \param aroundElements - if true, elements will be created on boundary of given
12538 * elements else, on boundary of the whole mesh.
12539 * \return nb of added boundary elements
12541 //================================================================================
12543 int SMESH_MeshEditor::MakeBoundaryMesh(const TIDSortedElemSet& elements,
12544 Bnd_Dimension dimension,
12545 SMESH_Group* group/*=0*/,
12546 SMESH_Mesh* targetMesh/*=0*/,
12547 bool toCopyElements/*=false*/,
12548 bool toCopyExistingBoundary/*=false*/,
12549 bool toAddExistingBondary/*= false*/,
12550 bool aroundElements/*= false*/)
12552 SMDSAbs_ElementType missType = (dimension == BND_2DFROM3D) ? SMDSAbs_Face : SMDSAbs_Edge;
12553 SMDSAbs_ElementType elemType = (dimension == BND_1DFROM2D) ? SMDSAbs_Face : SMDSAbs_Volume;
12554 // hope that all elements are of the same type, do not check them all
12555 if ( !elements.empty() && (*elements.begin())->GetType() != elemType )
12556 throw SALOME_Exception(LOCALIZED("wrong element type"));
12559 toCopyElements = toCopyExistingBoundary = false;
12561 SMESH_MeshEditor tgtEditor( targetMesh ? targetMesh : myMesh );
12562 SMESHDS_Mesh* aMesh = GetMeshDS(), *tgtMeshDS = tgtEditor.GetMeshDS();
12563 int nbAddedBnd = 0;
12565 // editor adding present bnd elements and optionally holding elements to add to the group
12566 SMESH_MeshEditor* presentEditor;
12567 SMESH_MeshEditor tgtEditor2( tgtEditor.GetMesh() );
12568 presentEditor = toAddExistingBondary ? &tgtEditor : &tgtEditor2;
12570 SMESH_MesherHelper helper( *myMesh );
12571 const TopAbs_ShapeEnum missShapeType = ( missType==SMDSAbs_Face ? TopAbs_FACE : TopAbs_EDGE );
12572 SMDS_VolumeTool vTool;
12573 TIDSortedElemSet avoidSet;
12574 const TIDSortedElemSet emptySet, *elemSet = aroundElements ? &elements : &emptySet;
12577 typedef vector<const SMDS_MeshNode*> TConnectivity;
12578 TConnectivity tgtNodes;
12579 ElemFeatures elemKind( missType ), elemToCopy;
12581 vector<const SMDS_MeshElement*> presentBndElems;
12582 vector<TConnectivity> missingBndElems;
12583 vector<int> freeFacets;
12584 TConnectivity nodes, elemNodes;
12586 SMDS_ElemIteratorPtr eIt;
12587 if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
12588 else eIt = elemSetIterator( elements );
12590 while (eIt->more())
12592 const SMDS_MeshElement* elem = eIt->next();
12593 const int iQuad = elem->IsQuadratic();
12594 elemKind.SetQuad( iQuad );
12596 // ------------------------------------------------------------------------------------
12597 // 1. For an elem, get present bnd elements and connectivities of missing bnd elements
12598 // ------------------------------------------------------------------------------------
12599 presentBndElems.clear();
12600 missingBndElems.clear();
12601 freeFacets.clear(); nodes.clear(); elemNodes.clear();
12602 if ( vTool.Set(elem, /*ignoreCentralNodes=*/true) ) // elem is a volume --------------
12604 const SMDS_MeshElement* otherVol = 0;
12605 for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12607 if ( !vTool.IsFreeFace(iface, &otherVol) &&
12608 ( !aroundElements || elements.count( otherVol )))
12610 freeFacets.push_back( iface );
12612 if ( missType == SMDSAbs_Face )
12613 vTool.SetExternalNormal();
12614 for ( size_t i = 0; i < freeFacets.size(); ++i )
12616 int iface = freeFacets[i];
12617 const SMDS_MeshNode** nn = vTool.GetFaceNodes(iface);
12618 const size_t nbFaceNodes = vTool.NbFaceNodes (iface);
12619 if ( missType == SMDSAbs_Edge ) // boundary edges
12621 nodes.resize( 2+iQuad );
12622 for ( size_t i = 0; i < nbFaceNodes; i += 1+iQuad )
12624 for ( size_t j = 0; j < nodes.size(); ++j )
12625 nodes[ j ] = nn[ i+j ];
12626 if ( const SMDS_MeshElement* edge =
12627 aMesh->FindElement( nodes, SMDSAbs_Edge, /*noMedium=*/false ))
12628 presentBndElems.push_back( edge );
12630 missingBndElems.push_back( nodes );
12633 else // boundary face
12636 for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12637 nodes.push_back( nn[inode] ); // add corner nodes
12639 for ( inode = 1; inode < nbFaceNodes; inode += 2)
12640 nodes.push_back( nn[inode] ); // add medium nodes
12641 int iCenter = vTool.GetCenterNodeIndex(iface); // for HEX27
12643 nodes.push_back( vTool.GetNodes()[ iCenter ] );
12645 if (const SMDS_MeshElement * f = aMesh->FindElement( nodes,
12646 SMDSAbs_Face, /*noMedium=*/false ))
12647 presentBndElems.push_back( f );
12649 missingBndElems.push_back( nodes );
12651 if ( targetMesh != myMesh )
12653 // add 1D elements on face boundary to be added to a new mesh
12654 const SMDS_MeshElement* edge;
12655 for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12658 edge = aMesh->FindEdge( nn[inode], nn[inode+1], nn[inode+2]);
12660 edge = aMesh->FindEdge( nn[inode], nn[inode+1]);
12661 if ( edge && avoidSet.insert( edge ).second )
12662 presentBndElems.push_back( edge );
12668 else if ( elem->GetType() == SMDSAbs_Face ) // elem is a face ------------------------
12670 avoidSet.clear(), avoidSet.insert( elem );
12671 elemNodes.assign( SMDS_MeshElement::iterator( elem->interlacedNodesElemIterator() ),
12672 SMDS_MeshElement::iterator() );
12673 elemNodes.push_back( elemNodes[0] );
12674 nodes.resize( 2 + iQuad );
12675 const int nbLinks = elem->NbCornerNodes();
12676 for ( int i = 0, iN = 0; i < nbLinks; i++, iN += 1+iQuad )
12678 nodes[0] = elemNodes[iN];
12679 nodes[1] = elemNodes[iN+1+iQuad];
12680 if ( SMESH_MeshAlgos::FindFaceInSet( nodes[0], nodes[1], *elemSet, avoidSet))
12681 continue; // not free link
12683 if ( iQuad ) nodes[2] = elemNodes[iN+1];
12684 if ( const SMDS_MeshElement* edge =
12685 aMesh->FindElement(nodes,SMDSAbs_Edge,/*noMedium=*/false))
12686 presentBndElems.push_back( edge );
12688 missingBndElems.push_back( nodes );
12692 // ---------------------------------
12693 // 2. Add missing boundary elements
12694 // ---------------------------------
12695 if ( targetMesh != myMesh )
12696 // instead of making a map of nodes in this mesh and targetMesh,
12697 // we create nodes with same IDs.
12698 for ( size_t i = 0; i < missingBndElems.size(); ++i )
12700 TConnectivity& srcNodes = missingBndElems[i];
12701 tgtNodes.resize( srcNodes.size() );
12702 for ( inode = 0; inode < srcNodes.size(); ++inode )
12703 tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, srcNodes[inode] );
12704 if ( aroundElements && tgtEditor.GetMeshDS()->FindElement( tgtNodes,
12706 /*noMedium=*/false))
12708 tgtEditor.AddElement( tgtNodes, elemKind.SetPoly( tgtNodes.size()/(iQuad+1) > 4 ));
12712 for ( size_t i = 0; i < missingBndElems.size(); ++i )
12714 TConnectivity& nodes = missingBndElems[ i ];
12715 if ( aroundElements && tgtEditor.GetMeshDS()->FindElement( nodes,
12717 /*noMedium=*/false))
12719 SMDS_MeshElement* newElem =
12720 tgtEditor.AddElement( nodes, elemKind.SetPoly( nodes.size()/(iQuad+1) > 4 ));
12721 nbAddedBnd += bool( newElem );
12723 // try to set a new element to a shape
12724 if ( myMesh->HasShapeToMesh() )
12727 set< pair<TopAbs_ShapeEnum, int > > mediumShapes;
12728 const size_t nbN = nodes.size() / (iQuad+1 );
12729 for ( inode = 0; inode < nbN && ok; ++inode )
12731 pair<int, TopAbs_ShapeEnum> i_stype =
12732 helper.GetMediumPos( nodes[inode], nodes[(inode+1)%nbN]);
12733 if (( ok = ( i_stype.first > 0 && i_stype.second >= TopAbs_FACE )))
12734 mediumShapes.insert( make_pair ( i_stype.second, i_stype.first ));
12736 if ( ok && mediumShapes.size() > 1 )
12738 set< pair<TopAbs_ShapeEnum, int > >::iterator stype_i = mediumShapes.begin();
12739 pair<TopAbs_ShapeEnum, int> stype_i_0 = *stype_i;
12740 for ( ++stype_i; stype_i != mediumShapes.end() && ok; ++stype_i )
12742 if (( ok = ( stype_i->first != stype_i_0.first )))
12743 ok = helper.IsSubShape( aMesh->IndexToShape( stype_i->second ),
12744 aMesh->IndexToShape( stype_i_0.second ));
12747 if ( ok && mediumShapes.begin()->first == missShapeType )
12748 aMesh->SetMeshElementOnShape( newElem, mediumShapes.begin()->second );
12752 // ----------------------------------
12753 // 3. Copy present boundary elements
12754 // ----------------------------------
12755 if ( toCopyExistingBoundary )
12756 for ( size_t i = 0 ; i < presentBndElems.size(); ++i )
12758 const SMDS_MeshElement* e = presentBndElems[i];
12759 tgtNodes.resize( e->NbNodes() );
12760 for ( inode = 0; inode < tgtNodes.size(); ++inode )
12761 tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, e->GetNode(inode) );
12762 presentEditor->AddElement( tgtNodes, elemToCopy.Init( e ));
12764 else // store present elements to add them to a group
12765 for ( size_t i = 0 ; i < presentBndElems.size(); ++i )
12767 presentEditor->myLastCreatedElems.Append( presentBndElems[ i ]);
12770 } // loop on given elements
12772 // ---------------------------------------------
12773 // 4. Fill group with boundary elements
12774 // ---------------------------------------------
12777 if ( SMESHDS_Group* g = dynamic_cast<SMESHDS_Group*>( group->GetGroupDS() ))
12778 for ( int i = 0; i < tgtEditor.myLastCreatedElems.Size(); ++i )
12779 g->SMDSGroup().Add( tgtEditor.myLastCreatedElems( i+1 ));
12781 tgtEditor.myLastCreatedElems.Clear();
12782 tgtEditor2.myLastCreatedElems.Clear();
12784 // -----------------------
12785 // 5. Copy given elements
12786 // -----------------------
12787 if ( toCopyElements && targetMesh != myMesh )
12789 if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
12790 else eIt = elemSetIterator( elements );
12791 while (eIt->more())
12793 const SMDS_MeshElement* elem = eIt->next();
12794 tgtNodes.resize( elem->NbNodes() );
12795 for ( inode = 0; inode < tgtNodes.size(); ++inode )
12796 tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, elem->GetNode(inode) );
12797 tgtEditor.AddElement( tgtNodes, elemToCopy.Init( elem ));
12799 tgtEditor.myLastCreatedElems.Clear();
12805 //================================================================================
12807 * \brief Copy node position and set \a to node on the same geometry
12809 //================================================================================
12811 void SMESH_MeshEditor::copyPosition( const SMDS_MeshNode* from,
12812 const SMDS_MeshNode* to )
12814 if ( !from || !to ) return;
12816 SMDS_PositionPtr pos = from->GetPosition();
12817 if ( !pos || from->getshapeId() < 1 ) return;
12819 switch ( pos->GetTypeOfPosition() )
12821 case SMDS_TOP_3DSPACE: break;
12823 case SMDS_TOP_FACE:
12825 const SMDS_FacePosition* fPos = static_cast< const SMDS_FacePosition* >( pos );
12826 GetMeshDS()->SetNodeOnFace( to, from->getshapeId(),
12827 fPos->GetUParameter(), fPos->GetVParameter() );
12830 case SMDS_TOP_EDGE:
12832 // WARNING: it is dangerous to set equal nodes on one EDGE!!!!!!!!
12833 const SMDS_EdgePosition* ePos = static_cast< const SMDS_EdgePosition* >( pos );
12834 GetMeshDS()->SetNodeOnEdge( to, from->getshapeId(), ePos->GetUParameter() );
12837 case SMDS_TOP_VERTEX:
12839 GetMeshDS()->SetNodeOnVertex( to, from->getshapeId() );
12842 case SMDS_TOP_UNSPEC: