1 // Copyright (C) 2007-2015 CEA/DEN, EDF R&D, OPEN CASCADE
3 // Copyright (C) 2003-2007 OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN,
4 // CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS
6 // This library is free software; you can redistribute it and/or
7 // modify it under the terms of the GNU Lesser General Public
8 // License as published by the Free Software Foundation; either
9 // version 2.1 of the License, or (at your option) any later version.
11 // This library is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 // Lesser General Public License for more details.
16 // You should have received a copy of the GNU Lesser General Public
17 // License along with this library; if not, write to the Free Software
18 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
23 // File : SMESH_MeshEditor.cxx
24 // Created : Mon Apr 12 16:10:22 2004
25 // Author : Edward AGAPOV (eap)
27 #include "SMESH_MeshEditor.hxx"
29 #include "SMDS_FaceOfNodes.hxx"
30 #include "SMDS_VolumeTool.hxx"
31 #include "SMDS_EdgePosition.hxx"
32 #include "SMDS_FacePosition.hxx"
33 #include "SMDS_SpacePosition.hxx"
34 #include "SMDS_MeshGroup.hxx"
35 #include "SMDS_LinearEdge.hxx"
36 #include "SMDS_Downward.hxx"
37 #include "SMDS_SetIterator.hxx"
39 #include "SMESHDS_Group.hxx"
40 #include "SMESHDS_Mesh.hxx"
42 #include "SMESH_Algo.hxx"
43 #include "SMESH_ControlsDef.hxx"
44 #include "SMESH_Group.hxx"
45 #include "SMESH_MeshAlgos.hxx"
46 #include "SMESH_MesherHelper.hxx"
47 #include "SMESH_OctreeNode.hxx"
48 #include "SMESH_subMesh.hxx"
50 #include <Basics_OCCTVersion.hxx>
52 #include "utilities.h"
55 #include <BRepAdaptor_Surface.hxx>
56 #include <BRepBuilderAPI_MakeEdge.hxx>
57 #include <BRepClass3d_SolidClassifier.hxx>
58 #include <BRep_Tool.hxx>
60 #include <Extrema_GenExtPS.hxx>
61 #include <Extrema_POnCurv.hxx>
62 #include <Extrema_POnSurf.hxx>
63 #include <Geom2d_Curve.hxx>
64 #include <GeomAdaptor_Surface.hxx>
65 #include <Geom_Curve.hxx>
66 #include <Geom_Surface.hxx>
67 #include <Precision.hxx>
68 #include <TColStd_ListOfInteger.hxx>
69 #include <TopAbs_State.hxx>
71 #include <TopExp_Explorer.hxx>
72 #include <TopTools_ListIteratorOfListOfShape.hxx>
73 #include <TopTools_ListOfShape.hxx>
74 #include <TopTools_SequenceOfShape.hxx>
76 #include <TopoDS_Face.hxx>
77 #include <TopoDS_Solid.hxx>
83 #include <gp_Trsf.hxx>
97 #include <boost/tuple/tuple.hpp>
99 #include <Standard_Failure.hxx>
100 #include <Standard_ErrorHandler.hxx>
102 #define cast2Node(elem) static_cast<const SMDS_MeshNode*>( elem )
105 using namespace SMESH::Controls;
109 template < class ELEM_SET >
110 SMDS_ElemIteratorPtr elemSetIterator( const ELEM_SET& elements )
112 typedef SMDS_SetIterator
113 < SMDS_pElement, typename ELEM_SET::const_iterator> TSetIterator;
114 return SMDS_ElemIteratorPtr( new TSetIterator( elements.begin(), elements.end() ));
118 //=======================================================================
119 //function : SMESH_MeshEditor
121 //=======================================================================
123 SMESH_MeshEditor::SMESH_MeshEditor( SMESH_Mesh* theMesh )
124 :myMesh( theMesh ) // theMesh may be NULL
128 //================================================================================
130 * \brief Clears myLastCreatedNodes and myLastCreatedElems
132 //================================================================================
134 void SMESH_MeshEditor::ClearLastCreated()
136 myLastCreatedNodes.Clear();
137 myLastCreatedElems.Clear();
140 //================================================================================
142 * \brief Initializes members by an existing element
143 * \param [in] elem - the source element
144 * \param [in] basicOnly - if true, does not set additional data of Ball and Polyhedron
146 //================================================================================
148 SMESH_MeshEditor::ElemFeatures&
149 SMESH_MeshEditor::ElemFeatures::Init( const SMDS_MeshElement* elem, bool basicOnly )
153 myType = elem->GetType();
154 if ( myType == SMDSAbs_Face || myType == SMDSAbs_Volume )
156 myIsPoly = elem->IsPoly();
159 myIsQuad = elem->IsQuadratic();
160 if ( myType == SMDSAbs_Volume && !basicOnly )
162 vector<int > quant = static_cast<const SMDS_VtkVolume* >( elem )->GetQuantities();
163 myPolyhedQuantities.swap( quant );
167 else if ( myType == SMDSAbs_Ball && !basicOnly )
169 myBallDiameter = static_cast<const SMDS_BallElement*>(elem)->GetDiameter();
175 //=======================================================================
179 //=======================================================================
182 SMESH_MeshEditor::AddElement(const vector<const SMDS_MeshNode*> & node,
183 const ElemFeatures& features)
185 SMDS_MeshElement* e = 0;
186 int nbnode = node.size();
187 SMESHDS_Mesh* mesh = GetMeshDS();
188 const int ID = features.myID;
190 switch ( features.myType ) {
192 if ( !features.myIsPoly ) {
194 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], ID);
195 else e = mesh->AddFace (node[0], node[1], node[2] );
197 else if (nbnode == 4) {
198 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3], ID);
199 else e = mesh->AddFace (node[0], node[1], node[2], node[3] );
201 else if (nbnode == 6) {
202 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
203 node[4], node[5], ID);
204 else e = mesh->AddFace (node[0], node[1], node[2], node[3],
207 else if (nbnode == 7) {
208 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
209 node[4], node[5], node[6], ID);
210 else e = mesh->AddFace (node[0], node[1], node[2], node[3],
211 node[4], node[5], node[6] );
213 else if (nbnode == 8) {
214 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
215 node[4], node[5], node[6], node[7], ID);
216 else e = mesh->AddFace (node[0], node[1], node[2], node[3],
217 node[4], node[5], node[6], node[7] );
219 else if (nbnode == 9) {
220 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
221 node[4], node[5], node[6], node[7], node[8], ID);
222 else e = mesh->AddFace (node[0], node[1], node[2], node[3],
223 node[4], node[5], node[6], node[7], node[8] );
226 else if ( !features.myIsQuad )
228 if ( ID >= 1 ) e = mesh->AddPolygonalFaceWithID(node, ID);
229 else e = mesh->AddPolygonalFace (node );
231 else if ( nbnode % 2 == 0 ) // just a protection
233 if ( ID >= 1 ) e = mesh->AddQuadPolygonalFaceWithID(node, ID);
234 else e = mesh->AddQuadPolygonalFace (node );
239 if ( !features.myIsPoly ) {
241 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3], ID);
242 else e = mesh->AddVolume (node[0], node[1], node[2], node[3] );
244 else if (nbnode == 5) {
245 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
247 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
250 else if (nbnode == 6) {
251 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
252 node[4], node[5], ID);
253 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
256 else if (nbnode == 8) {
257 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
258 node[4], node[5], node[6], node[7], ID);
259 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
260 node[4], node[5], node[6], node[7] );
262 else if (nbnode == 10) {
263 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
264 node[4], node[5], node[6], node[7],
265 node[8], node[9], ID);
266 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
267 node[4], node[5], node[6], node[7],
270 else if (nbnode == 12) {
271 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
272 node[4], node[5], node[6], node[7],
273 node[8], node[9], node[10], node[11], ID);
274 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
275 node[4], node[5], node[6], node[7],
276 node[8], node[9], node[10], node[11] );
278 else if (nbnode == 13) {
279 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
280 node[4], node[5], node[6], node[7],
281 node[8], node[9], node[10],node[11],
283 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
284 node[4], node[5], node[6], node[7],
285 node[8], node[9], node[10],node[11],
288 else if (nbnode == 15) {
289 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
290 node[4], node[5], node[6], node[7],
291 node[8], node[9], node[10],node[11],
292 node[12],node[13],node[14],ID);
293 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
294 node[4], node[5], node[6], node[7],
295 node[8], node[9], node[10],node[11],
296 node[12],node[13],node[14] );
298 else if (nbnode == 20) {
299 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
300 node[4], node[5], node[6], node[7],
301 node[8], node[9], node[10],node[11],
302 node[12],node[13],node[14],node[15],
303 node[16],node[17],node[18],node[19],ID);
304 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
305 node[4], node[5], node[6], node[7],
306 node[8], node[9], node[10],node[11],
307 node[12],node[13],node[14],node[15],
308 node[16],node[17],node[18],node[19] );
310 else if (nbnode == 27) {
311 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
312 node[4], node[5], node[6], node[7],
313 node[8], node[9], node[10],node[11],
314 node[12],node[13],node[14],node[15],
315 node[16],node[17],node[18],node[19],
316 node[20],node[21],node[22],node[23],
317 node[24],node[25],node[26], ID);
318 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
319 node[4], node[5], node[6], node[7],
320 node[8], node[9], node[10],node[11],
321 node[12],node[13],node[14],node[15],
322 node[16],node[17],node[18],node[19],
323 node[20],node[21],node[22],node[23],
324 node[24],node[25],node[26] );
327 else if ( !features.myIsQuad )
329 if ( ID >= 1 ) e = mesh->AddPolyhedralVolumeWithID(node, features.myPolyhedQuantities, ID);
330 else e = mesh->AddPolyhedralVolume (node, features.myPolyhedQuantities );
334 // if ( ID >= 1 ) e = mesh->AddQuadPolyhedralVolumeWithID(node, features.myPolyhedQuantities,ID);
335 // else e = mesh->AddQuadPolyhedralVolume (node, features.myPolyhedQuantities );
341 if ( ID >= 1 ) e = mesh->AddEdgeWithID(node[0], node[1], ID);
342 else e = mesh->AddEdge (node[0], node[1] );
344 else if ( nbnode == 3 ) {
345 if ( ID >= 1 ) e = mesh->AddEdgeWithID(node[0], node[1], node[2], ID);
346 else e = mesh->AddEdge (node[0], node[1], node[2] );
350 case SMDSAbs_0DElement:
352 if ( ID >= 1 ) e = mesh->Add0DElementWithID(node[0], ID);
353 else e = mesh->Add0DElement (node[0] );
358 if ( ID >= 1 ) e = mesh->AddNodeWithID(node[0]->X(), node[0]->Y(), node[0]->Z(), ID);
359 else e = mesh->AddNode (node[0]->X(), node[0]->Y(), node[0]->Z() );
363 if ( ID >= 1 ) e = mesh->AddBallWithID(node[0], features.myBallDiameter, ID);
364 else e = mesh->AddBall (node[0], features.myBallDiameter );
369 if ( e ) myLastCreatedElems.Append( e );
373 //=======================================================================
377 //=======================================================================
379 SMDS_MeshElement* SMESH_MeshEditor::AddElement(const vector<int> & nodeIDs,
380 const ElemFeatures& features)
382 vector<const SMDS_MeshNode*> nodes;
383 nodes.reserve( nodeIDs.size() );
384 vector<int>::const_iterator id = nodeIDs.begin();
385 while ( id != nodeIDs.end() ) {
386 if ( const SMDS_MeshNode* node = GetMeshDS()->FindNode( *id++ ))
387 nodes.push_back( node );
391 return AddElement( nodes, features );
394 //=======================================================================
396 //purpose : Remove a node or an element.
397 // Modify a compute state of sub-meshes which become empty
398 //=======================================================================
400 int SMESH_MeshEditor::Remove (const list< int >& theIDs,
403 myLastCreatedElems.Clear();
404 myLastCreatedNodes.Clear();
406 SMESHDS_Mesh* aMesh = GetMeshDS();
407 set< SMESH_subMesh *> smmap;
410 list<int>::const_iterator it = theIDs.begin();
411 for ( ; it != theIDs.end(); it++ ) {
412 const SMDS_MeshElement * elem;
414 elem = aMesh->FindNode( *it );
416 elem = aMesh->FindElement( *it );
420 // Notify VERTEX sub-meshes about modification
422 const SMDS_MeshNode* node = cast2Node( elem );
423 if ( node->GetPosition()->GetTypeOfPosition() == SMDS_TOP_VERTEX )
424 if ( int aShapeID = node->getshapeId() )
425 if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( aShapeID ) )
428 // Find sub-meshes to notify about modification
429 // SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
430 // while ( nodeIt->more() ) {
431 // const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
432 // const SMDS_PositionPtr& aPosition = node->GetPosition();
433 // if ( aPosition.get() ) {
434 // if ( int aShapeID = aPosition->GetShapeId() ) {
435 // if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( aShapeID ) )
436 // smmap.insert( sm );
443 aMesh->RemoveNode( static_cast< const SMDS_MeshNode* >( elem ));
445 aMesh->RemoveElement( elem );
449 // Notify sub-meshes about modification
450 if ( !smmap.empty() ) {
451 set< SMESH_subMesh *>::iterator smIt;
452 for ( smIt = smmap.begin(); smIt != smmap.end(); smIt++ )
453 (*smIt)->ComputeStateEngine( SMESH_subMesh::MESH_ENTITY_REMOVED );
456 // // Check if the whole mesh becomes empty
457 // if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( 1 ) )
458 // sm->ComputeStateEngine( SMESH_subMesh::CHECK_COMPUTE_STATE );
463 //================================================================================
465 * \brief Create 0D elements on all nodes of the given object except those
466 * nodes on which a 0D element already exists.
467 * \param elements - Elements on whose nodes to create 0D elements; if empty,
468 * the all mesh is treated
469 * \param all0DElems - returns all 0D elements found or created on nodes of \a elements
471 //================================================================================
473 void SMESH_MeshEditor::Create0DElementsOnAllNodes( const TIDSortedElemSet& elements,
474 TIDSortedElemSet& all0DElems )
476 SMDS_ElemIteratorPtr elemIt;
477 vector< const SMDS_MeshElement* > allNodes;
478 if ( elements.empty() )
480 allNodes.reserve( GetMeshDS()->NbNodes() );
481 elemIt = GetMeshDS()->elementsIterator( SMDSAbs_Node );
482 while ( elemIt->more() )
483 allNodes.push_back( elemIt->next() );
485 elemIt = elemSetIterator( allNodes );
489 elemIt = elemSetIterator( elements );
492 while ( elemIt->more() )
494 const SMDS_MeshElement* e = elemIt->next();
495 SMDS_ElemIteratorPtr nodeIt = e->nodesIterator();
496 while ( nodeIt->more() )
498 const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
499 SMDS_ElemIteratorPtr it0D = n->GetInverseElementIterator( SMDSAbs_0DElement );
501 all0DElems.insert( it0D->next() );
503 myLastCreatedElems.Append( GetMeshDS()->Add0DElement( n ));
504 all0DElems.insert( myLastCreatedElems.Last() );
510 //=======================================================================
511 //function : FindShape
512 //purpose : Return an index of the shape theElem is on
513 // or zero if a shape not found
514 //=======================================================================
516 int SMESH_MeshEditor::FindShape (const SMDS_MeshElement * theElem)
518 myLastCreatedElems.Clear();
519 myLastCreatedNodes.Clear();
521 SMESHDS_Mesh * aMesh = GetMeshDS();
522 if ( aMesh->ShapeToMesh().IsNull() )
525 int aShapeID = theElem->getshapeId();
529 if ( SMESHDS_SubMesh * sm = aMesh->MeshElements( aShapeID ))
530 if ( sm->Contains( theElem ))
533 if ( theElem->GetType() == SMDSAbs_Node ) {
534 MESSAGE( ":( Error: invalid myShapeId of node " << theElem->GetID() );
537 MESSAGE( ":( Error: invalid myShapeId of element " << theElem->GetID() );
540 TopoDS_Shape aShape; // the shape a node of theElem is on
541 if ( theElem->GetType() != SMDSAbs_Node )
543 SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator();
544 while ( nodeIt->more() ) {
545 const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
546 if ((aShapeID = node->getshapeId()) > 0) {
547 if ( SMESHDS_SubMesh * sm = aMesh->MeshElements( aShapeID ) ) {
548 if ( sm->Contains( theElem ))
550 if ( aShape.IsNull() )
551 aShape = aMesh->IndexToShape( aShapeID );
557 // None of nodes is on a proper shape,
558 // find the shape among ancestors of aShape on which a node is
559 if ( !aShape.IsNull() ) {
560 TopTools_ListIteratorOfListOfShape ancIt( GetMesh()->GetAncestors( aShape ));
561 for ( ; ancIt.More(); ancIt.Next() ) {
562 SMESHDS_SubMesh * sm = aMesh->MeshElements( ancIt.Value() );
563 if ( sm && sm->Contains( theElem ))
564 return aMesh->ShapeToIndex( ancIt.Value() );
569 SMESHDS_SubMeshIteratorPtr smIt = GetMeshDS()->SubMeshes();
570 while ( const SMESHDS_SubMesh* sm = smIt->next() )
571 if ( sm->Contains( theElem ))
578 //=======================================================================
579 //function : IsMedium
581 //=======================================================================
583 bool SMESH_MeshEditor::IsMedium(const SMDS_MeshNode* node,
584 const SMDSAbs_ElementType typeToCheck)
586 bool isMedium = false;
587 SMDS_ElemIteratorPtr it = node->GetInverseElementIterator(typeToCheck);
588 while (it->more() && !isMedium ) {
589 const SMDS_MeshElement* elem = it->next();
590 isMedium = elem->IsMediumNode(node);
595 //=======================================================================
596 //function : shiftNodesQuadTria
597 //purpose : Shift nodes in the array corresponded to quadratic triangle
598 // example: (0,1,2,3,4,5) -> (1,2,0,4,5,3)
599 //=======================================================================
601 static void shiftNodesQuadTria(vector< const SMDS_MeshNode* >& aNodes)
603 const SMDS_MeshNode* nd1 = aNodes[0];
604 aNodes[0] = aNodes[1];
605 aNodes[1] = aNodes[2];
607 const SMDS_MeshNode* nd2 = aNodes[3];
608 aNodes[3] = aNodes[4];
609 aNodes[4] = aNodes[5];
613 //=======================================================================
614 //function : nbEdgeConnectivity
615 //purpose : return number of the edges connected with the theNode.
616 // if theEdges has connections with the other type of the
617 // elements, return -1
618 //=======================================================================
620 static int nbEdgeConnectivity(const SMDS_MeshNode* theNode)
622 // SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator();
624 // while(elemIt->more()) {
629 return theNode->NbInverseElements();
632 //=======================================================================
633 //function : getNodesFromTwoTria
635 //=======================================================================
637 static bool getNodesFromTwoTria(const SMDS_MeshElement * theTria1,
638 const SMDS_MeshElement * theTria2,
639 vector< const SMDS_MeshNode*>& N1,
640 vector< const SMDS_MeshNode*>& N2)
642 N1.assign( theTria1->begin_nodes(), theTria1->end_nodes() );
643 if ( N1.size() < 6 ) return false;
644 N2.assign( theTria2->begin_nodes(), theTria2->end_nodes() );
645 if ( N2.size() < 6 ) return false;
647 int sames[3] = {-1,-1,-1};
659 if(nbsames!=2) return false;
661 shiftNodesQuadTria(N1);
663 shiftNodesQuadTria(N1);
666 i = sames[0] + sames[1] + sames[2];
668 shiftNodesQuadTria(N2);
670 // now we receive following N1 and N2 (using numeration as in the image below)
671 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
672 // i.e. first nodes from both arrays form a new diagonal
676 //=======================================================================
677 //function : InverseDiag
678 //purpose : Replace two neighbour triangles with ones built on the same 4 nodes
679 // but having other common link.
680 // Return False if args are improper
681 //=======================================================================
683 bool SMESH_MeshEditor::InverseDiag (const SMDS_MeshElement * theTria1,
684 const SMDS_MeshElement * theTria2 )
686 MESSAGE("InverseDiag");
687 myLastCreatedElems.Clear();
688 myLastCreatedNodes.Clear();
690 if (!theTria1 || !theTria2)
693 const SMDS_VtkFace* F1 = dynamic_cast<const SMDS_VtkFace*>( theTria1 );
694 if (!F1) return false;
695 const SMDS_VtkFace* F2 = dynamic_cast<const SMDS_VtkFace*>( theTria2 );
696 if (!F2) return false;
697 if ((theTria1->GetEntityType() == SMDSEntity_Triangle) &&
698 (theTria2->GetEntityType() == SMDSEntity_Triangle)) {
700 // 1 +--+ A theTria1: ( 1 A B ) A->2 ( 1 2 B ) 1 +--+ A
701 // | /| theTria2: ( B A 2 ) B->1 ( 1 A 2 ) |\ |
705 // put nodes in array and find out indices of the same ones
706 const SMDS_MeshNode* aNodes [6];
707 int sameInd [] = { -1, -1, -1, -1, -1, -1 };
709 SMDS_ElemIteratorPtr it = theTria1->nodesIterator();
710 while ( it->more() ) {
711 aNodes[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
713 if ( i > 2 ) // theTria2
714 // find same node of theTria1
715 for ( int j = 0; j < 3; j++ )
716 if ( aNodes[ i ] == aNodes[ j ]) {
725 return false; // theTria1 is not a triangle
726 it = theTria2->nodesIterator();
728 if ( i == 6 && it->more() )
729 return false; // theTria2 is not a triangle
732 // find indices of 1,2 and of A,B in theTria1
733 int iA = -1, iB = 0, i1 = 0, i2 = 0;
734 for ( i = 0; i < 6; i++ ) {
735 if ( sameInd [ i ] == -1 ) {
740 if ( iA >= 0) iB = i;
744 // nodes 1 and 2 should not be the same
745 if ( aNodes[ i1 ] == aNodes[ i2 ] )
749 aNodes[ iA ] = aNodes[ i2 ];
751 aNodes[ sameInd[ iB ]] = aNodes[ i1 ];
753 GetMeshDS()->ChangeElementNodes( theTria1, aNodes, 3 );
754 GetMeshDS()->ChangeElementNodes( theTria2, &aNodes[ 3 ], 3 );
758 } // end if(F1 && F2)
760 // check case of quadratic faces
761 if (theTria1->GetEntityType() != SMDSEntity_Quad_Triangle &&
762 theTria1->GetEntityType() != SMDSEntity_BiQuad_Triangle)
764 if (theTria2->GetEntityType() != SMDSEntity_Quad_Triangle&&
765 theTria2->GetEntityType() != SMDSEntity_BiQuad_Triangle)
769 // 1 +--+--+ 2 theTria1: (1 2 4 5 9 7) or (2 4 1 9 7 5) or (4 1 2 7 5 9)
770 // | /| theTria2: (2 3 4 6 8 9) or (3 4 2 8 9 6) or (4 2 3 9 6 8)
778 vector< const SMDS_MeshNode* > N1;
779 vector< const SMDS_MeshNode* > N2;
780 if(!getNodesFromTwoTria(theTria1,theTria2,N1,N2))
782 // now we receive following N1 and N2 (using numeration as above image)
783 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
784 // i.e. first nodes from both arrays determ new diagonal
786 vector< const SMDS_MeshNode*> N1new( N1.size() );
787 vector< const SMDS_MeshNode*> N2new( N2.size() );
788 N1new.back() = N1.back(); // central node of biquadratic
789 N2new.back() = N2.back();
790 N1new[0] = N1[0]; N2new[0] = N1[0];
791 N1new[1] = N2[0]; N2new[1] = N1[1];
792 N1new[2] = N2[1]; N2new[2] = N2[0];
793 N1new[3] = N1[4]; N2new[3] = N1[3];
794 N1new[4] = N2[3]; N2new[4] = N2[5];
795 N1new[5] = N1[5]; N2new[5] = N1[4];
796 // change nodes in faces
797 GetMeshDS()->ChangeElementNodes( theTria1, &N1new[0], N1new.size() );
798 GetMeshDS()->ChangeElementNodes( theTria2, &N2new[0], N2new.size() );
800 // move the central node of biquadratic triangle
801 SMESH_MesherHelper helper( *GetMesh() );
802 for ( int is2nd = 0; is2nd < 2; ++is2nd )
804 const SMDS_MeshElement* tria = is2nd ? theTria2 : theTria1;
805 vector< const SMDS_MeshNode*>& nodes = is2nd ? N2new : N1new;
806 if ( nodes.size() < 7 )
808 helper.SetSubShape( tria->getshapeId() );
809 const TopoDS_Face& F = TopoDS::Face( helper.GetSubShape() );
813 xyz = ( SMESH_TNodeXYZ( nodes[3] ) +
814 SMESH_TNodeXYZ( nodes[4] ) +
815 SMESH_TNodeXYZ( nodes[5] )) / 3.;
820 gp_XY uv = ( helper.GetNodeUV( F, nodes[3], nodes[2], &checkUV ) +
821 helper.GetNodeUV( F, nodes[4], nodes[0], &checkUV ) +
822 helper.GetNodeUV( F, nodes[5], nodes[1], &checkUV )) / 3.;
824 Handle(Geom_Surface) S = BRep_Tool::Surface(F,loc);
825 xyz = S->Value( uv.X(), uv.Y() );
826 xyz.Transform( loc );
827 if ( nodes[6]->GetPosition()->GetTypeOfPosition() == SMDS_TOP_FACE && // set UV
828 nodes[6]->getshapeId() > 0 )
829 GetMeshDS()->SetNodeOnFace( nodes[6], nodes[6]->getshapeId(), uv.X(), uv.Y() );
831 GetMeshDS()->MoveNode( nodes[6], xyz.X(), xyz.Y(), xyz.Z() );
836 //=======================================================================
837 //function : findTriangles
838 //purpose : find triangles sharing theNode1-theNode2 link
839 //=======================================================================
841 static bool findTriangles(const SMDS_MeshNode * theNode1,
842 const SMDS_MeshNode * theNode2,
843 const SMDS_MeshElement*& theTria1,
844 const SMDS_MeshElement*& theTria2)
846 if ( !theNode1 || !theNode2 ) return false;
848 theTria1 = theTria2 = 0;
850 set< const SMDS_MeshElement* > emap;
851 SMDS_ElemIteratorPtr it = theNode1->GetInverseElementIterator(SMDSAbs_Face);
853 const SMDS_MeshElement* elem = it->next();
854 if ( elem->NbCornerNodes() == 3 )
857 it = theNode2->GetInverseElementIterator(SMDSAbs_Face);
859 const SMDS_MeshElement* elem = it->next();
860 if ( emap.count( elem )) {
868 // theTria1 must be element with minimum ID
869 if ( theTria2->GetID() < theTria1->GetID() )
870 std::swap( theTria2, theTria1 );
878 //=======================================================================
879 //function : InverseDiag
880 //purpose : Replace two neighbour triangles sharing theNode1-theNode2 link
881 // with ones built on the same 4 nodes but having other common link.
882 // Return false if proper faces not found
883 //=======================================================================
885 bool SMESH_MeshEditor::InverseDiag (const SMDS_MeshNode * theNode1,
886 const SMDS_MeshNode * theNode2)
888 myLastCreatedElems.Clear();
889 myLastCreatedNodes.Clear();
891 MESSAGE( "::InverseDiag()" );
893 const SMDS_MeshElement *tr1, *tr2;
894 if ( !findTriangles( theNode1, theNode2, tr1, tr2 ))
897 const SMDS_VtkFace* F1 = dynamic_cast<const SMDS_VtkFace*>( tr1 );
898 if (!F1) return false;
899 const SMDS_VtkFace* F2 = dynamic_cast<const SMDS_VtkFace*>( tr2 );
900 if (!F2) return false;
901 if ((tr1->GetEntityType() == SMDSEntity_Triangle) &&
902 (tr2->GetEntityType() == SMDSEntity_Triangle)) {
904 // 1 +--+ A tr1: ( 1 A B ) A->2 ( 1 2 B ) 1 +--+ A
905 // | /| tr2: ( B A 2 ) B->1 ( 1 A 2 ) |\ |
909 // put nodes in array
910 // and find indices of 1,2 and of A in tr1 and of B in tr2
911 int i, iA1 = 0, i1 = 0;
912 const SMDS_MeshNode* aNodes1 [3];
913 SMDS_ElemIteratorPtr it;
914 for (i = 0, it = tr1->nodesIterator(); it->more(); i++ ) {
915 aNodes1[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
916 if ( aNodes1[ i ] == theNode1 )
917 iA1 = i; // node A in tr1
918 else if ( aNodes1[ i ] != theNode2 )
922 const SMDS_MeshNode* aNodes2 [3];
923 for (i = 0, it = tr2->nodesIterator(); it->more(); i++ ) {
924 aNodes2[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
925 if ( aNodes2[ i ] == theNode2 )
926 iB2 = i; // node B in tr2
927 else if ( aNodes2[ i ] != theNode1 )
931 // nodes 1 and 2 should not be the same
932 if ( aNodes1[ i1 ] == aNodes2[ i2 ] )
936 aNodes1[ iA1 ] = aNodes2[ i2 ];
938 aNodes2[ iB2 ] = aNodes1[ i1 ];
940 GetMeshDS()->ChangeElementNodes( tr1, aNodes1, 3 );
941 GetMeshDS()->ChangeElementNodes( tr2, aNodes2, 3 );
946 // check case of quadratic faces
947 return InverseDiag(tr1,tr2);
950 //=======================================================================
951 //function : getQuadrangleNodes
952 //purpose : fill theQuadNodes - nodes of a quadrangle resulting from
953 // fusion of triangles tr1 and tr2 having shared link on
954 // theNode1 and theNode2
955 //=======================================================================
957 bool getQuadrangleNodes(const SMDS_MeshNode * theQuadNodes [],
958 const SMDS_MeshNode * theNode1,
959 const SMDS_MeshNode * theNode2,
960 const SMDS_MeshElement * tr1,
961 const SMDS_MeshElement * tr2 )
963 if( tr1->NbNodes() != tr2->NbNodes() )
965 // find the 4-th node to insert into tr1
966 const SMDS_MeshNode* n4 = 0;
967 SMDS_ElemIteratorPtr it = tr2->nodesIterator();
969 while ( !n4 && i<3 ) {
970 const SMDS_MeshNode * n = cast2Node( it->next() );
972 bool isDiag = ( n == theNode1 || n == theNode2 );
976 // Make an array of nodes to be in a quadrangle
977 int iNode = 0, iFirstDiag = -1;
978 it = tr1->nodesIterator();
981 const SMDS_MeshNode * n = cast2Node( it->next() );
983 bool isDiag = ( n == theNode1 || n == theNode2 );
985 if ( iFirstDiag < 0 )
987 else if ( iNode - iFirstDiag == 1 )
988 theQuadNodes[ iNode++ ] = n4; // insert the 4-th node between diagonal nodes
990 else if ( n == n4 ) {
991 return false; // tr1 and tr2 should not have all the same nodes
993 theQuadNodes[ iNode++ ] = n;
995 if ( iNode == 3 ) // diagonal nodes have 0 and 2 indices
996 theQuadNodes[ iNode ] = n4;
1001 //=======================================================================
1002 //function : DeleteDiag
1003 //purpose : Replace two neighbour triangles sharing theNode1-theNode2 link
1004 // with a quadrangle built on the same 4 nodes.
1005 // Return false if proper faces not found
1006 //=======================================================================
1008 bool SMESH_MeshEditor::DeleteDiag (const SMDS_MeshNode * theNode1,
1009 const SMDS_MeshNode * theNode2)
1011 myLastCreatedElems.Clear();
1012 myLastCreatedNodes.Clear();
1014 MESSAGE( "::DeleteDiag()" );
1016 const SMDS_MeshElement *tr1, *tr2;
1017 if ( !findTriangles( theNode1, theNode2, tr1, tr2 ))
1020 const SMDS_VtkFace* F1 = dynamic_cast<const SMDS_VtkFace*>( tr1 );
1021 if (!F1) return false;
1022 const SMDS_VtkFace* F2 = dynamic_cast<const SMDS_VtkFace*>( tr2 );
1023 if (!F2) return false;
1024 SMESHDS_Mesh * aMesh = GetMeshDS();
1026 if ((tr1->GetEntityType() == SMDSEntity_Triangle) &&
1027 (tr2->GetEntityType() == SMDSEntity_Triangle)) {
1029 const SMDS_MeshNode* aNodes [ 4 ];
1030 if ( ! getQuadrangleNodes( aNodes, theNode1, theNode2, tr1, tr2 ))
1033 const SMDS_MeshElement* newElem = 0;
1034 newElem = aMesh->AddFace( aNodes[0], aNodes[1], aNodes[2], aNodes[3] );
1035 myLastCreatedElems.Append(newElem);
1036 AddToSameGroups( newElem, tr1, aMesh );
1037 int aShapeId = tr1->getshapeId();
1040 aMesh->SetMeshElementOnShape( newElem, aShapeId );
1042 aMesh->RemoveElement( tr1 );
1043 aMesh->RemoveElement( tr2 );
1048 // check case of quadratic faces
1049 if (tr1->GetEntityType() != SMDSEntity_Quad_Triangle)
1051 if (tr2->GetEntityType() != SMDSEntity_Quad_Triangle)
1055 // 1 +--+--+ 2 tr1: (1 2 4 5 9 7) or (2 4 1 9 7 5) or (4 1 2 7 5 9)
1056 // | /| tr2: (2 3 4 6 8 9) or (3 4 2 8 9 6) or (4 2 3 9 6 8)
1064 vector< const SMDS_MeshNode* > N1;
1065 vector< const SMDS_MeshNode* > N2;
1066 if(!getNodesFromTwoTria(tr1,tr2,N1,N2))
1068 // now we receive following N1 and N2 (using numeration as above image)
1069 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
1070 // i.e. first nodes from both arrays determ new diagonal
1072 const SMDS_MeshNode* aNodes[8];
1082 const SMDS_MeshElement* newElem = 0;
1083 newElem = aMesh->AddFace( aNodes[0], aNodes[1], aNodes[2], aNodes[3],
1084 aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
1085 myLastCreatedElems.Append(newElem);
1086 AddToSameGroups( newElem, tr1, aMesh );
1087 int aShapeId = tr1->getshapeId();
1090 aMesh->SetMeshElementOnShape( newElem, aShapeId );
1092 aMesh->RemoveElement( tr1 );
1093 aMesh->RemoveElement( tr2 );
1095 // remove middle node (9)
1096 GetMeshDS()->RemoveNode( N1[4] );
1101 //=======================================================================
1102 //function : Reorient
1103 //purpose : Reverse theElement orientation
1104 //=======================================================================
1106 bool SMESH_MeshEditor::Reorient (const SMDS_MeshElement * theElem)
1108 MESSAGE("Reorient");
1109 myLastCreatedElems.Clear();
1110 myLastCreatedNodes.Clear();
1114 SMDS_ElemIteratorPtr it = theElem->nodesIterator();
1115 if ( !it || !it->more() )
1118 const SMDSAbs_ElementType type = theElem->GetType();
1119 if ( type < SMDSAbs_Edge || type > SMDSAbs_Volume )
1122 const SMDSAbs_EntityType geomType = theElem->GetEntityType();
1123 if ( geomType == SMDSEntity_Polyhedra ) // polyhedron
1125 const SMDS_VtkVolume* aPolyedre =
1126 dynamic_cast<const SMDS_VtkVolume*>( theElem );
1128 MESSAGE("Warning: bad volumic element");
1131 const int nbFaces = aPolyedre->NbFaces();
1132 vector<const SMDS_MeshNode *> poly_nodes;
1133 vector<int> quantities (nbFaces);
1135 // reverse each face of the polyedre
1136 for (int iface = 1; iface <= nbFaces; iface++) {
1137 int inode, nbFaceNodes = aPolyedre->NbFaceNodes(iface);
1138 quantities[iface - 1] = nbFaceNodes;
1140 for (inode = nbFaceNodes; inode >= 1; inode--) {
1141 const SMDS_MeshNode* curNode = aPolyedre->GetFaceNode(iface, inode);
1142 poly_nodes.push_back(curNode);
1145 return GetMeshDS()->ChangePolyhedronNodes( theElem, poly_nodes, quantities );
1147 else // other elements
1149 vector<const SMDS_MeshNode*> nodes( theElem->begin_nodes(), theElem->end_nodes() );
1150 const std::vector<int>& interlace = SMDS_MeshCell::reverseSmdsOrder( geomType, nodes.size() );
1151 if ( interlace.empty() )
1153 std::reverse( nodes.begin(), nodes.end() ); // obsolete, just in case
1157 SMDS_MeshCell::applyInterlace( interlace, nodes );
1159 return GetMeshDS()->ChangeElementNodes( theElem, &nodes[0], nodes.size() );
1164 //================================================================================
1166 * \brief Reorient faces.
1167 * \param theFaces - the faces to reorient. If empty the whole mesh is meant
1168 * \param theDirection - desired direction of normal of \a theFace
1169 * \param theFace - one of \a theFaces that sould be oriented according to
1170 * \a theDirection and whose orientation defines orientation of other faces
1171 * \return number of reoriented faces.
1173 //================================================================================
1175 int SMESH_MeshEditor::Reorient2D (TIDSortedElemSet & theFaces,
1176 const gp_Dir& theDirection,
1177 const SMDS_MeshElement * theFace)
1180 if ( !theFace || theFace->GetType() != SMDSAbs_Face ) return nbReori;
1182 if ( theFaces.empty() )
1184 SMDS_FaceIteratorPtr fIt = GetMeshDS()->facesIterator(/*idInceasingOrder=*/true);
1185 while ( fIt->more() )
1186 theFaces.insert( theFaces.end(), fIt->next() );
1189 // orient theFace according to theDirection
1191 SMESH_MeshAlgos::FaceNormal( theFace, normal, /*normalized=*/false );
1192 if ( normal * theDirection.XYZ() < 0 )
1193 nbReori += Reorient( theFace );
1195 // Orient other faces
1197 set< const SMDS_MeshElement* > startFaces, visitedFaces;
1198 TIDSortedElemSet avoidSet;
1199 set< SMESH_TLink > checkedLinks;
1200 pair< set< SMESH_TLink >::iterator, bool > linkIt_isNew;
1202 if ( theFaces.size() > 1 )// leave 1 face to prevent finding not selected faces
1203 theFaces.erase( theFace );
1204 startFaces.insert( theFace );
1206 int nodeInd1, nodeInd2;
1207 const SMDS_MeshElement* otherFace;
1208 vector< const SMDS_MeshElement* > facesNearLink;
1209 vector< std::pair< int, int > > nodeIndsOfFace;
1211 set< const SMDS_MeshElement* >::iterator startFace = startFaces.begin();
1212 while ( !startFaces.empty() )
1214 startFace = startFaces.begin();
1215 theFace = *startFace;
1216 startFaces.erase( startFace );
1217 if ( !visitedFaces.insert( theFace ).second )
1221 avoidSet.insert(theFace);
1223 NLink link( theFace->GetNode( 0 ), (SMDS_MeshNode *) 0 );
1225 const int nbNodes = theFace->NbCornerNodes();
1226 for ( int i = 0; i < nbNodes; ++i ) // loop on links of theFace
1228 link.second = theFace->GetNode(( i+1 ) % nbNodes );
1229 linkIt_isNew = checkedLinks.insert( link );
1230 if ( !linkIt_isNew.second )
1232 // link has already been checked and won't be encountered more
1233 // if the group (theFaces) is manifold
1234 //checkedLinks.erase( linkIt_isNew.first );
1238 facesNearLink.clear();
1239 nodeIndsOfFace.clear();
1240 while (( otherFace = SMESH_MeshAlgos::FindFaceInSet( link.first, link.second,
1242 &nodeInd1, &nodeInd2 )))
1243 if ( otherFace != theFace)
1245 facesNearLink.push_back( otherFace );
1246 nodeIndsOfFace.push_back( make_pair( nodeInd1, nodeInd2 ));
1247 avoidSet.insert( otherFace );
1249 if ( facesNearLink.size() > 1 )
1251 // NON-MANIFOLD mesh shell !
1252 // select a face most co-directed with theFace,
1253 // other faces won't be visited this time
1255 SMESH_MeshAlgos::FaceNormal( theFace, NF, /*normalized=*/false );
1256 double proj, maxProj = -1;
1257 for ( size_t i = 0; i < facesNearLink.size(); ++i ) {
1258 SMESH_MeshAlgos::FaceNormal( facesNearLink[i], NOF, /*normalized=*/false );
1259 if (( proj = Abs( NF * NOF )) > maxProj ) {
1261 otherFace = facesNearLink[i];
1262 nodeInd1 = nodeIndsOfFace[i].first;
1263 nodeInd2 = nodeIndsOfFace[i].second;
1266 // not to visit rejected faces
1267 for ( size_t i = 0; i < facesNearLink.size(); ++i )
1268 if ( facesNearLink[i] != otherFace && theFaces.size() > 1 )
1269 visitedFaces.insert( facesNearLink[i] );
1271 else if ( facesNearLink.size() == 1 )
1273 otherFace = facesNearLink[0];
1274 nodeInd1 = nodeIndsOfFace.back().first;
1275 nodeInd2 = nodeIndsOfFace.back().second;
1277 if ( otherFace && otherFace != theFace)
1279 // link must be reverse in otherFace if orientation ot otherFace
1280 // is same as that of theFace
1281 if ( abs(nodeInd2-nodeInd1) == 1 ? nodeInd2 > nodeInd1 : nodeInd1 > nodeInd2 )
1283 nbReori += Reorient( otherFace );
1285 startFaces.insert( otherFace );
1288 std::swap( link.first, link.second ); // reverse the link
1294 //================================================================================
1296 * \brief Reorient faces basing on orientation of adjacent volumes.
1297 * \param theFaces - faces to reorient. If empty, all mesh faces are treated.
1298 * \param theVolumes - reference volumes.
1299 * \param theOutsideNormal - to orient faces to have their normal
1300 * pointing either \a outside or \a inside the adjacent volumes.
1301 * \return number of reoriented faces.
1303 //================================================================================
1305 int SMESH_MeshEditor::Reorient2DBy3D (TIDSortedElemSet & theFaces,
1306 TIDSortedElemSet & theVolumes,
1307 const bool theOutsideNormal)
1311 SMDS_ElemIteratorPtr faceIt;
1312 if ( theFaces.empty() )
1313 faceIt = GetMeshDS()->elementsIterator( SMDSAbs_Face );
1315 faceIt = elemSetIterator( theFaces );
1317 vector< const SMDS_MeshNode* > faceNodes;
1318 TIDSortedElemSet checkedVolumes;
1319 set< const SMDS_MeshNode* > faceNodesSet;
1320 SMDS_VolumeTool volumeTool;
1322 while ( faceIt->more() ) // loop on given faces
1324 const SMDS_MeshElement* face = faceIt->next();
1325 if ( face->GetType() != SMDSAbs_Face )
1328 const int nbCornersNodes = face->NbCornerNodes();
1329 faceNodes.assign( face->begin_nodes(), face->end_nodes() );
1331 checkedVolumes.clear();
1332 SMDS_ElemIteratorPtr vIt = faceNodes[ 0 ]->GetInverseElementIterator( SMDSAbs_Volume );
1333 while ( vIt->more() )
1335 const SMDS_MeshElement* volume = vIt->next();
1337 if ( !checkedVolumes.insert( volume ).second )
1339 if ( !theVolumes.empty() && !theVolumes.count( volume ))
1342 // is volume adjacent?
1343 bool allNodesCommon = true;
1344 for ( int iN = 1; iN < nbCornersNodes && allNodesCommon; ++iN )
1345 allNodesCommon = ( volume->GetNodeIndex( faceNodes[ iN ]) > -1 );
1346 if ( !allNodesCommon )
1349 // get nodes of a corresponding volume facet
1350 faceNodesSet.clear();
1351 faceNodesSet.insert( faceNodes.begin(), faceNodes.end() );
1352 volumeTool.Set( volume );
1353 int facetID = volumeTool.GetFaceIndex( faceNodesSet );
1354 if ( facetID < 0 ) continue;
1355 volumeTool.SetExternalNormal();
1356 const SMDS_MeshNode** facetNodes = volumeTool.GetFaceNodes( facetID );
1358 // compare order of faceNodes and facetNodes
1359 const int iQ = 1 + ( nbCornersNodes < faceNodes.size() );
1361 for ( int i = 0; i < 2; ++i )
1363 const SMDS_MeshNode* n = facetNodes[ i*iQ ];
1364 for ( int iN = 0; iN < nbCornersNodes; ++iN )
1365 if ( faceNodes[ iN ] == n )
1371 bool isOutside = Abs( iNN[0]-iNN[1] ) == 1 ? iNN[0] < iNN[1] : iNN[0] > iNN[1];
1372 if ( isOutside != theOutsideNormal )
1373 nbReori += Reorient( face );
1375 } // loop on given faces
1380 //=======================================================================
1381 //function : getBadRate
1383 //=======================================================================
1385 static double getBadRate (const SMDS_MeshElement* theElem,
1386 SMESH::Controls::NumericalFunctorPtr& theCrit)
1388 SMESH::Controls::TSequenceOfXYZ P;
1389 if ( !theElem || !theCrit->GetPoints( theElem, P ))
1391 return theCrit->GetBadRate( theCrit->GetValue( P ), theElem->NbNodes() );
1392 //return theCrit->GetBadRate( theCrit->GetValue( theElem->GetID() ), theElem->NbNodes() );
1395 //=======================================================================
1396 //function : QuadToTri
1397 //purpose : Cut quadrangles into triangles.
1398 // theCrit is used to select a diagonal to cut
1399 //=======================================================================
1401 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet & theElems,
1402 SMESH::Controls::NumericalFunctorPtr theCrit)
1404 myLastCreatedElems.Clear();
1405 myLastCreatedNodes.Clear();
1407 if ( !theCrit.get() )
1410 SMESHDS_Mesh * aMesh = GetMeshDS();
1412 Handle(Geom_Surface) surface;
1413 SMESH_MesherHelper helper( *GetMesh() );
1415 TIDSortedElemSet::iterator itElem;
1416 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
1418 const SMDS_MeshElement* elem = *itElem;
1419 if ( !elem || elem->GetType() != SMDSAbs_Face )
1421 if ( elem->NbCornerNodes() != 4 )
1424 // retrieve element nodes
1425 vector< const SMDS_MeshNode* > aNodes( elem->begin_nodes(), elem->end_nodes() );
1427 // compare two sets of possible triangles
1428 double aBadRate1, aBadRate2; // to what extent a set is bad
1429 SMDS_FaceOfNodes tr1 ( aNodes[0], aNodes[1], aNodes[2] );
1430 SMDS_FaceOfNodes tr2 ( aNodes[2], aNodes[3], aNodes[0] );
1431 aBadRate1 = getBadRate( &tr1, theCrit ) + getBadRate( &tr2, theCrit );
1433 SMDS_FaceOfNodes tr3 ( aNodes[1], aNodes[2], aNodes[3] );
1434 SMDS_FaceOfNodes tr4 ( aNodes[3], aNodes[0], aNodes[1] );
1435 aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit );
1437 const int aShapeId = FindShape( elem );
1438 const SMDS_MeshElement* newElem1 = 0;
1439 const SMDS_MeshElement* newElem2 = 0;
1441 if ( !elem->IsQuadratic() ) // split liner quadrangle
1443 // for MaxElementLength2D functor we return minimum diagonal for splitting,
1444 // because aBadRate1=2*len(diagonal 1-3); aBadRate2=2*len(diagonal 2-4)
1445 if ( aBadRate1 <= aBadRate2 ) {
1446 // tr1 + tr2 is better
1447 newElem1 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
1448 newElem2 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
1451 // tr3 + tr4 is better
1452 newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
1453 newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
1456 else // split quadratic quadrangle
1458 helper.SetIsQuadratic( true );
1459 helper.SetIsBiQuadratic( aNodes.size() == 9 );
1461 helper.AddTLinks( static_cast< const SMDS_MeshFace* >( elem ));
1462 if ( aNodes.size() == 9 )
1464 helper.SetIsBiQuadratic( true );
1465 if ( aBadRate1 <= aBadRate2 )
1466 helper.AddTLinkNode( aNodes[0], aNodes[2], aNodes[8] );
1468 helper.AddTLinkNode( aNodes[1], aNodes[3], aNodes[8] );
1470 // create a new element
1471 if ( aBadRate1 <= aBadRate2 ) {
1472 newElem1 = helper.AddFace( aNodes[2], aNodes[3], aNodes[0] );
1473 newElem2 = helper.AddFace( aNodes[2], aNodes[0], aNodes[1] );
1476 newElem1 = helper.AddFace( aNodes[3], aNodes[0], aNodes[1] );
1477 newElem2 = helper.AddFace( aNodes[3], aNodes[1], aNodes[2] );
1481 // care of a new element
1483 myLastCreatedElems.Append(newElem1);
1484 myLastCreatedElems.Append(newElem2);
1485 AddToSameGroups( newElem1, elem, aMesh );
1486 AddToSameGroups( newElem2, elem, aMesh );
1488 // put a new triangle on the same shape
1490 aMesh->SetMeshElementOnShape( newElem1, aShapeId );
1491 aMesh->SetMeshElementOnShape( newElem2, aShapeId );
1493 aMesh->RemoveElement( elem );
1498 //=======================================================================
1500 * \brief Split each of given quadrangles into 4 triangles.
1501 * \param theElems - The faces to be splitted. If empty all faces are split.
1503 //=======================================================================
1505 void SMESH_MeshEditor::QuadTo4Tri (TIDSortedElemSet & theElems)
1507 myLastCreatedElems.Clear();
1508 myLastCreatedNodes.Clear();
1510 SMESH_MesherHelper helper( *GetMesh() );
1511 helper.SetElementsOnShape( true );
1513 SMDS_ElemIteratorPtr faceIt;
1514 if ( theElems.empty() ) faceIt = GetMeshDS()->elementsIterator(SMDSAbs_Face);
1515 else faceIt = elemSetIterator( theElems );
1518 gp_XY uv [9]; uv[8] = gp_XY(0,0);
1520 vector< const SMDS_MeshNode* > nodes;
1521 SMESHDS_SubMesh* subMeshDS;
1523 Handle(Geom_Surface) surface;
1524 TopLoc_Location loc;
1526 while ( faceIt->more() )
1528 const SMDS_MeshElement* quad = faceIt->next();
1529 if ( !quad || quad->NbCornerNodes() != 4 )
1532 // get a surface the quad is on
1534 if ( quad->getshapeId() < 1 )
1537 helper.SetSubShape( 0 );
1540 else if ( quad->getshapeId() != helper.GetSubShapeID() )
1542 helper.SetSubShape( quad->getshapeId() );
1543 if ( !helper.GetSubShape().IsNull() &&
1544 helper.GetSubShape().ShapeType() == TopAbs_FACE )
1546 F = TopoDS::Face( helper.GetSubShape() );
1547 surface = BRep_Tool::Surface( F, loc );
1548 subMeshDS = GetMeshDS()->MeshElements( quad->getshapeId() );
1552 helper.SetSubShape( 0 );
1557 // create a central node
1559 const SMDS_MeshNode* nCentral;
1560 nodes.assign( quad->begin_nodes(), quad->end_nodes() );
1562 if ( nodes.size() == 9 )
1564 nCentral = nodes.back();
1571 for ( ; iN < nodes.size(); ++iN )
1572 xyz[ iN ] = SMESH_TNodeXYZ( nodes[ iN ] );
1574 for ( ; iN < 8; ++iN ) // mid-side points of a linear qudrangle
1575 xyz[ iN ] = 0.5 * ( xyz[ iN - 4 ] + xyz[( iN - 3 )%4 ] );
1577 xyz[ 8 ] = helper.calcTFI( 0.5, 0.5,
1578 xyz[0], xyz[1], xyz[2], xyz[3],
1579 xyz[4], xyz[5], xyz[6], xyz[7] );
1583 for ( ; iN < nodes.size(); ++iN )
1584 uv[ iN ] = helper.GetNodeUV( F, nodes[iN], nodes[(iN+2)%4], &checkUV );
1586 for ( ; iN < 8; ++iN ) // UV of mid-side points of a linear qudrangle
1587 uv[ iN ] = helper.GetMiddleUV( surface, uv[ iN - 4 ], uv[( iN - 3 )%4 ] );
1589 uv[ 8 ] = helper.calcTFI( 0.5, 0.5,
1590 uv[0], uv[1], uv[2], uv[3],
1591 uv[4], uv[5], uv[6], uv[7] );
1593 gp_Pnt p = surface->Value( uv[8].X(), uv[8].Y() ).Transformed( loc );
1597 nCentral = helper.AddNode( xyz[8].X(), xyz[8].Y(), xyz[8].Z(), /*id=*/0,
1598 uv[8].X(), uv[8].Y() );
1599 myLastCreatedNodes.Append( nCentral );
1602 // create 4 triangles
1604 GetMeshDS()->RemoveFreeElement( quad, subMeshDS, /*fromGroups=*/false );
1606 helper.SetIsQuadratic ( nodes.size() > 4 );
1607 helper.SetIsBiQuadratic( nodes.size() == 9 );
1608 if ( helper.GetIsQuadratic() )
1609 helper.AddTLinks( static_cast< const SMDS_MeshFace*>( quad ));
1611 for ( int i = 0; i < 4; ++i )
1613 SMDS_MeshElement* tria = helper.AddFace( nodes[ i ],
1616 ReplaceElemInGroups( tria, quad, GetMeshDS() );
1617 myLastCreatedElems.Append( tria );
1622 //=======================================================================
1623 //function : BestSplit
1624 //purpose : Find better diagonal for cutting.
1625 //=======================================================================
1627 int SMESH_MeshEditor::BestSplit (const SMDS_MeshElement* theQuad,
1628 SMESH::Controls::NumericalFunctorPtr theCrit)
1630 myLastCreatedElems.Clear();
1631 myLastCreatedNodes.Clear();
1636 if (!theQuad || theQuad->GetType() != SMDSAbs_Face )
1639 if( theQuad->NbNodes()==4 ||
1640 (theQuad->NbNodes()==8 && theQuad->IsQuadratic()) ) {
1642 // retrieve element nodes
1643 const SMDS_MeshNode* aNodes [4];
1644 SMDS_ElemIteratorPtr itN = theQuad->nodesIterator();
1646 //while (itN->more())
1648 aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
1650 // compare two sets of possible triangles
1651 double aBadRate1, aBadRate2; // to what extent a set is bad
1652 SMDS_FaceOfNodes tr1 ( aNodes[0], aNodes[1], aNodes[2] );
1653 SMDS_FaceOfNodes tr2 ( aNodes[2], aNodes[3], aNodes[0] );
1654 aBadRate1 = getBadRate( &tr1, theCrit ) + getBadRate( &tr2, theCrit );
1656 SMDS_FaceOfNodes tr3 ( aNodes[1], aNodes[2], aNodes[3] );
1657 SMDS_FaceOfNodes tr4 ( aNodes[3], aNodes[0], aNodes[1] );
1658 aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit );
1659 // for MaxElementLength2D functor we return minimum diagonal for splitting,
1660 // because aBadRate1=2*len(diagonal 1-3); aBadRate2=2*len(diagonal 2-4)
1661 if (aBadRate1 <= aBadRate2) // tr1 + tr2 is better
1662 return 1; // diagonal 1-3
1664 return 2; // diagonal 2-4
1671 // Methods of splitting volumes into tetra
1673 const int theHexTo5_1[5*4+1] =
1675 0, 1, 2, 5, 0, 4, 5, 7, 0, 2, 3, 7, 2, 5, 6, 7, 0, 5, 2, 7, -1
1677 const int theHexTo5_2[5*4+1] =
1679 1, 2, 3, 6, 1, 4, 5, 6, 0, 1, 3, 4, 3, 4, 6, 7, 1, 3, 4, 6, -1
1681 const int* theHexTo5[2] = { theHexTo5_1, theHexTo5_2 };
1683 const int theHexTo6_1[6*4+1] =
1685 1, 5, 6, 0, 0, 1, 2, 6, 0, 4, 5, 6, 0, 4, 6, 7, 0, 2, 3, 6, 0, 3, 7, 6, -1
1687 const int theHexTo6_2[6*4+1] =
1689 2, 6, 7, 1, 1, 2, 3, 7, 1, 5, 6, 7, 1, 5, 7, 4, 1, 3, 0, 7, 1, 0, 4, 7, -1
1691 const int theHexTo6_3[6*4+1] =
1693 3, 7, 4, 2, 2, 3, 0, 4, 2, 6, 7, 4, 2, 6, 4, 5, 2, 0, 1, 4, 2, 1, 5, 4, -1
1695 const int theHexTo6_4[6*4+1] =
1697 0, 4, 5, 3, 3, 0, 1, 5, 3, 7, 4, 5, 3, 7, 5, 6, 3, 1, 2, 5, 3, 2, 6, 5, -1
1699 const int* theHexTo6[4] = { theHexTo6_1, theHexTo6_2, theHexTo6_3, theHexTo6_4 };
1701 const int thePyraTo2_1[2*4+1] =
1703 0, 1, 2, 4, 0, 2, 3, 4, -1
1705 const int thePyraTo2_2[2*4+1] =
1707 1, 2, 3, 4, 1, 3, 0, 4, -1
1709 const int* thePyraTo2[2] = { thePyraTo2_1, thePyraTo2_2 };
1711 const int thePentaTo3_1[3*4+1] =
1713 0, 1, 2, 3, 1, 3, 4, 2, 2, 3, 4, 5, -1
1715 const int thePentaTo3_2[3*4+1] =
1717 1, 2, 0, 4, 2, 4, 5, 0, 0, 4, 5, 3, -1
1719 const int thePentaTo3_3[3*4+1] =
1721 2, 0, 1, 5, 0, 5, 3, 1, 1, 5, 3, 4, -1
1723 const int thePentaTo3_4[3*4+1] =
1725 0, 1, 2, 3, 1, 3, 4, 5, 2, 3, 1, 5, -1
1727 const int thePentaTo3_5[3*4+1] =
1729 1, 2, 0, 4, 2, 4, 5, 3, 0, 4, 2, 3, -1
1731 const int thePentaTo3_6[3*4+1] =
1733 2, 0, 1, 5, 0, 5, 3, 4, 1, 5, 0, 4, -1
1735 const int* thePentaTo3[6] = { thePentaTo3_1, thePentaTo3_2, thePentaTo3_3,
1736 thePentaTo3_4, thePentaTo3_5, thePentaTo3_6 };
1738 // Methods of splitting hexahedron into prisms
1740 const int theHexTo4Prisms_BT[6*4+1] = // bottom-top
1742 0, 1, 8, 4, 5, 9, 1, 2, 8, 5, 6, 9, 2, 3, 8, 6, 7, 9, 3, 0, 8, 7, 4, 9, -1
1744 const int theHexTo4Prisms_LR[6*4+1] = // left-right
1746 1, 0, 8, 2, 3, 9, 0, 4, 8, 3, 7, 9, 4, 5, 8, 7, 6, 9, 5, 1, 8, 6, 2, 9, -1
1748 const int theHexTo4Prisms_FB[6*4+1] = // front-back
1750 0, 3, 9, 1, 2, 8, 3, 7, 9, 2, 6, 8, 7, 4, 9, 6, 5, 8, 4, 0, 9, 5, 1, 8, -1
1753 const int theHexTo2Prisms_BT_1[6*2+1] =
1755 0, 1, 3, 4, 5, 7, 1, 2, 3, 5, 6, 7, -1
1757 const int theHexTo2Prisms_BT_2[6*2+1] =
1759 0, 1, 2, 4, 5, 6, 0, 2, 3, 4, 6, 7, -1
1761 const int* theHexTo2Prisms_BT[2] = { theHexTo2Prisms_BT_1, theHexTo2Prisms_BT_2 };
1763 const int theHexTo2Prisms_LR_1[6*2+1] =
1765 1, 0, 4, 2, 3, 7, 1, 4, 5, 2, 7, 6, -1
1767 const int theHexTo2Prisms_LR_2[6*2+1] =
1769 1, 0, 4, 2, 3, 7, 1, 4, 5, 2, 7, 6, -1
1771 const int* theHexTo2Prisms_LR[2] = { theHexTo2Prisms_LR_1, theHexTo2Prisms_LR_2 };
1773 const int theHexTo2Prisms_FB_1[6*2+1] =
1775 0, 3, 4, 1, 2, 5, 3, 7, 4, 2, 6, 5, -1
1777 const int theHexTo2Prisms_FB_2[6*2+1] =
1779 0, 3, 7, 1, 2, 7, 0, 7, 4, 1, 6, 5, -1
1781 const int* theHexTo2Prisms_FB[2] = { theHexTo2Prisms_FB_1, theHexTo2Prisms_FB_2 };
1784 struct TTriangleFacet //!< stores indices of three nodes of tetra facet
1787 TTriangleFacet(int n1, int n2, int n3): _n1(n1), _n2(n2), _n3(n3) {}
1788 bool contains(int n) const { return ( n == _n1 || n == _n2 || n == _n3 ); }
1789 bool hasAdjacentVol( const SMDS_MeshElement* elem,
1790 const SMDSAbs_GeometryType geom = SMDSGeom_TETRA) const;
1796 const int* _connectivity; //!< foursomes of tetra connectivy finished by -1
1797 bool _baryNode; //!< additional node is to be created at cell barycenter
1798 bool _ownConn; //!< to delete _connectivity in destructor
1799 map<int, const SMDS_MeshNode*> _faceBaryNode; //!< map face index to node at BC of face
1801 TSplitMethod( int nbTet=0, const int* conn=0, bool addNode=false)
1802 : _nbSplits(nbTet), _nbCorners(4), _connectivity(conn), _baryNode(addNode), _ownConn(false) {}
1803 ~TSplitMethod() { if ( _ownConn ) delete [] _connectivity; _connectivity = 0; }
1804 bool hasFacet( const TTriangleFacet& facet ) const
1806 if ( _nbCorners == 4 )
1808 const int* tetConn = _connectivity;
1809 for ( ; tetConn[0] >= 0; tetConn += 4 )
1810 if (( facet.contains( tetConn[0] ) +
1811 facet.contains( tetConn[1] ) +
1812 facet.contains( tetConn[2] ) +
1813 facet.contains( tetConn[3] )) == 3 )
1816 else // prism, _nbCorners == 6
1818 const int* prismConn = _connectivity;
1819 for ( ; prismConn[0] >= 0; prismConn += 6 )
1821 if (( facet.contains( prismConn[0] ) &&
1822 facet.contains( prismConn[1] ) &&
1823 facet.contains( prismConn[2] ))
1825 ( facet.contains( prismConn[3] ) &&
1826 facet.contains( prismConn[4] ) &&
1827 facet.contains( prismConn[5] )))
1835 //=======================================================================
1837 * \brief return TSplitMethod for the given element to split into tetrahedra
1839 //=======================================================================
1841 TSplitMethod getTetraSplitMethod( SMDS_VolumeTool& vol, const int theMethodFlags)
1843 const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
1845 // at HEXA_TO_24 method, each face of volume is split into triangles each based on
1846 // an edge and a face barycenter; tertaherdons are based on triangles and
1847 // a volume barycenter
1848 const bool is24TetMode = ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_24 );
1850 // Find out how adjacent volumes are split
1852 vector < list< TTriangleFacet > > triaSplitsByFace( vol.NbFaces() ); // splits of each side
1853 int hasAdjacentSplits = 0, maxTetConnSize = 0;
1854 for ( int iF = 0; iF < vol.NbFaces(); ++iF )
1856 int nbNodes = vol.NbFaceNodes( iF ) / iQ;
1857 maxTetConnSize += 4 * ( nbNodes - (is24TetMode ? 0 : 2));
1858 if ( nbNodes < 4 ) continue;
1860 list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
1861 const int* nInd = vol.GetFaceNodesIndices( iF );
1864 TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
1865 TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
1866 if ( t012.hasAdjacentVol( vol.Element() )) triaSplits.push_back( t012 );
1867 else if ( t123.hasAdjacentVol( vol.Element() )) triaSplits.push_back( t123 );
1871 int iCom = 0; // common node of triangle faces to split into
1872 for ( int iVar = 0; iVar < nbNodes; ++iVar, ++iCom )
1874 TTriangleFacet t012( nInd[ iQ * ( iCom )],
1875 nInd[ iQ * ( (iCom+1)%nbNodes )],
1876 nInd[ iQ * ( (iCom+2)%nbNodes )]);
1877 TTriangleFacet t023( nInd[ iQ * ( iCom )],
1878 nInd[ iQ * ( (iCom+2)%nbNodes )],
1879 nInd[ iQ * ( (iCom+3)%nbNodes )]);
1880 if ( t012.hasAdjacentVol( vol.Element() ) && t023.hasAdjacentVol( vol.Element() ))
1882 triaSplits.push_back( t012 );
1883 triaSplits.push_back( t023 );
1888 if ( !triaSplits.empty() )
1889 hasAdjacentSplits = true;
1892 // Among variants of split method select one compliant with adjacent volumes
1894 TSplitMethod method;
1895 if ( !vol.Element()->IsPoly() && !is24TetMode )
1897 int nbVariants = 2, nbTet = 0;
1898 const int** connVariants = 0;
1899 switch ( vol.Element()->GetEntityType() )
1901 case SMDSEntity_Hexa:
1902 case SMDSEntity_Quad_Hexa:
1903 case SMDSEntity_TriQuad_Hexa:
1904 if ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_5 )
1905 connVariants = theHexTo5, nbTet = 5;
1907 connVariants = theHexTo6, nbTet = 6, nbVariants = 4;
1909 case SMDSEntity_Pyramid:
1910 case SMDSEntity_Quad_Pyramid:
1911 connVariants = thePyraTo2; nbTet = 2;
1913 case SMDSEntity_Penta:
1914 case SMDSEntity_Quad_Penta:
1915 connVariants = thePentaTo3; nbTet = 3; nbVariants = 6;
1920 for ( int variant = 0; variant < nbVariants && method._nbSplits == 0; ++variant )
1922 // check method compliancy with adjacent tetras,
1923 // all found splits must be among facets of tetras described by this method
1924 method = TSplitMethod( nbTet, connVariants[variant] );
1925 if ( hasAdjacentSplits && method._nbSplits > 0 )
1927 bool facetCreated = true;
1928 for ( int iF = 0; facetCreated && iF < triaSplitsByFace.size(); ++iF )
1930 list< TTriangleFacet >::const_iterator facet = triaSplitsByFace[iF].begin();
1931 for ( ; facetCreated && facet != triaSplitsByFace[iF].end(); ++facet )
1932 facetCreated = method.hasFacet( *facet );
1934 if ( !facetCreated )
1935 method = TSplitMethod(0); // incompatible method
1939 if ( method._nbSplits < 1 )
1941 // No standard method is applicable, use a generic solution:
1942 // each facet of a volume is split into triangles and
1943 // each of triangles and a volume barycenter form a tetrahedron.
1945 const bool isHex27 = ( vol.Element()->GetEntityType() == SMDSEntity_TriQuad_Hexa );
1947 int* connectivity = new int[ maxTetConnSize + 1 ];
1948 method._connectivity = connectivity;
1949 method._ownConn = true;
1950 method._baryNode = !isHex27; // to create central node or not
1953 int baryCenInd = vol.NbNodes() - int( isHex27 );
1954 for ( int iF = 0; iF < vol.NbFaces(); ++iF )
1956 const int nbNodes = vol.NbFaceNodes( iF ) / iQ;
1957 const int* nInd = vol.GetFaceNodesIndices( iF );
1958 // find common node of triangle facets of tetra to create
1959 int iCommon = 0; // index in linear numeration
1960 const list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
1961 if ( !triaSplits.empty() )
1964 const TTriangleFacet* facet = &triaSplits.front();
1965 for ( ; iCommon < nbNodes-1 ; ++iCommon )
1966 if ( facet->contains( nInd[ iQ * iCommon ]) &&
1967 facet->contains( nInd[ iQ * ((iCommon+2)%nbNodes) ]))
1970 else if ( nbNodes > 3 && !is24TetMode )
1972 // find the best method of splitting into triangles by aspect ratio
1973 SMESH::Controls::NumericalFunctorPtr aspectRatio( new SMESH::Controls::AspectRatio);
1974 map< double, int > badness2iCommon;
1975 const SMDS_MeshNode** nodes = vol.GetFaceNodes( iF );
1976 int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
1977 for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCommon )
1980 for ( int iLast = iCommon+2; iLast < iCommon+nbNodes; ++iLast )
1982 SMDS_FaceOfNodes tria ( nodes[ iQ*( iCommon )],
1983 nodes[ iQ*((iLast-1)%nbNodes)],
1984 nodes[ iQ*((iLast )%nbNodes)]);
1985 badness += getBadRate( &tria, aspectRatio );
1987 badness2iCommon.insert( make_pair( badness, iCommon ));
1989 // use iCommon with lowest badness
1990 iCommon = badness2iCommon.begin()->second;
1992 if ( iCommon >= nbNodes )
1993 iCommon = 0; // something wrong
1995 // fill connectivity of tetrahedra based on a current face
1996 int nbTet = nbNodes - 2;
1997 if ( is24TetMode && nbNodes > 3 && triaSplits.empty())
2002 faceBaryCenInd = vol.GetCenterNodeIndex( iF );
2003 method._faceBaryNode[ iF ] = vol.GetNodes()[ faceBaryCenInd ];
2007 method._faceBaryNode[ iF ] = 0;
2008 faceBaryCenInd = baryCenInd + method._faceBaryNode.size();
2011 for ( int i = 0; i < nbTet; ++i )
2013 int i1 = i, i2 = (i+1) % nbNodes;
2014 if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
2015 connectivity[ connSize++ ] = nInd[ iQ * i1 ];
2016 connectivity[ connSize++ ] = nInd[ iQ * i2 ];
2017 connectivity[ connSize++ ] = faceBaryCenInd;
2018 connectivity[ connSize++ ] = baryCenInd;
2023 for ( int i = 0; i < nbTet; ++i )
2025 int i1 = (iCommon+1+i) % nbNodes, i2 = (iCommon+2+i) % nbNodes;
2026 if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
2027 connectivity[ connSize++ ] = nInd[ iQ * iCommon ];
2028 connectivity[ connSize++ ] = nInd[ iQ * i1 ];
2029 connectivity[ connSize++ ] = nInd[ iQ * i2 ];
2030 connectivity[ connSize++ ] = baryCenInd;
2033 method._nbSplits += nbTet;
2035 } // loop on volume faces
2037 connectivity[ connSize++ ] = -1;
2039 } // end of generic solution
2043 //=======================================================================
2045 * \brief return TSplitMethod to split haxhedron into prisms
2047 //=======================================================================
2049 TSplitMethod getPrismSplitMethod( SMDS_VolumeTool& vol,
2050 const int methodFlags,
2051 const int facetToSplit)
2053 // order of facets in HEX according to SMDS_VolumeTool::Hexa_F :
2055 const int iF = ( facetToSplit < 2 ) ? 0 : 1 + ( facetToSplit-2 ) % 2; // [0,1,2]
2057 if ( methodFlags == SMESH_MeshEditor::HEXA_TO_4_PRISMS )
2059 static TSplitMethod to4methods[4]; // order BT, LR, FB
2060 if ( to4methods[iF]._nbSplits == 0 )
2064 to4methods[iF]._connectivity = theHexTo4Prisms_BT;
2065 to4methods[iF]._faceBaryNode[ 0 ] = 0;
2066 to4methods[iF]._faceBaryNode[ 1 ] = 0;
2069 to4methods[iF]._connectivity = theHexTo4Prisms_LR;
2070 to4methods[iF]._faceBaryNode[ 2 ] = 0;
2071 to4methods[iF]._faceBaryNode[ 4 ] = 0;
2074 to4methods[iF]._connectivity = theHexTo4Prisms_FB;
2075 to4methods[iF]._faceBaryNode[ 3 ] = 0;
2076 to4methods[iF]._faceBaryNode[ 5 ] = 0;
2078 default: return to4methods[3];
2080 to4methods[iF]._nbSplits = 4;
2081 to4methods[iF]._nbCorners = 6;
2083 return to4methods[iF];
2085 // else if ( methodFlags == HEXA_TO_2_PRISMS )
2087 TSplitMethod method;
2089 const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
2091 const int nbVariants = 2, nbSplits = 2;
2092 const int** connVariants = 0;
2094 case 0: connVariants = theHexTo2Prisms_BT; break;
2095 case 1: connVariants = theHexTo2Prisms_LR; break;
2096 case 2: connVariants = theHexTo2Prisms_FB; break;
2097 default: return method;
2100 // look for prisms adjacent via facetToSplit and an opposite one
2101 for ( int is2nd = 0; is2nd < 2; ++is2nd )
2103 int iFacet = is2nd ? vol.GetOppFaceIndexOfHex( facetToSplit ) : facetToSplit;
2104 int nbNodes = vol.NbFaceNodes( iFacet ) / iQ;
2105 if ( nbNodes != 4 ) return method;
2107 const int* nInd = vol.GetFaceNodesIndices( iFacet );
2108 TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
2109 TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
2111 if ( t012.hasAdjacentVol( vol.Element(), SMDSGeom_PENTA ))
2113 else if ( t123.hasAdjacentVol( vol.Element(), SMDSGeom_PENTA ))
2118 // there are adjacent prism
2119 for ( int variant = 0; variant < nbVariants; ++variant )
2121 // check method compliancy with adjacent prisms,
2122 // the found prism facets must be among facets of prisms described by current method
2123 method._nbSplits = nbSplits;
2124 method._nbCorners = 6;
2125 method._connectivity = connVariants[ variant ];
2126 if ( method.hasFacet( *t ))
2131 // No adjacent prisms. Select a variant with a best aspect ratio.
2133 double badness[2] = { 0, 0 };
2134 static SMESH::Controls::NumericalFunctorPtr aspectRatio( new SMESH::Controls::AspectRatio);
2135 const SMDS_MeshNode** nodes = vol.GetNodes();
2136 for ( int variant = 0; variant < nbVariants; ++variant )
2137 for ( int is2nd = 0; is2nd < 2; ++is2nd )
2139 int iFacet = is2nd ? vol.GetOppFaceIndexOfHex( facetToSplit ) : facetToSplit;
2140 const int* nInd = vol.GetFaceNodesIndices( iFacet );
2142 method._connectivity = connVariants[ variant ];
2143 TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
2144 TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
2145 TTriangleFacet* t = ( method.hasFacet( t012 )) ? & t012 : & t123;
2147 SMDS_FaceOfNodes tria ( nodes[ t->_n1 ],
2150 badness[ variant ] += getBadRate( &tria, aspectRatio );
2152 const int iBetter = ( badness[1] < badness[0] && badness[0]-badness[1] > 0.1 * badness[0] );
2154 method._nbSplits = nbSplits;
2155 method._nbCorners = 6;
2156 method._connectivity = connVariants[ iBetter ];
2161 //================================================================================
2163 * \brief Check if there is a tetraherdon adjacent to the given element via this facet
2165 //================================================================================
2167 bool TTriangleFacet::hasAdjacentVol( const SMDS_MeshElement* elem,
2168 const SMDSAbs_GeometryType geom ) const
2170 // find the tetrahedron including the three nodes of facet
2171 const SMDS_MeshNode* n1 = elem->GetNode(_n1);
2172 const SMDS_MeshNode* n2 = elem->GetNode(_n2);
2173 const SMDS_MeshNode* n3 = elem->GetNode(_n3);
2174 SMDS_ElemIteratorPtr volIt1 = n1->GetInverseElementIterator(SMDSAbs_Volume);
2175 while ( volIt1->more() )
2177 const SMDS_MeshElement* v = volIt1->next();
2178 if ( v->GetGeomType() != geom )
2180 const int lastCornerInd = v->NbCornerNodes() - 1;
2181 if ( v->IsQuadratic() && v->GetNodeIndex( n1 ) > lastCornerInd )
2182 continue; // medium node not allowed
2183 const int ind2 = v->GetNodeIndex( n2 );
2184 if ( ind2 < 0 || lastCornerInd < ind2 )
2186 const int ind3 = v->GetNodeIndex( n3 );
2187 if ( ind3 < 0 || lastCornerInd < ind3 )
2194 //=======================================================================
2196 * \brief A key of a face of volume
2198 //=======================================================================
2200 struct TVolumeFaceKey: pair< pair< int, int>, pair< int, int> >
2202 TVolumeFaceKey( SMDS_VolumeTool& vol, int iF )
2204 TIDSortedNodeSet sortedNodes;
2205 const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
2206 int nbNodes = vol.NbFaceNodes( iF );
2207 const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iF );
2208 for ( int i = 0; i < nbNodes; i += iQ )
2209 sortedNodes.insert( fNodes[i] );
2210 TIDSortedNodeSet::iterator n = sortedNodes.begin();
2211 first.first = (*(n++))->GetID();
2212 first.second = (*(n++))->GetID();
2213 second.first = (*(n++))->GetID();
2214 second.second = ( sortedNodes.size() > 3 ) ? (*(n++))->GetID() : 0;
2219 //=======================================================================
2220 //function : SplitVolumes
2221 //purpose : Split volume elements into tetrahedra or prisms.
2222 // If facet ID < 0, element is split into tetrahedra,
2223 // else a hexahedron is split into prisms so that the given facet is
2224 // split into triangles
2225 //=======================================================================
2227 void SMESH_MeshEditor::SplitVolumes (const TFacetOfElem & theElems,
2228 const int theMethodFlags)
2230 SMDS_VolumeTool volTool;
2231 SMESH_MesherHelper helper( *GetMesh()), fHelper(*GetMesh());
2232 fHelper.ToFixNodeParameters( true );
2234 SMESHDS_SubMesh* subMesh = 0;//GetMeshDS()->MeshElements(1);
2235 SMESHDS_SubMesh* fSubMesh = 0;//subMesh;
2237 SMESH_SequenceOfElemPtr newNodes, newElems;
2239 // map face of volume to it's baricenrtic node
2240 map< TVolumeFaceKey, const SMDS_MeshNode* > volFace2BaryNode;
2243 TFacetOfElem::const_iterator elem2facet = theElems.begin();
2244 for ( ; elem2facet != theElems.end(); ++elem2facet )
2246 const SMDS_MeshElement* elem = elem2facet->first;
2247 const int facetToSplit = elem2facet->second;
2248 if ( elem->GetType() != SMDSAbs_Volume )
2250 const SMDSAbs_EntityType geomType = elem->GetEntityType();
2251 if ( geomType == SMDSEntity_Tetra || geomType == SMDSEntity_Quad_Tetra )
2254 if ( !volTool.Set( elem, /*ignoreCentralNodes=*/false )) continue; // strange...
2256 TSplitMethod splitMethod = ( facetToSplit < 0 ?
2257 getTetraSplitMethod( volTool, theMethodFlags ) :
2258 getPrismSplitMethod( volTool, theMethodFlags, facetToSplit ));
2259 if ( splitMethod._nbSplits < 1 ) continue;
2261 // find submesh to add new tetras to
2262 if ( !subMesh || !subMesh->Contains( elem ))
2264 int shapeID = FindShape( elem );
2265 helper.SetSubShape( shapeID ); // helper will add tetras to the found submesh
2266 subMesh = GetMeshDS()->MeshElements( shapeID );
2269 if ( elem->IsQuadratic() )
2272 // add quadratic links to the helper
2273 for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
2275 const SMDS_MeshNode** fNodes = volTool.GetFaceNodes( iF );
2276 int nbN = volTool.NbFaceNodes( iF ) - bool( volTool.GetCenterNodeIndex(iF) > 0 );
2277 for ( int iN = 0; iN < nbN; iN += iQ )
2278 helper.AddTLinkNode( fNodes[iN], fNodes[iN+2], fNodes[iN+1] );
2280 helper.SetIsQuadratic( true );
2285 helper.SetIsQuadratic( false );
2287 vector<const SMDS_MeshNode*> nodes( volTool.GetNodes(),
2288 volTool.GetNodes() + elem->NbNodes() );
2289 helper.SetElementsOnShape( true );
2290 if ( splitMethod._baryNode )
2292 // make a node at barycenter
2293 volTool.GetBaryCenter( bc[0], bc[1], bc[2] );
2294 SMDS_MeshNode* gcNode = helper.AddNode( bc[0], bc[1], bc[2] );
2295 nodes.push_back( gcNode );
2296 newNodes.Append( gcNode );
2298 if ( !splitMethod._faceBaryNode.empty() )
2300 // make or find baricentric nodes of faces
2301 map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.begin();
2302 for ( ; iF_n != splitMethod._faceBaryNode.end(); ++iF_n )
2304 map< TVolumeFaceKey, const SMDS_MeshNode* >::iterator f_n =
2305 volFace2BaryNode.insert
2306 ( make_pair( TVolumeFaceKey( volTool,iF_n->first ), iF_n->second )).first;
2309 volTool.GetFaceBaryCenter( iF_n->first, bc[0], bc[1], bc[2] );
2310 newNodes.Append( f_n->second = helper.AddNode( bc[0], bc[1], bc[2] ));
2312 nodes.push_back( iF_n->second = f_n->second );
2317 vector<const SMDS_MeshElement* > splitVols( splitMethod._nbSplits ); // splits of a volume
2318 const int* volConn = splitMethod._connectivity;
2319 if ( splitMethod._nbCorners == 4 ) // tetra
2320 for ( int i = 0; i < splitMethod._nbSplits; ++i, volConn += splitMethod._nbCorners )
2321 newElems.Append( splitVols[ i ] = helper.AddVolume( nodes[ volConn[0] ],
2322 nodes[ volConn[1] ],
2323 nodes[ volConn[2] ],
2324 nodes[ volConn[3] ]));
2326 for ( int i = 0; i < splitMethod._nbSplits; ++i, volConn += splitMethod._nbCorners )
2327 newElems.Append( splitVols[ i ] = helper.AddVolume( nodes[ volConn[0] ],
2328 nodes[ volConn[1] ],
2329 nodes[ volConn[2] ],
2330 nodes[ volConn[3] ],
2331 nodes[ volConn[4] ],
2332 nodes[ volConn[5] ]));
2334 ReplaceElemInGroups( elem, splitVols, GetMeshDS() );
2336 // Split faces on sides of the split volume
2338 const SMDS_MeshNode** volNodes = volTool.GetNodes();
2339 for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
2341 const int nbNodes = volTool.NbFaceNodes( iF ) / iQ;
2342 if ( nbNodes < 4 ) continue;
2344 // find an existing face
2345 vector<const SMDS_MeshNode*> fNodes( volTool.GetFaceNodes( iF ),
2346 volTool.GetFaceNodes( iF ) + volTool.NbFaceNodes( iF ));
2347 while ( const SMDS_MeshElement* face = GetMeshDS()->FindElement( fNodes, SMDSAbs_Face,
2348 /*noMedium=*/false))
2351 helper.SetElementsOnShape( false );
2352 vector< const SMDS_MeshElement* > triangles;
2354 // find submesh to add new triangles in
2355 if ( !fSubMesh || !fSubMesh->Contains( face ))
2357 int shapeID = FindShape( face );
2358 fSubMesh = GetMeshDS()->MeshElements( shapeID );
2360 map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.find(iF);
2361 if ( iF_n != splitMethod._faceBaryNode.end() )
2363 const SMDS_MeshNode *baryNode = iF_n->second;
2364 for ( int iN = 0; iN < nbNodes*iQ; iN += iQ )
2366 const SMDS_MeshNode* n1 = fNodes[iN];
2367 const SMDS_MeshNode *n2 = fNodes[(iN+iQ)%(nbNodes*iQ)];
2368 const SMDS_MeshNode *n3 = baryNode;
2369 if ( !volTool.IsFaceExternal( iF ))
2371 triangles.push_back( helper.AddFace( n1,n2,n3 ));
2373 if ( fSubMesh ) // update position of the bary node on geometry
2376 subMesh->RemoveNode( baryNode, false );
2377 GetMeshDS()->SetNodeOnFace( baryNode, fSubMesh->GetID() );
2378 const TopoDS_Shape& s = GetMeshDS()->IndexToShape( fSubMesh->GetID() );
2379 if ( !s.IsNull() && s.ShapeType() == TopAbs_FACE )
2381 fHelper.SetSubShape( s );
2382 gp_XY uv( 1e100, 1e100 );
2384 if ( !fHelper.CheckNodeUV( TopoDS::Face( s ), baryNode,
2385 uv, /*tol=*/1e-7, /*force=*/true, distXYZ ) &&
2388 // node is too far from the surface
2389 GetMeshDS()->MoveNode( baryNode, distXYZ[1], distXYZ[2], distXYZ[3] );
2390 const_cast<SMDS_MeshNode*>( baryNode )->SetPosition
2391 ( SMDS_PositionPtr( new SMDS_FacePosition( uv.X(), uv.Y() )));
2398 // among possible triangles create ones discribed by split method
2399 const int* nInd = volTool.GetFaceNodesIndices( iF );
2400 int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
2401 int iCom = 0; // common node of triangle faces to split into
2402 list< TTriangleFacet > facets;
2403 for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCom )
2405 TTriangleFacet t012( nInd[ iQ * ( iCom )],
2406 nInd[ iQ * ( (iCom+1)%nbNodes )],
2407 nInd[ iQ * ( (iCom+2)%nbNodes )]);
2408 TTriangleFacet t023( nInd[ iQ * ( iCom )],
2409 nInd[ iQ * ( (iCom+2)%nbNodes )],
2410 nInd[ iQ * ( (iCom+3)%nbNodes )]);
2411 if ( splitMethod.hasFacet( t012 ) && splitMethod.hasFacet( t023 ))
2413 facets.push_back( t012 );
2414 facets.push_back( t023 );
2415 for ( int iLast = iCom+4; iLast < iCom+nbNodes; ++iLast )
2416 facets.push_back( TTriangleFacet( nInd[ iQ * ( iCom )],
2417 nInd[ iQ * ((iLast-1)%nbNodes )],
2418 nInd[ iQ * ((iLast )%nbNodes )]));
2422 list< TTriangleFacet >::iterator facet = facets.begin();
2423 if ( facet == facets.end() )
2425 for ( ; facet != facets.end(); ++facet )
2427 if ( !volTool.IsFaceExternal( iF ))
2428 swap( facet->_n2, facet->_n3 );
2429 triangles.push_back( helper.AddFace( volNodes[ facet->_n1 ],
2430 volNodes[ facet->_n2 ],
2431 volNodes[ facet->_n3 ]));
2434 for ( int i = 0; i < triangles.size(); ++i )
2436 if ( !triangles[i] ) continue;
2438 fSubMesh->AddElement( triangles[i]);
2439 newElems.Append( triangles[i] );
2441 ReplaceElemInGroups( face, triangles, GetMeshDS() );
2442 GetMeshDS()->RemoveFreeElement( face, fSubMesh, /*fromGroups=*/false );
2444 } // while a face based on facet nodes exists
2445 } // loop on volume faces to split them into triangles
2447 GetMeshDS()->RemoveFreeElement( elem, subMesh, /*fromGroups=*/false );
2449 if ( geomType == SMDSEntity_TriQuad_Hexa )
2451 // remove medium nodes that could become free
2452 for ( int i = 20; i < volTool.NbNodes(); ++i )
2453 if ( volNodes[i]->NbInverseElements() == 0 )
2454 GetMeshDS()->RemoveNode( volNodes[i] );
2456 } // loop on volumes to split
2458 myLastCreatedNodes = newNodes;
2459 myLastCreatedElems = newElems;
2462 //=======================================================================
2463 //function : GetHexaFacetsToSplit
2464 //purpose : For hexahedra that will be split into prisms, finds facets to
2465 // split into triangles. Only hexahedra adjacent to the one closest
2466 // to theFacetNormal.Location() are returned.
2467 //param [in,out] theHexas - the hexahedra
2468 //param [in] theFacetNormal - facet normal
2469 //param [out] theFacets - the hexahedra and found facet IDs
2470 //=======================================================================
2472 void SMESH_MeshEditor::GetHexaFacetsToSplit( TIDSortedElemSet& theHexas,
2473 const gp_Ax1& theFacetNormal,
2474 TFacetOfElem & theFacets)
2476 #define THIS_METHOD "SMESH_MeshEditor::GetHexaFacetsToSplit(): "
2478 // Find a hexa closest to the location of theFacetNormal
2480 const SMDS_MeshElement* startHex;
2482 // get SMDS_ElemIteratorPtr on theHexas
2483 typedef const SMDS_MeshElement* TValue;
2484 typedef TIDSortedElemSet::iterator TSetIterator;
2485 typedef SMDS::SimpleAccessor<TValue,TSetIterator> TAccesor;
2486 typedef SMDS_MeshElement::GeomFilter TFilter;
2487 typedef SMDS_SetIterator < TValue, TSetIterator, TAccesor, TFilter > TElemSetIter;
2488 SMDS_ElemIteratorPtr elemIt = SMDS_ElemIteratorPtr
2489 ( new TElemSetIter( theHexas.begin(),
2491 SMDS_MeshElement::GeomFilter( SMDSGeom_HEXA )));
2493 SMESH_ElementSearcher* searcher =
2494 SMESH_MeshAlgos::GetElementSearcher( *myMesh->GetMeshDS(), elemIt );
2496 startHex = searcher->FindClosestTo( theFacetNormal.Location(), SMDSAbs_Volume );
2501 throw SALOME_Exception( THIS_METHOD "startHex not found");
2504 // Select a facet of startHex by theFacetNormal
2506 SMDS_VolumeTool vTool( startHex );
2507 double norm[3], dot, maxDot = 0;
2509 for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2510 if ( vTool.GetFaceNormal( iF, norm[0], norm[1], norm[2] ))
2512 dot = Abs( theFacetNormal.Direction().Dot( gp_Dir( norm[0], norm[1], norm[2] )));
2520 throw SALOME_Exception( THIS_METHOD "facet of startHex not found");
2522 // Fill theFacets starting from facetID of startHex
2524 // facets used for seach of volumes adjacent to already treated ones
2525 typedef pair< TFacetOfElem::iterator, int > TElemFacets;
2526 typedef map< TVolumeFaceKey, TElemFacets > TFacetMap;
2527 TFacetMap facetsToCheck;
2529 set<const SMDS_MeshNode*> facetNodes;
2530 const SMDS_MeshElement* curHex;
2532 const bool allHex = ( theHexas.size() == myMesh->NbHexas() );
2536 // move in two directions from startHex via facetID
2537 for ( int is2nd = 0; is2nd < 2; ++is2nd )
2540 int curFacet = facetID;
2541 if ( is2nd ) // do not treat startHex twice
2543 vTool.Set( curHex );
2544 if ( vTool.IsFreeFace( curFacet, &curHex ))
2550 vTool.GetFaceNodes( curFacet, facetNodes );
2551 vTool.Set( curHex );
2552 curFacet = vTool.GetFaceIndex( facetNodes );
2557 // store a facet to split
2558 if ( curHex->GetGeomType() != SMDSGeom_HEXA )
2560 theFacets.insert( make_pair( curHex, -1 ));
2563 if ( !allHex && !theHexas.count( curHex ))
2566 pair< TFacetOfElem::iterator, bool > facetIt2isNew =
2567 theFacets.insert( make_pair( curHex, curFacet ));
2568 if ( !facetIt2isNew.second )
2571 // remember not-to-split facets in facetsToCheck
2572 int oppFacet = vTool.GetOppFaceIndexOfHex( curFacet );
2573 for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2575 if ( iF == curFacet && iF == oppFacet )
2577 TVolumeFaceKey facetKey ( vTool, iF );
2578 TElemFacets elemFacet( facetIt2isNew.first, iF );
2579 pair< TFacetMap::iterator, bool > it2isnew =
2580 facetsToCheck.insert( make_pair( facetKey, elemFacet ));
2581 if ( !it2isnew.second )
2582 facetsToCheck.erase( it2isnew.first ); // adjacent hex already checked
2584 // pass to a volume adjacent via oppFacet
2585 if ( vTool.IsFreeFace( oppFacet, &curHex ))
2591 // get a new curFacet
2592 vTool.GetFaceNodes( oppFacet, facetNodes );
2593 vTool.Set( curHex );
2594 curFacet = vTool.GetFaceIndex( facetNodes, /*hint=*/curFacet );
2597 } // move in two directions from startHex via facetID
2599 // Find a new startHex by facetsToCheck
2603 TFacetMap::iterator fIt = facetsToCheck.begin();
2604 while ( !startHex && fIt != facetsToCheck.end() )
2606 const TElemFacets& elemFacets = fIt->second;
2607 const SMDS_MeshElement* hex = elemFacets.first->first;
2608 int splitFacet = elemFacets.first->second;
2609 int lateralFacet = elemFacets.second;
2610 facetsToCheck.erase( fIt );
2611 fIt = facetsToCheck.begin();
2614 if ( vTool.IsFreeFace( lateralFacet, &curHex ) ||
2615 curHex->GetGeomType() != SMDSGeom_HEXA )
2617 if ( !allHex && !theHexas.count( curHex ))
2622 // find a facet of startHex to split
2624 set<const SMDS_MeshNode*> lateralNodes;
2625 vTool.GetFaceNodes( lateralFacet, lateralNodes );
2626 vTool.GetFaceNodes( splitFacet, facetNodes );
2627 int oppLateralFacet = vTool.GetOppFaceIndexOfHex( lateralFacet );
2628 vTool.Set( startHex );
2629 lateralFacet = vTool.GetFaceIndex( lateralNodes, oppLateralFacet );
2631 // look for a facet of startHex having common nodes with facetNodes
2632 // but not lateralFacet
2633 for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2635 if ( iF == lateralFacet )
2637 int nbCommonNodes = 0;
2638 const SMDS_MeshNode** nn = vTool.GetFaceNodes( iF );
2639 for ( int iN = 0, nbN = vTool.NbFaceNodes( iF ); iN < nbN; ++iN )
2640 nbCommonNodes += facetNodes.count( nn[ iN ]);
2642 if ( nbCommonNodes >= 2 )
2649 throw SALOME_Exception( THIS_METHOD "facet of a new startHex not found");
2651 } // while ( startHex )
2654 //=======================================================================
2655 //function : AddToSameGroups
2656 //purpose : add elemToAdd to the groups the elemInGroups belongs to
2657 //=======================================================================
2659 void SMESH_MeshEditor::AddToSameGroups (const SMDS_MeshElement* elemToAdd,
2660 const SMDS_MeshElement* elemInGroups,
2661 SMESHDS_Mesh * aMesh)
2663 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2664 if (!groups.empty()) {
2665 set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2666 for ( ; grIt != groups.end(); grIt++ ) {
2667 SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2668 if ( group && group->Contains( elemInGroups ))
2669 group->SMDSGroup().Add( elemToAdd );
2675 //=======================================================================
2676 //function : RemoveElemFromGroups
2677 //purpose : Remove removeelem to the groups the elemInGroups belongs to
2678 //=======================================================================
2679 void SMESH_MeshEditor::RemoveElemFromGroups (const SMDS_MeshElement* removeelem,
2680 SMESHDS_Mesh * aMesh)
2682 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2683 if (!groups.empty())
2685 set<SMESHDS_GroupBase*>::const_iterator GrIt = groups.begin();
2686 for (; GrIt != groups.end(); GrIt++)
2688 SMESHDS_Group* grp = dynamic_cast<SMESHDS_Group*>(*GrIt);
2689 if (!grp || grp->IsEmpty()) continue;
2690 grp->SMDSGroup().Remove(removeelem);
2695 //================================================================================
2697 * \brief Replace elemToRm by elemToAdd in the all groups
2699 //================================================================================
2701 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement* elemToRm,
2702 const SMDS_MeshElement* elemToAdd,
2703 SMESHDS_Mesh * aMesh)
2705 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2706 if (!groups.empty()) {
2707 set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2708 for ( ; grIt != groups.end(); grIt++ ) {
2709 SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2710 if ( group && group->SMDSGroup().Remove( elemToRm ) && elemToAdd )
2711 group->SMDSGroup().Add( elemToAdd );
2716 //================================================================================
2718 * \brief Replace elemToRm by elemToAdd in the all groups
2720 //================================================================================
2722 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement* elemToRm,
2723 const vector<const SMDS_MeshElement*>& elemToAdd,
2724 SMESHDS_Mesh * aMesh)
2726 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2727 if (!groups.empty())
2729 set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2730 for ( ; grIt != groups.end(); grIt++ ) {
2731 SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2732 if ( group && group->SMDSGroup().Remove( elemToRm ) )
2733 for ( int i = 0; i < elemToAdd.size(); ++i )
2734 group->SMDSGroup().Add( elemToAdd[ i ] );
2739 //=======================================================================
2740 //function : QuadToTri
2741 //purpose : Cut quadrangles into triangles.
2742 // theCrit is used to select a diagonal to cut
2743 //=======================================================================
2745 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet & theElems,
2746 const bool the13Diag)
2748 myLastCreatedElems.Clear();
2749 myLastCreatedNodes.Clear();
2751 MESSAGE( "::QuadToTri()" );
2753 SMESHDS_Mesh * aMesh = GetMeshDS();
2755 Handle(Geom_Surface) surface;
2756 SMESH_MesherHelper helper( *GetMesh() );
2758 TIDSortedElemSet::iterator itElem;
2759 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
2760 const SMDS_MeshElement* elem = *itElem;
2761 if ( !elem || elem->GetType() != SMDSAbs_Face )
2763 bool isquad = elem->NbNodes()==4 || elem->NbNodes()==8;
2764 if(!isquad) continue;
2766 if(elem->NbNodes()==4) {
2767 // retrieve element nodes
2768 const SMDS_MeshNode* aNodes [4];
2769 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
2771 while ( itN->more() )
2772 aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
2774 int aShapeId = FindShape( elem );
2775 const SMDS_MeshElement* newElem1 = 0;
2776 const SMDS_MeshElement* newElem2 = 0;
2778 newElem1 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
2779 newElem2 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
2782 newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
2783 newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
2785 myLastCreatedElems.Append(newElem1);
2786 myLastCreatedElems.Append(newElem2);
2787 // put a new triangle on the same shape and add to the same groups
2790 aMesh->SetMeshElementOnShape( newElem1, aShapeId );
2791 aMesh->SetMeshElementOnShape( newElem2, aShapeId );
2793 AddToSameGroups( newElem1, elem, aMesh );
2794 AddToSameGroups( newElem2, elem, aMesh );
2795 //aMesh->RemoveFreeElement(elem, aMesh->MeshElements(aShapeId), true);
2796 aMesh->RemoveElement( elem );
2799 // Quadratic quadrangle
2801 if( elem->NbNodes()==8 && elem->IsQuadratic() ) {
2803 // get surface elem is on
2804 int aShapeId = FindShape( elem );
2805 if ( aShapeId != helper.GetSubShapeID() ) {
2809 shape = aMesh->IndexToShape( aShapeId );
2810 if ( !shape.IsNull() && shape.ShapeType() == TopAbs_FACE ) {
2811 TopoDS_Face face = TopoDS::Face( shape );
2812 surface = BRep_Tool::Surface( face );
2813 if ( !surface.IsNull() )
2814 helper.SetSubShape( shape );
2818 const SMDS_MeshNode* aNodes [8];
2819 const SMDS_MeshNode* inFaceNode = 0;
2820 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
2822 while ( itN->more() ) {
2823 aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
2824 if ( !inFaceNode && helper.GetNodeUVneedInFaceNode() &&
2825 aNodes[ i-1 ]->GetPosition()->GetTypeOfPosition() == SMDS_TOP_FACE )
2827 inFaceNode = aNodes[ i-1 ];
2831 // find middle point for (0,1,2,3)
2832 // and create a node in this point;
2834 if ( surface.IsNull() ) {
2836 p += gp_XYZ(aNodes[i]->X(), aNodes[i]->Y(), aNodes[i]->Z() );
2840 TopoDS_Face geomFace = TopoDS::Face( helper.GetSubShape() );
2843 uv += helper.GetNodeUV( geomFace, aNodes[i], inFaceNode );
2845 p = surface->Value( uv.X(), uv.Y() ).XYZ();
2847 const SMDS_MeshNode* newN = aMesh->AddNode( p.X(), p.Y(), p.Z() );
2848 myLastCreatedNodes.Append(newN);
2850 // create a new element
2851 const SMDS_MeshElement* newElem1 = 0;
2852 const SMDS_MeshElement* newElem2 = 0;
2854 newElem1 = aMesh->AddFace(aNodes[2], aNodes[3], aNodes[0],
2855 aNodes[6], aNodes[7], newN );
2856 newElem2 = aMesh->AddFace(aNodes[2], aNodes[0], aNodes[1],
2857 newN, aNodes[4], aNodes[5] );
2860 newElem1 = aMesh->AddFace(aNodes[3], aNodes[0], aNodes[1],
2861 aNodes[7], aNodes[4], newN );
2862 newElem2 = aMesh->AddFace(aNodes[3], aNodes[1], aNodes[2],
2863 newN, aNodes[5], aNodes[6] );
2865 myLastCreatedElems.Append(newElem1);
2866 myLastCreatedElems.Append(newElem2);
2867 // put a new triangle on the same shape and add to the same groups
2870 aMesh->SetMeshElementOnShape( newElem1, aShapeId );
2871 aMesh->SetMeshElementOnShape( newElem2, aShapeId );
2873 AddToSameGroups( newElem1, elem, aMesh );
2874 AddToSameGroups( newElem2, elem, aMesh );
2875 aMesh->RemoveElement( elem );
2882 //=======================================================================
2883 //function : getAngle
2885 //=======================================================================
2887 double getAngle(const SMDS_MeshElement * tr1,
2888 const SMDS_MeshElement * tr2,
2889 const SMDS_MeshNode * n1,
2890 const SMDS_MeshNode * n2)
2892 double angle = 2. * M_PI; // bad angle
2895 SMESH::Controls::TSequenceOfXYZ P1, P2;
2896 if ( !SMESH::Controls::NumericalFunctor::GetPoints( tr1, P1 ) ||
2897 !SMESH::Controls::NumericalFunctor::GetPoints( tr2, P2 ))
2900 if(!tr1->IsQuadratic())
2901 N1 = gp_Vec( P1(2) - P1(1) ) ^ gp_Vec( P1(3) - P1(1) );
2903 N1 = gp_Vec( P1(3) - P1(1) ) ^ gp_Vec( P1(5) - P1(1) );
2904 if ( N1.SquareMagnitude() <= gp::Resolution() )
2906 if(!tr2->IsQuadratic())
2907 N2 = gp_Vec( P2(2) - P2(1) ) ^ gp_Vec( P2(3) - P2(1) );
2909 N2 = gp_Vec( P2(3) - P2(1) ) ^ gp_Vec( P2(5) - P2(1) );
2910 if ( N2.SquareMagnitude() <= gp::Resolution() )
2913 // find the first diagonal node n1 in the triangles:
2914 // take in account a diagonal link orientation
2915 const SMDS_MeshElement *nFirst[2], *tr[] = { tr1, tr2 };
2916 for ( int t = 0; t < 2; t++ ) {
2917 SMDS_ElemIteratorPtr it = tr[ t ]->nodesIterator();
2918 int i = 0, iDiag = -1;
2919 while ( it->more()) {
2920 const SMDS_MeshElement *n = it->next();
2921 if ( n == n1 || n == n2 ) {
2925 if ( i - iDiag == 1 )
2926 nFirst[ t ] = ( n == n1 ? n2 : n1 );
2935 if ( nFirst[ 0 ] == nFirst[ 1 ] )
2938 angle = N1.Angle( N2 );
2943 // =================================================
2944 // class generating a unique ID for a pair of nodes
2945 // and able to return nodes by that ID
2946 // =================================================
2950 LinkID_Gen( const SMESHDS_Mesh* theMesh )
2951 :myMesh( theMesh ), myMaxID( theMesh->MaxNodeID() + 1)
2954 long GetLinkID (const SMDS_MeshNode * n1,
2955 const SMDS_MeshNode * n2) const
2957 return ( Min(n1->GetID(),n2->GetID()) * myMaxID + Max(n1->GetID(),n2->GetID()));
2960 bool GetNodes (const long theLinkID,
2961 const SMDS_MeshNode* & theNode1,
2962 const SMDS_MeshNode* & theNode2) const
2964 theNode1 = myMesh->FindNode( theLinkID / myMaxID );
2965 if ( !theNode1 ) return false;
2966 theNode2 = myMesh->FindNode( theLinkID % myMaxID );
2967 if ( !theNode2 ) return false;
2973 const SMESHDS_Mesh* myMesh;
2978 //=======================================================================
2979 //function : TriToQuad
2980 //purpose : Fuse neighbour triangles into quadrangles.
2981 // theCrit is used to select a neighbour to fuse with.
2982 // theMaxAngle is a max angle between element normals at which
2983 // fusion is still performed.
2984 //=======================================================================
2986 bool SMESH_MeshEditor::TriToQuad (TIDSortedElemSet & theElems,
2987 SMESH::Controls::NumericalFunctorPtr theCrit,
2988 const double theMaxAngle)
2990 myLastCreatedElems.Clear();
2991 myLastCreatedNodes.Clear();
2993 MESSAGE( "::TriToQuad()" );
2995 if ( !theCrit.get() )
2998 SMESHDS_Mesh * aMesh = GetMeshDS();
3000 // Prepare data for algo: build
3001 // 1. map of elements with their linkIDs
3002 // 2. map of linkIDs with their elements
3004 map< SMESH_TLink, list< const SMDS_MeshElement* > > mapLi_listEl;
3005 map< SMESH_TLink, list< const SMDS_MeshElement* > >::iterator itLE;
3006 map< const SMDS_MeshElement*, set< SMESH_TLink > > mapEl_setLi;
3007 map< const SMDS_MeshElement*, set< SMESH_TLink > >::iterator itEL;
3009 TIDSortedElemSet::iterator itElem;
3010 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
3012 const SMDS_MeshElement* elem = *itElem;
3013 if(!elem || elem->GetType() != SMDSAbs_Face ) continue;
3014 bool IsTria = ( elem->NbCornerNodes()==3 );
3015 if (!IsTria) continue;
3017 // retrieve element nodes
3018 const SMDS_MeshNode* aNodes [4];
3019 SMDS_NodeIteratorPtr itN = elem->nodeIterator();
3022 aNodes[ i++ ] = itN->next();
3023 aNodes[ 3 ] = aNodes[ 0 ];
3026 for ( i = 0; i < 3; i++ ) {
3027 SMESH_TLink link( aNodes[i], aNodes[i+1] );
3028 // check if elements sharing a link can be fused
3029 itLE = mapLi_listEl.find( link );
3030 if ( itLE != mapLi_listEl.end() ) {
3031 if ((*itLE).second.size() > 1 ) // consider only 2 elems adjacent by a link
3033 const SMDS_MeshElement* elem2 = (*itLE).second.front();
3034 //if ( FindShape( elem ) != FindShape( elem2 ))
3035 // continue; // do not fuse triangles laying on different shapes
3036 if ( getAngle( elem, elem2, aNodes[i], aNodes[i+1] ) > theMaxAngle )
3037 continue; // avoid making badly shaped quads
3038 (*itLE).second.push_back( elem );
3041 mapLi_listEl[ link ].push_back( elem );
3043 mapEl_setLi [ elem ].insert( link );
3046 // Clean the maps from the links shared by a sole element, ie
3047 // links to which only one element is bound in mapLi_listEl
3049 for ( itLE = mapLi_listEl.begin(); itLE != mapLi_listEl.end(); itLE++ ) {
3050 int nbElems = (*itLE).second.size();
3051 if ( nbElems < 2 ) {
3052 const SMDS_MeshElement* elem = (*itLE).second.front();
3053 SMESH_TLink link = (*itLE).first;
3054 mapEl_setLi[ elem ].erase( link );
3055 if ( mapEl_setLi[ elem ].empty() )
3056 mapEl_setLi.erase( elem );
3060 // Algo: fuse triangles into quadrangles
3062 while ( ! mapEl_setLi.empty() ) {
3063 // Look for the start element:
3064 // the element having the least nb of shared links
3065 const SMDS_MeshElement* startElem = 0;
3067 for ( itEL = mapEl_setLi.begin(); itEL != mapEl_setLi.end(); itEL++ ) {
3068 int nbLinks = (*itEL).second.size();
3069 if ( nbLinks < minNbLinks ) {
3070 startElem = (*itEL).first;
3071 minNbLinks = nbLinks;
3072 if ( minNbLinks == 1 )
3077 // search elements to fuse starting from startElem or links of elements
3078 // fused earlyer - startLinks
3079 list< SMESH_TLink > startLinks;
3080 while ( startElem || !startLinks.empty() ) {
3081 while ( !startElem && !startLinks.empty() ) {
3082 // Get an element to start, by a link
3083 SMESH_TLink linkId = startLinks.front();
3084 startLinks.pop_front();
3085 itLE = mapLi_listEl.find( linkId );
3086 if ( itLE != mapLi_listEl.end() ) {
3087 list< const SMDS_MeshElement* > & listElem = (*itLE).second;
3088 list< const SMDS_MeshElement* >::iterator itE = listElem.begin();
3089 for ( ; itE != listElem.end() ; itE++ )
3090 if ( mapEl_setLi.find( (*itE) ) != mapEl_setLi.end() )
3092 mapLi_listEl.erase( itLE );
3097 // Get candidates to be fused
3098 const SMDS_MeshElement *tr1 = startElem, *tr2 = 0, *tr3 = 0;
3099 const SMESH_TLink *link12, *link13;
3101 ASSERT( mapEl_setLi.find( tr1 ) != mapEl_setLi.end() );
3102 set< SMESH_TLink >& setLi = mapEl_setLi[ tr1 ];
3103 ASSERT( !setLi.empty() );
3104 set< SMESH_TLink >::iterator itLi;
3105 for ( itLi = setLi.begin(); itLi != setLi.end(); itLi++ )
3107 const SMESH_TLink & link = (*itLi);
3108 itLE = mapLi_listEl.find( link );
3109 if ( itLE == mapLi_listEl.end() )
3112 const SMDS_MeshElement* elem = (*itLE).second.front();
3114 elem = (*itLE).second.back();
3115 mapLi_listEl.erase( itLE );
3116 if ( mapEl_setLi.find( elem ) == mapEl_setLi.end())
3127 // add other links of elem to list of links to re-start from
3128 set< SMESH_TLink >& links = mapEl_setLi[ elem ];
3129 set< SMESH_TLink >::iterator it;
3130 for ( it = links.begin(); it != links.end(); it++ ) {
3131 const SMESH_TLink& link2 = (*it);
3132 if ( link2 != link )
3133 startLinks.push_back( link2 );
3137 // Get nodes of possible quadrangles
3138 const SMDS_MeshNode *n12 [4], *n13 [4];
3139 bool Ok12 = false, Ok13 = false;
3140 const SMDS_MeshNode *linkNode1, *linkNode2;
3142 linkNode1 = link12->first;
3143 linkNode2 = link12->second;
3144 if ( tr2 && getQuadrangleNodes( n12, linkNode1, linkNode2, tr1, tr2 ))
3148 linkNode1 = link13->first;
3149 linkNode2 = link13->second;
3150 if ( tr3 && getQuadrangleNodes( n13, linkNode1, linkNode2, tr1, tr3 ))
3154 // Choose a pair to fuse
3155 if ( Ok12 && Ok13 ) {
3156 SMDS_FaceOfNodes quad12 ( n12[ 0 ], n12[ 1 ], n12[ 2 ], n12[ 3 ] );
3157 SMDS_FaceOfNodes quad13 ( n13[ 0 ], n13[ 1 ], n13[ 2 ], n13[ 3 ] );
3158 double aBadRate12 = getBadRate( &quad12, theCrit );
3159 double aBadRate13 = getBadRate( &quad13, theCrit );
3160 if ( aBadRate13 < aBadRate12 )
3167 // and remove fused elems and remove links from the maps
3168 mapEl_setLi.erase( tr1 );
3171 mapEl_setLi.erase( tr2 );
3172 mapLi_listEl.erase( *link12 );
3173 if ( tr1->NbNodes() == 3 )
3175 const SMDS_MeshElement* newElem = 0;
3176 newElem = aMesh->AddFace(n12[0], n12[1], n12[2], n12[3] );
3177 myLastCreatedElems.Append(newElem);
3178 AddToSameGroups( newElem, tr1, aMesh );
3179 int aShapeId = tr1->getshapeId();
3181 aMesh->SetMeshElementOnShape( newElem, aShapeId );
3182 aMesh->RemoveElement( tr1 );
3183 aMesh->RemoveElement( tr2 );
3186 vector< const SMDS_MeshNode* > N1;
3187 vector< const SMDS_MeshNode* > N2;
3188 getNodesFromTwoTria(tr1,tr2,N1,N2);
3189 // now we receive following N1 and N2 (using numeration as in image in InverseDiag())
3190 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
3191 // i.e. first nodes from both arrays form a new diagonal
3192 const SMDS_MeshNode* aNodes[8];
3201 const SMDS_MeshElement* newElem = 0;
3202 if ( N1.size() == 7 || N2.size() == 7 ) // biquadratic
3203 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3204 aNodes[4], aNodes[5], aNodes[6], aNodes[7], N1[4]);
3206 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3207 aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
3208 myLastCreatedElems.Append(newElem);
3209 AddToSameGroups( newElem, tr1, aMesh );
3210 int aShapeId = tr1->getshapeId();
3212 aMesh->SetMeshElementOnShape( newElem, aShapeId );
3213 aMesh->RemoveElement( tr1 );
3214 aMesh->RemoveElement( tr2 );
3215 // remove middle node (9)
3216 if ( N1[4]->NbInverseElements() == 0 )
3217 aMesh->RemoveNode( N1[4] );
3218 if ( N1.size() == 7 && N1[6]->NbInverseElements() == 0 )
3219 aMesh->RemoveNode( N1[6] );
3220 if ( N2.size() == 7 && N2[6]->NbInverseElements() == 0 )
3221 aMesh->RemoveNode( N2[6] );
3226 mapEl_setLi.erase( tr3 );
3227 mapLi_listEl.erase( *link13 );
3228 if ( tr1->NbNodes() == 3 ) {
3229 const SMDS_MeshElement* newElem = 0;
3230 newElem = aMesh->AddFace(n13[0], n13[1], n13[2], n13[3] );
3231 myLastCreatedElems.Append(newElem);
3232 AddToSameGroups( newElem, tr1, aMesh );
3233 int aShapeId = tr1->getshapeId();
3235 aMesh->SetMeshElementOnShape( newElem, aShapeId );
3236 aMesh->RemoveElement( tr1 );
3237 aMesh->RemoveElement( tr3 );
3240 vector< const SMDS_MeshNode* > N1;
3241 vector< const SMDS_MeshNode* > N2;
3242 getNodesFromTwoTria(tr1,tr3,N1,N2);
3243 // now we receive following N1 and N2 (using numeration as above image)
3244 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
3245 // i.e. first nodes from both arrays form a new diagonal
3246 const SMDS_MeshNode* aNodes[8];
3255 const SMDS_MeshElement* newElem = 0;
3256 if ( N1.size() == 7 || N2.size() == 7 ) // biquadratic
3257 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3258 aNodes[4], aNodes[5], aNodes[6], aNodes[7], N1[4]);
3260 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3261 aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
3262 myLastCreatedElems.Append(newElem);
3263 AddToSameGroups( newElem, tr1, aMesh );
3264 int aShapeId = tr1->getshapeId();
3266 aMesh->SetMeshElementOnShape( newElem, aShapeId );
3267 aMesh->RemoveElement( tr1 );
3268 aMesh->RemoveElement( tr3 );
3269 // remove middle node (9)
3270 if ( N1[4]->NbInverseElements() == 0 )
3271 aMesh->RemoveNode( N1[4] );
3272 if ( N1.size() == 7 && N1[6]->NbInverseElements() == 0 )
3273 aMesh->RemoveNode( N1[6] );
3274 if ( N2.size() == 7 && N2[6]->NbInverseElements() == 0 )
3275 aMesh->RemoveNode( N2[6] );
3279 // Next element to fuse: the rejected one
3281 startElem = Ok12 ? tr3 : tr2;
3283 } // if ( startElem )
3284 } // while ( startElem || !startLinks.empty() )
3285 } // while ( ! mapEl_setLi.empty() )
3291 /*#define DUMPSO(txt) \
3292 // cout << txt << endl;
3293 //=============================================================================
3297 //=============================================================================
3298 static void swap( int i1, int i2, int idNodes[], gp_Pnt P[] )
3302 int tmp = idNodes[ i1 ];
3303 idNodes[ i1 ] = idNodes[ i2 ];
3304 idNodes[ i2 ] = tmp;
3305 gp_Pnt Ptmp = P[ i1 ];
3308 DUMPSO( i1 << "(" << idNodes[ i2 ] << ") <-> " << i2 << "(" << idNodes[ i1 ] << ")");
3311 //=======================================================================
3312 //function : SortQuadNodes
3313 //purpose : Set 4 nodes of a quadrangle face in a good order.
3314 // Swap 1<->2 or 2<->3 nodes and correspondingly return
3316 //=======================================================================
3318 int SMESH_MeshEditor::SortQuadNodes (const SMDS_Mesh * theMesh,
3323 for ( i = 0; i < 4; i++ ) {
3324 const SMDS_MeshNode *n = theMesh->FindNode( idNodes[i] );
3326 P[ i ].SetCoord( n->X(), n->Y(), n->Z() );
3329 gp_Vec V1(P[0], P[1]);
3330 gp_Vec V2(P[0], P[2]);
3331 gp_Vec V3(P[0], P[3]);
3333 gp_Vec Cross1 = V1 ^ V2;
3334 gp_Vec Cross2 = V2 ^ V3;
3337 if (Cross1.Dot(Cross2) < 0)
3342 if (Cross1.Dot(Cross2) < 0)
3346 swap ( i, i + 1, idNodes, P );
3348 // for ( int ii = 0; ii < 4; ii++ ) {
3349 // const SMDS_MeshNode *n = theMesh->FindNode( idNodes[ii] );
3350 // DUMPSO( ii << "(" << idNodes[ii] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
3356 //=======================================================================
3357 //function : SortHexaNodes
3358 //purpose : Set 8 nodes of a hexahedron in a good order.
3359 // Return success status
3360 //=======================================================================
3362 bool SMESH_MeshEditor::SortHexaNodes (const SMDS_Mesh * theMesh,
3367 DUMPSO( "INPUT: ========================================");
3368 for ( i = 0; i < 8; i++ ) {
3369 const SMDS_MeshNode *n = theMesh->FindNode( idNodes[i] );
3370 if ( !n ) return false;
3371 P[ i ].SetCoord( n->X(), n->Y(), n->Z() );
3372 DUMPSO( i << "(" << idNodes[i] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
3374 DUMPSO( "========================================");
3377 set<int> faceNodes; // ids of bottom face nodes, to be found
3378 set<int> checkedId1; // ids of tried 2-nd nodes
3379 Standard_Real leastDist = DBL_MAX; // dist of the 4-th node from 123 plane
3380 const Standard_Real tol = 1.e-6; // tolerance to find nodes in plane
3381 int iMin, iLoop1 = 0;
3383 // Loop to try the 2-nd nodes
3385 while ( leastDist > DBL_MIN && ++iLoop1 < 8 )
3387 // Find not checked 2-nd node
3388 for ( i = 1; i < 8; i++ )
3389 if ( checkedId1.find( idNodes[i] ) == checkedId1.end() ) {
3390 int id1 = idNodes[i];
3391 swap ( 1, i, idNodes, P );
3392 checkedId1.insert ( id1 );
3396 // Find the 3-d node so that 1-2-3 triangle to be on a hexa face,
3397 // ie that all but meybe one (id3 which is on the same face) nodes
3398 // lay on the same side from the triangle plane.
3400 bool manyInPlane = false; // more than 4 nodes lay in plane
3402 while ( ++iLoop2 < 6 ) {
3404 // get 1-2-3 plane coeffs
3405 Standard_Real A, B, C, D;
3406 gp_Vec N = gp_Vec (P[0], P[1]).Crossed( gp_Vec (P[0], P[2]) );
3407 if ( N.SquareMagnitude() > gp::Resolution() )
3409 gp_Pln pln ( P[0], N );
3410 pln.Coefficients( A, B, C, D );
3412 // find the node (iMin) closest to pln
3413 Standard_Real dist[ 8 ], minDist = DBL_MAX;
3415 for ( i = 3; i < 8; i++ ) {
3416 dist[i] = A * P[i].X() + B * P[i].Y() + C * P[i].Z() + D;
3417 if ( fabs( dist[i] ) < minDist ) {
3418 minDist = fabs( dist[i] );
3421 if ( fabs( dist[i] ) <= tol )
3422 idInPln.insert( idNodes[i] );
3425 // there should not be more than 4 nodes in bottom plane
3426 if ( idInPln.size() > 1 )
3428 DUMPSO( "### idInPln.size() = " << idInPln.size());
3429 // idInPlane does not contain the first 3 nodes
3430 if ( manyInPlane || idInPln.size() == 5)
3431 return false; // all nodes in one plane
3434 // set the 1-st node to be not in plane
3435 for ( i = 3; i < 8; i++ ) {
3436 if ( idInPln.find( idNodes[ i ] ) == idInPln.end() ) {
3437 DUMPSO( "### Reset 0-th node");
3438 swap( 0, i, idNodes, P );
3443 // reset to re-check second nodes
3444 leastDist = DBL_MAX;
3448 break; // from iLoop2;
3451 // check that the other 4 nodes are on the same side
3452 bool sameSide = true;
3453 bool isNeg = dist[ iMin == 3 ? 4 : 3 ] <= 0.;
3454 for ( i = 3; sameSide && i < 8; i++ ) {
3456 sameSide = ( isNeg == dist[i] <= 0.);
3459 // keep best solution
3460 if ( sameSide && minDist < leastDist ) {
3461 leastDist = minDist;
3463 faceNodes.insert( idNodes[ 1 ] );
3464 faceNodes.insert( idNodes[ 2 ] );
3465 faceNodes.insert( idNodes[ iMin ] );
3466 DUMPSO( "loop " << iLoop2 << " id2 " << idNodes[ 1 ] << " id3 " << idNodes[ 2 ]
3467 << " leastDist = " << leastDist);
3468 if ( leastDist <= DBL_MIN )
3473 // set next 3-d node to check
3474 int iNext = 2 + iLoop2;
3476 DUMPSO( "Try 2-nd");
3477 swap ( 2, iNext, idNodes, P );
3479 } // while ( iLoop2 < 6 )
3482 if ( faceNodes.empty() ) return false;
3484 // Put the faceNodes in proper places
3485 for ( i = 4; i < 8; i++ ) {
3486 if ( faceNodes.find( idNodes[ i ] ) != faceNodes.end() ) {
3487 // find a place to put
3489 while ( faceNodes.find( idNodes[ iTo ] ) != faceNodes.end() )
3491 DUMPSO( "Set faceNodes");
3492 swap ( iTo, i, idNodes, P );
3497 // Set nodes of the found bottom face in good order
3498 DUMPSO( " Found bottom face: ");
3499 i = SortQuadNodes( theMesh, idNodes );
3501 gp_Pnt Ptmp = P[ i ];
3506 // for ( int ii = 0; ii < 4; ii++ ) {
3507 // const SMDS_MeshNode *n = theMesh->FindNode( idNodes[ii] );
3508 // DUMPSO( ii << "(" << idNodes[ii] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
3511 // Gravity center of the top and bottom faces
3512 gp_Pnt aGCb = ( P[0].XYZ() + P[1].XYZ() + P[2].XYZ() + P[3].XYZ() ) / 4.;
3513 gp_Pnt aGCt = ( P[4].XYZ() + P[5].XYZ() + P[6].XYZ() + P[7].XYZ() ) / 4.;
3515 // Get direction from the bottom to the top face
3516 gp_Vec upDir ( aGCb, aGCt );
3517 Standard_Real upDirSize = upDir.Magnitude();
3518 if ( upDirSize <= gp::Resolution() ) return false;
3521 // Assure that the bottom face normal points up
3522 gp_Vec Nb = gp_Vec (P[0], P[1]).Crossed( gp_Vec (P[0], P[2]) );
3523 Nb += gp_Vec (P[0], P[2]).Crossed( gp_Vec (P[0], P[3]) );
3524 if ( Nb.Dot( upDir ) < 0 ) {
3525 DUMPSO( "Reverse bottom face");
3526 swap( 1, 3, idNodes, P );
3529 // Find 5-th node - the one closest to the 1-st among the last 4 nodes.
3530 Standard_Real minDist = DBL_MAX;
3531 for ( i = 4; i < 8; i++ ) {
3532 // projection of P[i] to the plane defined by P[0] and upDir
3533 gp_Pnt Pp = P[i].Translated( upDir * ( upDir.Dot( gp_Vec( P[i], P[0] ))));
3534 Standard_Real sqDist = P[0].SquareDistance( Pp );
3535 if ( sqDist < minDist ) {
3540 DUMPSO( "Set 4-th");
3541 swap ( 4, iMin, idNodes, P );
3543 // Set nodes of the top face in good order
3544 DUMPSO( "Sort top face");
3545 i = SortQuadNodes( theMesh, &idNodes[4] );
3548 gp_Pnt Ptmp = P[ i ];
3553 // Assure that direction of the top face normal is from the bottom face
3554 gp_Vec Nt = gp_Vec (P[4], P[5]).Crossed( gp_Vec (P[4], P[6]) );
3555 Nt += gp_Vec (P[4], P[6]).Crossed( gp_Vec (P[4], P[7]) );
3556 if ( Nt.Dot( upDir ) < 0 ) {
3557 DUMPSO( "Reverse top face");
3558 swap( 5, 7, idNodes, P );
3561 // DUMPSO( "OUTPUT: ========================================");
3562 // for ( i = 0; i < 8; i++ ) {
3563 // float *p = ugrid->GetPoint(idNodes[i]);
3564 // DUMPSO( i << "(" << idNodes[i] << ") : " << p[0] << " " << p[1] << " " << p[2]);
3570 //================================================================================
3572 * \brief Return nodes linked to the given one
3573 * \param theNode - the node
3574 * \param linkedNodes - the found nodes
3575 * \param type - the type of elements to check
3577 * Medium nodes are ignored
3579 //================================================================================
3581 void SMESH_MeshEditor::GetLinkedNodes( const SMDS_MeshNode* theNode,
3582 TIDSortedElemSet & linkedNodes,
3583 SMDSAbs_ElementType type )
3585 SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(type);
3586 while ( elemIt->more() )
3588 const SMDS_MeshElement* elem = elemIt->next();
3589 if(elem->GetType() == SMDSAbs_0DElement)
3592 SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
3593 if ( elem->GetType() == SMDSAbs_Volume )
3595 SMDS_VolumeTool vol( elem );
3596 while ( nodeIt->more() ) {
3597 const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
3598 if ( theNode != n && vol.IsLinked( theNode, n ))
3599 linkedNodes.insert( n );
3604 for ( int i = 0; nodeIt->more(); ++i ) {
3605 const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
3606 if ( n == theNode ) {
3607 int iBefore = i - 1;
3609 if ( elem->IsQuadratic() ) {
3610 int nb = elem->NbNodes() / 2;
3611 iAfter = SMESH_MesherHelper::WrapIndex( iAfter, nb );
3612 iBefore = SMESH_MesherHelper::WrapIndex( iBefore, nb );
3614 linkedNodes.insert( elem->GetNodeWrap( iAfter ));
3615 linkedNodes.insert( elem->GetNodeWrap( iBefore ));
3622 //=======================================================================
3623 //function : laplacianSmooth
3624 //purpose : pulls theNode toward the center of surrounding nodes directly
3625 // connected to that node along an element edge
3626 //=======================================================================
3628 void laplacianSmooth(const SMDS_MeshNode* theNode,
3629 const Handle(Geom_Surface)& theSurface,
3630 map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
3632 // find surrounding nodes
3634 TIDSortedElemSet nodeSet;
3635 SMESH_MeshEditor::GetLinkedNodes( theNode, nodeSet, SMDSAbs_Face );
3637 // compute new coodrs
3639 double coord[] = { 0., 0., 0. };
3640 TIDSortedElemSet::iterator nodeSetIt = nodeSet.begin();
3641 for ( ; nodeSetIt != nodeSet.end(); nodeSetIt++ ) {
3642 const SMDS_MeshNode* node = cast2Node(*nodeSetIt);
3643 if ( theSurface.IsNull() ) { // smooth in 3D
3644 coord[0] += node->X();
3645 coord[1] += node->Y();
3646 coord[2] += node->Z();
3648 else { // smooth in 2D
3649 ASSERT( theUVMap.find( node ) != theUVMap.end() );
3650 gp_XY* uv = theUVMap[ node ];
3651 coord[0] += uv->X();
3652 coord[1] += uv->Y();
3655 int nbNodes = nodeSet.size();
3658 coord[0] /= nbNodes;
3659 coord[1] /= nbNodes;
3661 if ( !theSurface.IsNull() ) {
3662 ASSERT( theUVMap.find( theNode ) != theUVMap.end() );
3663 theUVMap[ theNode ]->SetCoord( coord[0], coord[1] );
3664 gp_Pnt p3d = theSurface->Value( coord[0], coord[1] );
3670 coord[2] /= nbNodes;
3674 const_cast< SMDS_MeshNode* >( theNode )->setXYZ(coord[0],coord[1],coord[2]);
3677 //=======================================================================
3678 //function : centroidalSmooth
3679 //purpose : pulls theNode toward the element-area-weighted centroid of the
3680 // surrounding elements
3681 //=======================================================================
3683 void centroidalSmooth(const SMDS_MeshNode* theNode,
3684 const Handle(Geom_Surface)& theSurface,
3685 map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
3687 gp_XYZ aNewXYZ(0.,0.,0.);
3688 SMESH::Controls::Area anAreaFunc;
3689 double totalArea = 0.;
3694 SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(SMDSAbs_Face);
3695 while ( elemIt->more() )
3697 const SMDS_MeshElement* elem = elemIt->next();
3700 gp_XYZ elemCenter(0.,0.,0.);
3701 SMESH::Controls::TSequenceOfXYZ aNodePoints;
3702 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3703 int nn = elem->NbNodes();
3704 if(elem->IsQuadratic()) nn = nn/2;
3706 //while ( itN->more() ) {
3708 const SMDS_MeshNode* aNode = static_cast<const SMDS_MeshNode*>( itN->next() );
3710 gp_XYZ aP( aNode->X(), aNode->Y(), aNode->Z() );
3711 aNodePoints.push_back( aP );
3712 if ( !theSurface.IsNull() ) { // smooth in 2D
3713 ASSERT( theUVMap.find( aNode ) != theUVMap.end() );
3714 gp_XY* uv = theUVMap[ aNode ];
3715 aP.SetCoord( uv->X(), uv->Y(), 0. );
3719 double elemArea = anAreaFunc.GetValue( aNodePoints );
3720 totalArea += elemArea;
3722 aNewXYZ += elemCenter * elemArea;
3724 aNewXYZ /= totalArea;
3725 if ( !theSurface.IsNull() ) {
3726 theUVMap[ theNode ]->SetCoord( aNewXYZ.X(), aNewXYZ.Y() );
3727 aNewXYZ = theSurface->Value( aNewXYZ.X(), aNewXYZ.Y() ).XYZ();
3732 const_cast< SMDS_MeshNode* >( theNode )->setXYZ(aNewXYZ.X(),aNewXYZ.Y(),aNewXYZ.Z());
3735 //=======================================================================
3736 //function : getClosestUV
3737 //purpose : return UV of closest projection
3738 //=======================================================================
3740 static bool getClosestUV (Extrema_GenExtPS& projector,
3741 const gp_Pnt& point,
3744 projector.Perform( point );
3745 if ( projector.IsDone() ) {
3746 double u, v, minVal = DBL_MAX;
3747 for ( int i = projector.NbExt(); i > 0; i-- )
3748 if ( projector.SquareDistance( i ) < minVal ) {
3749 minVal = projector.SquareDistance( i );
3750 projector.Point( i ).Parameter( u, v );
3752 result.SetCoord( u, v );
3758 //=======================================================================
3760 //purpose : Smooth theElements during theNbIterations or until a worst
3761 // element has aspect ratio <= theTgtAspectRatio.
3762 // Aspect Ratio varies in range [1.0, inf].
3763 // If theElements is empty, the whole mesh is smoothed.
3764 // theFixedNodes contains additionally fixed nodes. Nodes built
3765 // on edges and boundary nodes are always fixed.
3766 //=======================================================================
3768 void SMESH_MeshEditor::Smooth (TIDSortedElemSet & theElems,
3769 set<const SMDS_MeshNode*> & theFixedNodes,
3770 const SmoothMethod theSmoothMethod,
3771 const int theNbIterations,
3772 double theTgtAspectRatio,
3775 myLastCreatedElems.Clear();
3776 myLastCreatedNodes.Clear();
3778 MESSAGE((theSmoothMethod==LAPLACIAN ? "LAPLACIAN" : "CENTROIDAL") << "--::Smooth()");
3780 if ( theTgtAspectRatio < 1.0 )
3781 theTgtAspectRatio = 1.0;
3783 const double disttol = 1.e-16;
3785 SMESH::Controls::AspectRatio aQualityFunc;
3787 SMESHDS_Mesh* aMesh = GetMeshDS();
3789 if ( theElems.empty() ) {
3790 // add all faces to theElems
3791 SMDS_FaceIteratorPtr fIt = aMesh->facesIterator();
3792 while ( fIt->more() ) {
3793 const SMDS_MeshElement* face = fIt->next();
3794 theElems.insert( theElems.end(), face );
3797 // get all face ids theElems are on
3798 set< int > faceIdSet;
3799 TIDSortedElemSet::iterator itElem;
3801 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
3802 int fId = FindShape( *itElem );
3803 // check that corresponding submesh exists and a shape is face
3805 faceIdSet.find( fId ) == faceIdSet.end() &&
3806 aMesh->MeshElements( fId )) {
3807 TopoDS_Shape F = aMesh->IndexToShape( fId );
3808 if ( !F.IsNull() && F.ShapeType() == TopAbs_FACE )
3809 faceIdSet.insert( fId );
3812 faceIdSet.insert( 0 ); // to smooth elements that are not on any TopoDS_Face
3814 // ===============================================
3815 // smooth elements on each TopoDS_Face separately
3816 // ===============================================
3818 SMESH_MesherHelper helper( *GetMesh() );
3820 set< int >::reverse_iterator fId = faceIdSet.rbegin(); // treat 0 fId at the end
3821 for ( ; fId != faceIdSet.rend(); ++fId )
3823 // get face surface and submesh
3824 Handle(Geom_Surface) surface;
3825 SMESHDS_SubMesh* faceSubMesh = 0;
3827 double fToler2 = 0, f,l;
3828 double u1 = 0, u2 = 0, v1 = 0, v2 = 0;
3829 bool isUPeriodic = false, isVPeriodic = false;
3832 face = TopoDS::Face( aMesh->IndexToShape( *fId ));
3833 surface = BRep_Tool::Surface( face );
3834 faceSubMesh = aMesh->MeshElements( *fId );
3835 fToler2 = BRep_Tool::Tolerance( face );
3836 fToler2 *= fToler2 * 10.;
3837 isUPeriodic = surface->IsUPeriodic();
3840 isVPeriodic = surface->IsVPeriodic();
3843 surface->Bounds( u1, u2, v1, v2 );
3844 helper.SetSubShape( face );
3846 // ---------------------------------------------------------
3847 // for elements on a face, find movable and fixed nodes and
3848 // compute UV for them
3849 // ---------------------------------------------------------
3850 bool checkBoundaryNodes = false;
3851 bool isQuadratic = false;
3852 set<const SMDS_MeshNode*> setMovableNodes;
3853 map< const SMDS_MeshNode*, gp_XY* > uvMap, uvMap2;
3854 list< gp_XY > listUV; // uvs the 2 uvMaps refer to
3855 list< const SMDS_MeshElement* > elemsOnFace;
3857 Extrema_GenExtPS projector;
3858 GeomAdaptor_Surface surfAdaptor;
3859 if ( !surface.IsNull() ) {
3860 surfAdaptor.Load( surface );
3861 projector.Initialize( surfAdaptor, 20,20, 1e-5,1e-5 );
3863 int nbElemOnFace = 0;
3864 itElem = theElems.begin();
3865 // loop on not yet smoothed elements: look for elems on a face
3866 while ( itElem != theElems.end() )
3868 if ( faceSubMesh && nbElemOnFace == faceSubMesh->NbElements() )
3869 break; // all elements found
3871 const SMDS_MeshElement* elem = *itElem;
3872 if ( !elem || elem->GetType() != SMDSAbs_Face || elem->NbNodes() < 3 ||
3873 ( faceSubMesh && !faceSubMesh->Contains( elem ))) {
3877 elemsOnFace.push_back( elem );
3878 theElems.erase( itElem++ );
3882 isQuadratic = elem->IsQuadratic();
3884 // get movable nodes of elem
3885 const SMDS_MeshNode* node;
3886 SMDS_TypeOfPosition posType;
3887 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3888 int nn = 0, nbn = elem->NbNodes();
3889 if(elem->IsQuadratic())
3891 while ( nn++ < nbn ) {
3892 node = static_cast<const SMDS_MeshNode*>( itN->next() );
3893 const SMDS_PositionPtr& pos = node->GetPosition();
3894 posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
3895 if (posType != SMDS_TOP_EDGE &&
3896 posType != SMDS_TOP_VERTEX &&
3897 theFixedNodes.find( node ) == theFixedNodes.end())
3899 // check if all faces around the node are on faceSubMesh
3900 // because a node on edge may be bound to face
3901 SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
3903 if ( faceSubMesh ) {
3904 while ( eIt->more() && all ) {
3905 const SMDS_MeshElement* e = eIt->next();
3906 all = faceSubMesh->Contains( e );
3910 setMovableNodes.insert( node );
3912 checkBoundaryNodes = true;
3914 if ( posType == SMDS_TOP_3DSPACE )
3915 checkBoundaryNodes = true;
3918 if ( surface.IsNull() )
3921 // get nodes to check UV
3922 list< const SMDS_MeshNode* > uvCheckNodes;
3923 const SMDS_MeshNode* nodeInFace = 0;
3924 itN = elem->nodesIterator();
3925 nn = 0; nbn = elem->NbNodes();
3926 if(elem->IsQuadratic())
3928 while ( nn++ < nbn ) {
3929 node = static_cast<const SMDS_MeshNode*>( itN->next() );
3930 if ( node->GetPosition()->GetDim() == 2 )
3932 if ( uvMap.find( node ) == uvMap.end() )
3933 uvCheckNodes.push_back( node );
3934 // add nodes of elems sharing node
3935 // SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
3936 // while ( eIt->more() ) {
3937 // const SMDS_MeshElement* e = eIt->next();
3938 // if ( e != elem ) {
3939 // SMDS_ElemIteratorPtr nIt = e->nodesIterator();
3940 // while ( nIt->more() ) {
3941 // const SMDS_MeshNode* n =
3942 // static_cast<const SMDS_MeshNode*>( nIt->next() );
3943 // if ( uvMap.find( n ) == uvMap.end() )
3944 // uvCheckNodes.push_back( n );
3950 list< const SMDS_MeshNode* >::iterator n = uvCheckNodes.begin();
3951 for ( ; n != uvCheckNodes.end(); ++n ) {
3954 const SMDS_PositionPtr& pos = node->GetPosition();
3955 posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
3959 bool toCheck = true;
3960 uv = helper.GetNodeUV( face, node, nodeInFace, &toCheck );
3962 // compute not existing UV
3963 bool project = ( posType == SMDS_TOP_3DSPACE );
3964 // double dist1 = DBL_MAX, dist2 = 0;
3965 // if ( posType != SMDS_TOP_3DSPACE ) {
3966 // dist1 = pNode.SquareDistance( surface->Value( uv.X(), uv.Y() ));
3967 // project = dist1 > fToler2;
3969 if ( project ) { // compute new UV
3971 gp_Pnt pNode = SMESH_TNodeXYZ( node );
3972 if ( !getClosestUV( projector, pNode, newUV )) {
3973 MESSAGE("Node Projection Failed " << node);
3977 newUV.SetX( ElCLib::InPeriod( newUV.X(), u1, u2 ));
3979 newUV.SetY( ElCLib::InPeriod( newUV.Y(), v1, v2 ));
3981 // if ( posType != SMDS_TOP_3DSPACE )
3982 // dist2 = pNode.SquareDistance( surface->Value( newUV.X(), newUV.Y() ));
3983 // if ( dist2 < dist1 )
3987 // store UV in the map
3988 listUV.push_back( uv );
3989 uvMap.insert( make_pair( node, &listUV.back() ));
3991 } // loop on not yet smoothed elements
3993 if ( !faceSubMesh || nbElemOnFace != faceSubMesh->NbElements() )
3994 checkBoundaryNodes = true;
3996 // fix nodes on mesh boundary
3998 if ( checkBoundaryNodes ) {
3999 map< SMESH_TLink, int > linkNbMap; // how many times a link encounters in elemsOnFace
4000 map< SMESH_TLink, int >::iterator link_nb;
4001 // put all elements links to linkNbMap
4002 list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4003 for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
4004 const SMDS_MeshElement* elem = (*elemIt);
4005 int nbn = elem->NbCornerNodes();
4006 // loop on elem links: insert them in linkNbMap
4007 for ( int iN = 0; iN < nbn; ++iN ) {
4008 const SMDS_MeshNode* n1 = elem->GetNode( iN );
4009 const SMDS_MeshNode* n2 = elem->GetNode(( iN+1 ) % nbn);
4010 SMESH_TLink link( n1, n2 );
4011 link_nb = linkNbMap.insert( make_pair( link, 0 )).first;
4015 // remove nodes that are in links encountered only once from setMovableNodes
4016 for ( link_nb = linkNbMap.begin(); link_nb != linkNbMap.end(); ++link_nb ) {
4017 if ( link_nb->second == 1 ) {
4018 setMovableNodes.erase( link_nb->first.node1() );
4019 setMovableNodes.erase( link_nb->first.node2() );
4024 // -----------------------------------------------------
4025 // for nodes on seam edge, compute one more UV ( uvMap2 );
4026 // find movable nodes linked to nodes on seam and which
4027 // are to be smoothed using the second UV ( uvMap2 )
4028 // -----------------------------------------------------
4030 set<const SMDS_MeshNode*> nodesNearSeam; // to smooth using uvMap2
4031 if ( !surface.IsNull() ) {
4032 TopExp_Explorer eExp( face, TopAbs_EDGE );
4033 for ( ; eExp.More(); eExp.Next() ) {
4034 TopoDS_Edge edge = TopoDS::Edge( eExp.Current() );
4035 if ( !BRep_Tool::IsClosed( edge, face ))
4037 SMESHDS_SubMesh* sm = aMesh->MeshElements( edge );
4038 if ( !sm ) continue;
4039 // find out which parameter varies for a node on seam
4042 Handle(Geom2d_Curve) pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
4043 if ( pcurve.IsNull() ) continue;
4044 uv1 = pcurve->Value( f );
4046 pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
4047 if ( pcurve.IsNull() ) continue;
4048 uv2 = pcurve->Value( f );
4049 int iPar = Abs( uv1.X() - uv2.X() ) > Abs( uv1.Y() - uv2.Y() ) ? 1 : 2;
4051 if ( uv1.Coord( iPar ) > uv2.Coord( iPar ))
4052 std::swap( uv1, uv2 );
4053 // get nodes on seam and its vertices
4054 list< const SMDS_MeshNode* > seamNodes;
4055 SMDS_NodeIteratorPtr nSeamIt = sm->GetNodes();
4056 while ( nSeamIt->more() ) {
4057 const SMDS_MeshNode* node = nSeamIt->next();
4058 if ( !isQuadratic || !IsMedium( node ))
4059 seamNodes.push_back( node );
4061 TopExp_Explorer vExp( edge, TopAbs_VERTEX );
4062 for ( ; vExp.More(); vExp.Next() ) {
4063 sm = aMesh->MeshElements( vExp.Current() );
4065 nSeamIt = sm->GetNodes();
4066 while ( nSeamIt->more() )
4067 seamNodes.push_back( nSeamIt->next() );
4070 // loop on nodes on seam
4071 list< const SMDS_MeshNode* >::iterator noSeIt = seamNodes.begin();
4072 for ( ; noSeIt != seamNodes.end(); ++noSeIt ) {
4073 const SMDS_MeshNode* nSeam = *noSeIt;
4074 map< const SMDS_MeshNode*, gp_XY* >::iterator n_uv = uvMap.find( nSeam );
4075 if ( n_uv == uvMap.end() )
4078 n_uv->second->SetCoord( iPar, uv1.Coord( iPar ));
4079 // set the second UV
4080 listUV.push_back( *n_uv->second );
4081 listUV.back().SetCoord( iPar, uv2.Coord( iPar ));
4082 if ( uvMap2.empty() )
4083 uvMap2 = uvMap; // copy the uvMap contents
4084 uvMap2[ nSeam ] = &listUV.back();
4086 // collect movable nodes linked to ones on seam in nodesNearSeam
4087 SMDS_ElemIteratorPtr eIt = nSeam->GetInverseElementIterator(SMDSAbs_Face);
4088 while ( eIt->more() ) {
4089 const SMDS_MeshElement* e = eIt->next();
4090 int nbUseMap1 = 0, nbUseMap2 = 0;
4091 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
4092 int nn = 0, nbn = e->NbNodes();
4093 if(e->IsQuadratic()) nbn = nbn/2;
4094 while ( nn++ < nbn )
4096 const SMDS_MeshNode* n =
4097 static_cast<const SMDS_MeshNode*>( nIt->next() );
4099 setMovableNodes.find( n ) == setMovableNodes.end() )
4101 // add only nodes being closer to uv2 than to uv1
4102 // gp_Pnt pMid (0.5 * ( n->X() + nSeam->X() ),
4103 // 0.5 * ( n->Y() + nSeam->Y() ),
4104 // 0.5 * ( n->Z() + nSeam->Z() ));
4106 // getClosestUV( projector, pMid, uv );
4107 double x = uvMap[ n ]->Coord( iPar );
4108 if ( Abs( uv1.Coord( iPar ) - x ) >
4109 Abs( uv2.Coord( iPar ) - x )) {
4110 nodesNearSeam.insert( n );
4116 // for centroidalSmooth all element nodes must
4117 // be on one side of a seam
4118 if ( theSmoothMethod == CENTROIDAL && nbUseMap1 && nbUseMap2 ) {
4119 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
4121 while ( nn++ < nbn ) {
4122 const SMDS_MeshNode* n =
4123 static_cast<const SMDS_MeshNode*>( nIt->next() );
4124 setMovableNodes.erase( n );
4128 } // loop on nodes on seam
4129 } // loop on edge of a face
4130 } // if ( !face.IsNull() )
4132 if ( setMovableNodes.empty() ) {
4133 MESSAGE( "Face id : " << *fId << " - NO SMOOTHING: no nodes to move!!!");
4134 continue; // goto next face
4142 double maxRatio = -1., maxDisplacement = -1.;
4143 set<const SMDS_MeshNode*>::iterator nodeToMove;
4144 for ( it = 0; it < theNbIterations; it++ ) {
4145 maxDisplacement = 0.;
4146 nodeToMove = setMovableNodes.begin();
4147 for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
4148 const SMDS_MeshNode* node = (*nodeToMove);
4149 gp_XYZ aPrevPos ( node->X(), node->Y(), node->Z() );
4152 bool map2 = ( nodesNearSeam.find( node ) != nodesNearSeam.end() );
4153 if ( theSmoothMethod == LAPLACIAN )
4154 laplacianSmooth( node, surface, map2 ? uvMap2 : uvMap );
4156 centroidalSmooth( node, surface, map2 ? uvMap2 : uvMap );
4158 // node displacement
4159 gp_XYZ aNewPos ( node->X(), node->Y(), node->Z() );
4160 Standard_Real aDispl = (aPrevPos - aNewPos).SquareModulus();
4161 if ( aDispl > maxDisplacement )
4162 maxDisplacement = aDispl;
4164 // no node movement => exit
4165 //if ( maxDisplacement < 1.e-16 ) {
4166 if ( maxDisplacement < disttol ) {
4167 MESSAGE("-- no node movement --");
4171 // check elements quality
4173 list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4174 for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
4175 const SMDS_MeshElement* elem = (*elemIt);
4176 if ( !elem || elem->GetType() != SMDSAbs_Face )
4178 SMESH::Controls::TSequenceOfXYZ aPoints;
4179 if ( aQualityFunc.GetPoints( elem, aPoints )) {
4180 double aValue = aQualityFunc.GetValue( aPoints );
4181 if ( aValue > maxRatio )
4185 if ( maxRatio <= theTgtAspectRatio ) {
4186 MESSAGE("-- quality achived --");
4189 if (it+1 == theNbIterations) {
4190 MESSAGE("-- Iteration limit exceeded --");
4192 } // smoothing iterations
4194 MESSAGE(" Face id: " << *fId <<
4195 " Nb iterstions: " << it <<
4196 " Displacement: " << maxDisplacement <<
4197 " Aspect Ratio " << maxRatio);
4199 // ---------------------------------------
4200 // new nodes positions are computed,
4201 // record movement in DS and set new UV
4202 // ---------------------------------------
4203 nodeToMove = setMovableNodes.begin();
4204 for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
4205 SMDS_MeshNode* node = const_cast< SMDS_MeshNode* > (*nodeToMove);
4206 aMesh->MoveNode( node, node->X(), node->Y(), node->Z() );
4207 map< const SMDS_MeshNode*, gp_XY* >::iterator node_uv = uvMap.find( node );
4208 if ( node_uv != uvMap.end() ) {
4209 gp_XY* uv = node_uv->second;
4211 ( SMDS_PositionPtr( new SMDS_FacePosition( uv->X(), uv->Y() )));
4215 // move medium nodes of quadratic elements
4218 vector<const SMDS_MeshNode*> nodes;
4220 list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4221 for ( ; elemIt != elemsOnFace.end(); ++elemIt )
4223 const SMDS_MeshElement* QF = *elemIt;
4224 if ( QF->IsQuadratic() )
4226 nodes.assign( SMDS_MeshElement::iterator( QF->interlacedNodesElemIterator() ),
4227 SMDS_MeshElement::iterator() );
4228 nodes.push_back( nodes[0] );
4230 for (size_t i = 1; i < nodes.size(); i += 2 ) // i points to a medium node
4232 if ( !surface.IsNull() )
4234 gp_XY uv1 = helper.GetNodeUV( face, nodes[i-1], nodes[i+1], &checkUV );
4235 gp_XY uv2 = helper.GetNodeUV( face, nodes[i+1], nodes[i-1], &checkUV );
4236 gp_XY uv = helper.GetMiddleUV( surface, uv1, uv2 );
4237 xyz = surface->Value( uv.X(), uv.Y() );
4240 xyz = 0.5 * ( SMESH_TNodeXYZ( nodes[i-1] ) + SMESH_TNodeXYZ( nodes[i+1] ));
4242 if (( SMESH_TNodeXYZ( nodes[i] ) - xyz.XYZ() ).Modulus() > disttol )
4243 // we have to move a medium node
4244 aMesh->MoveNode( nodes[i], xyz.X(), xyz.Y(), xyz.Z() );
4250 } // loop on face ids
4256 //=======================================================================
4257 //function : isReverse
4258 //purpose : Return true if normal of prevNodes is not co-directied with
4259 // gp_Vec(prevNodes[iNotSame],nextNodes[iNotSame]).
4260 // iNotSame is where prevNodes and nextNodes are different.
4261 // If result is true then future volume orientation is OK
4262 //=======================================================================
4264 bool isReverse(const SMDS_MeshElement* face,
4265 const vector<const SMDS_MeshNode*>& prevNodes,
4266 const vector<const SMDS_MeshNode*>& nextNodes,
4270 SMESH_TNodeXYZ pP = prevNodes[ iNotSame ];
4271 SMESH_TNodeXYZ pN = nextNodes[ iNotSame ];
4272 gp_XYZ extrDir( pN - pP ), faceNorm;
4273 SMESH_MeshAlgos::FaceNormal( face, faceNorm, /*normalized=*/false );
4275 return faceNorm * extrDir < 0.0;
4278 //================================================================================
4280 * \brief Assure that theElemSets[0] holds elements, not nodes
4282 //================================================================================
4284 void setElemsFirst( TIDSortedElemSet theElemSets[2] )
4286 if ( !theElemSets[0].empty() &&
4287 (*theElemSets[0].begin())->GetType() == SMDSAbs_Node )
4289 std::swap( theElemSets[0], theElemSets[1] );
4291 else if ( !theElemSets[1].empty() &&
4292 (*theElemSets[1].begin())->GetType() != SMDSAbs_Node )
4294 std::swap( theElemSets[0], theElemSets[1] );
4299 //=======================================================================
4301 * \brief Create elements by sweeping an element
4302 * \param elem - element to sweep
4303 * \param newNodesItVec - nodes generated from each node of the element
4304 * \param newElems - generated elements
4305 * \param nbSteps - number of sweeping steps
4306 * \param srcElements - to append elem for each generated element
4308 //=======================================================================
4310 void SMESH_MeshEditor::sweepElement(const SMDS_MeshElement* elem,
4311 const vector<TNodeOfNodeListMapItr> & newNodesItVec,
4312 list<const SMDS_MeshElement*>& newElems,
4314 SMESH_SequenceOfElemPtr& srcElements)
4316 //MESSAGE("sweepElement " << nbSteps);
4317 SMESHDS_Mesh* aMesh = GetMeshDS();
4319 const int nbNodes = elem->NbNodes();
4320 const int nbCorners = elem->NbCornerNodes();
4321 SMDSAbs_EntityType baseType = elem->GetEntityType(); /* it can change in case of
4322 polyhedron creation !!! */
4323 // Loop on elem nodes:
4324 // find new nodes and detect same nodes indices
4325 vector < list< const SMDS_MeshNode* >::const_iterator > itNN( nbNodes );
4326 vector<const SMDS_MeshNode*> prevNod( nbNodes );
4327 vector<const SMDS_MeshNode*> nextNod( nbNodes );
4328 vector<const SMDS_MeshNode*> midlNod( nbNodes );
4330 int iNode, nbSame = 0, nbDouble = 0, iNotSameNode = 0;
4331 vector<int> sames(nbNodes);
4332 vector<bool> isSingleNode(nbNodes);
4334 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4335 TNodeOfNodeListMapItr nnIt = newNodesItVec[ iNode ];
4336 const SMDS_MeshNode* node = nnIt->first;
4337 const list< const SMDS_MeshNode* > & listNewNodes = nnIt->second;
4338 if ( listNewNodes.empty() )
4341 itNN [ iNode ] = listNewNodes.begin();
4342 prevNod[ iNode ] = node;
4343 nextNod[ iNode ] = listNewNodes.front();
4345 isSingleNode[iNode] = (listNewNodes.size()==nbSteps); /* medium node of quadratic or
4346 corner node of linear */
4347 if ( prevNod[ iNode ] != nextNod [ iNode ])
4348 nbDouble += !isSingleNode[iNode];
4350 if( iNode < nbCorners ) { // check corners only
4351 if ( prevNod[ iNode ] == nextNod [ iNode ])
4352 sames[nbSame++] = iNode;
4354 iNotSameNode = iNode;
4358 if ( nbSame == nbNodes || nbSame > 2) {
4359 MESSAGE( " Too many same nodes of element " << elem->GetID() );
4363 if ( elem->GetType() == SMDSAbs_Face && !isReverse( elem, prevNod, nextNod, iNotSameNode ))
4365 // fix nodes order to have bottom normal external
4366 if ( baseType == SMDSEntity_Polygon )
4368 std::reverse( itNN.begin(), itNN.end() );
4369 std::reverse( prevNod.begin(), prevNod.end() );
4370 std::reverse( midlNod.begin(), midlNod.end() );
4371 std::reverse( nextNod.begin(), nextNod.end() );
4372 std::reverse( isSingleNode.begin(), isSingleNode.end() );
4376 const vector<int>& ind = SMDS_MeshCell::reverseSmdsOrder( baseType, nbNodes );
4377 SMDS_MeshCell::applyInterlace( ind, itNN );
4378 SMDS_MeshCell::applyInterlace( ind, prevNod );
4379 SMDS_MeshCell::applyInterlace( ind, nextNod );
4380 SMDS_MeshCell::applyInterlace( ind, midlNod );
4381 SMDS_MeshCell::applyInterlace( ind, isSingleNode );
4384 sames[nbSame] = iNotSameNode;
4385 for ( int j = 0; j <= nbSame; ++j )
4386 for ( size_t i = 0; i < ind.size(); ++i )
4387 if ( ind[i] == sames[j] )
4392 iNotSameNode = sames[nbSame];
4396 else if ( elem->GetType() == SMDSAbs_Edge )
4398 // orient a new face same as adjacent one
4400 const SMDS_MeshElement* e;
4401 TIDSortedElemSet dummy;
4402 if (( e = SMESH_MeshAlgos::FindFaceInSet( nextNod[0], prevNod[0], dummy,dummy, &i1, &i2 )) ||
4403 ( e = SMESH_MeshAlgos::FindFaceInSet( prevNod[1], nextNod[1], dummy,dummy, &i1, &i2 )) ||
4404 ( e = SMESH_MeshAlgos::FindFaceInSet( prevNod[0], prevNod[1], dummy,dummy, &i1, &i2 )))
4406 // there is an adjacent face, check order of nodes in it
4407 bool sameOrder = ( Abs( i2 - i1 ) == 1 ) ? ( i2 > i1 ) : ( i2 < i1 );
4410 std::swap( itNN[0], itNN[1] );
4411 std::swap( prevNod[0], prevNod[1] );
4412 std::swap( nextNod[0], nextNod[1] );
4413 isSingleNode.swap( isSingleNode[0], isSingleNode[1] );
4415 sames[0] = 1 - sames[0];
4416 iNotSameNode = 1 - iNotSameNode;
4421 int iSameNode = 0, iBeforeSame = 0, iAfterSame = 0, iOpposSame = 0;
4423 iSameNode = sames[ nbSame-1 ];
4424 iBeforeSame = ( iSameNode + nbCorners - 1 ) % nbCorners;
4425 iAfterSame = ( iSameNode + 1 ) % nbCorners;
4426 iOpposSame = ( iSameNode - 2 < 0 ? iSameNode + 2 : iSameNode - 2 );
4429 if ( baseType == SMDSEntity_Polygon )
4431 if ( nbNodes == 3 ) baseType = SMDSEntity_Triangle;
4432 else if ( nbNodes == 4 ) baseType = SMDSEntity_Quadrangle;
4434 else if ( baseType == SMDSEntity_Quad_Polygon )
4436 if ( nbNodes == 6 ) baseType = SMDSEntity_Quad_Triangle;
4437 else if ( nbNodes == 8 ) baseType = SMDSEntity_Quad_Quadrangle;
4440 // make new elements
4441 for (int iStep = 0; iStep < nbSteps; iStep++ )
4444 for ( iNode = 0; iNode < nbNodes; iNode++ )
4446 midlNod[ iNode ] = isSingleNode[iNode] ? 0 : *itNN[ iNode ]++;
4447 nextNod[ iNode ] = *itNN[ iNode ]++;
4450 SMDS_MeshElement* aNewElem = 0;
4451 /*if(!elem->IsPoly())*/ {
4452 switch ( baseType ) {
4454 case SMDSEntity_Node: { // sweep NODE
4455 if ( nbSame == 0 ) {
4456 if ( isSingleNode[0] )
4457 aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ] );
4459 aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ], midlNod[ 0 ] );
4465 case SMDSEntity_Edge: { // sweep EDGE
4466 if ( nbDouble == 0 )
4468 if ( nbSame == 0 ) // ---> quadrangle
4469 aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
4470 nextNod[ 1 ], nextNod[ 0 ] );
4471 else // ---> triangle
4472 aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
4473 nextNod[ iNotSameNode ] );
4475 else // ---> polygon
4477 vector<const SMDS_MeshNode*> poly_nodes;
4478 poly_nodes.push_back( prevNod[0] );
4479 poly_nodes.push_back( prevNod[1] );
4480 if ( prevNod[1] != nextNod[1] )
4482 if ( midlNod[1]) poly_nodes.push_back( midlNod[1]);
4483 poly_nodes.push_back( nextNod[1] );
4485 if ( prevNod[0] != nextNod[0] )
4487 poly_nodes.push_back( nextNod[0] );
4488 if ( midlNod[0]) poly_nodes.push_back( midlNod[0]);
4490 switch ( poly_nodes.size() ) {
4492 aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ], poly_nodes[ 2 ]);
4495 aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ],
4496 poly_nodes[ 2 ], poly_nodes[ 3 ]);
4499 aNewElem = aMesh->AddPolygonalFace (poly_nodes);
4504 case SMDSEntity_Triangle: // TRIANGLE --->
4506 if ( nbDouble > 0 ) break;
4507 if ( nbSame == 0 ) // ---> pentahedron
4508 aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
4509 nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ] );
4511 else if ( nbSame == 1 ) // ---> pyramid
4512 aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
4513 nextNod[ iAfterSame ], nextNod[ iBeforeSame ],
4514 nextNod[ iSameNode ]);
4516 else // 2 same nodes: ---> tetrahedron
4517 aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
4518 nextNod[ iNotSameNode ]);
4521 case SMDSEntity_Quad_Edge: // sweep quadratic EDGE --->
4525 if ( nbDouble+nbSame == 2 )
4527 if(nbSame==0) { // ---> quadratic quadrangle
4528 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
4529 prevNod[2], midlNod[1], nextNod[2], midlNod[0]);
4531 else { //(nbSame==1) // ---> quadratic triangle
4533 return; // medium node on axis
4535 else if(sames[0]==0)
4536 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1],
4537 prevNod[2], midlNod[1], nextNod[2] );
4539 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[0],
4540 prevNod[2], nextNod[2], midlNod[0]);
4543 else if ( nbDouble == 3 )
4545 if ( nbSame == 0 ) { // ---> bi-quadratic quadrangle
4546 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
4547 prevNod[2], midlNod[1], nextNod[2], midlNod[0], midlNod[2]);
4554 case SMDSEntity_Quadrangle: { // sweep QUADRANGLE --->
4555 if ( nbDouble > 0 ) break;
4557 if ( nbSame == 0 ) // ---> hexahedron
4558 aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ], prevNod[ 3 ],
4559 nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ], nextNod[ 3 ]);
4561 else if ( nbSame == 1 ) { // ---> pyramid + pentahedron
4562 aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
4563 nextNod[ iAfterSame ], nextNod[ iBeforeSame ],
4564 nextNod[ iSameNode ]);
4565 newElems.push_back( aNewElem );
4566 aNewElem = aMesh->AddVolume (prevNod[ iAfterSame ], prevNod[ iOpposSame ],
4567 prevNod[ iBeforeSame ], nextNod[ iAfterSame ],
4568 nextNod[ iOpposSame ], nextNod[ iBeforeSame ] );
4570 else if ( nbSame == 2 ) { // ---> pentahedron
4571 if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] )
4572 // iBeforeSame is same too
4573 aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iOpposSame ],
4574 nextNod[ iOpposSame ], prevNod[ iSameNode ],
4575 prevNod[ iAfterSame ], nextNod[ iAfterSame ]);
4577 // iAfterSame is same too
4578 aNewElem = aMesh->AddVolume (prevNod[ iSameNode ], prevNod[ iBeforeSame ],
4579 nextNod[ iBeforeSame ], prevNod[ iAfterSame ],
4580 prevNod[ iOpposSame ], nextNod[ iOpposSame ]);
4584 case SMDSEntity_Quad_Triangle: // sweep (Bi)Quadratic TRIANGLE --->
4585 case SMDSEntity_BiQuad_Triangle: /* ??? */ {
4586 if ( nbDouble+nbSame != 3 ) break;
4588 // ---> pentahedron with 15 nodes
4589 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
4590 nextNod[0], nextNod[1], nextNod[2],
4591 prevNod[3], prevNod[4], prevNod[5],
4592 nextNod[3], nextNod[4], nextNod[5],
4593 midlNod[0], midlNod[1], midlNod[2]);
4595 else if(nbSame==1) {
4596 // ---> 2d order pyramid of 13 nodes
4597 int apex = iSameNode;
4598 int i0 = ( apex + 1 ) % nbCorners;
4599 int i1 = ( apex - 1 + nbCorners ) % nbCorners;
4603 aNewElem = aMesh->AddVolume(prevNod[i1], prevNod[i0],
4604 nextNod[i0], nextNod[i1], prevNod[apex],
4605 prevNod[i01], midlNod[i0],
4606 nextNod[i01], midlNod[i1],
4607 prevNod[i1a], prevNod[i0a],
4608 nextNod[i0a], nextNod[i1a]);
4610 else if(nbSame==2) {
4611 // ---> 2d order tetrahedron of 10 nodes
4612 int n1 = iNotSameNode;
4613 int n2 = ( n1 + 1 ) % nbCorners;
4614 int n3 = ( n1 + nbCorners - 1 ) % nbCorners;
4618 aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], prevNod[n3], nextNod[n1],
4619 prevNod[n12], prevNod[n23], prevNod[n31],
4620 midlNod[n1], nextNod[n12], nextNod[n31]);
4624 case SMDSEntity_Quad_Quadrangle: { // sweep Quadratic QUADRANGLE --->
4626 if ( nbDouble != 4 ) break;
4627 // ---> hexahedron with 20 nodes
4628 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
4629 nextNod[0], nextNod[1], nextNod[2], nextNod[3],
4630 prevNod[4], prevNod[5], prevNod[6], prevNod[7],
4631 nextNod[4], nextNod[5], nextNod[6], nextNod[7],
4632 midlNod[0], midlNod[1], midlNod[2], midlNod[3]);
4634 else if(nbSame==1) {
4635 // ---> pyramid + pentahedron - can not be created since it is needed
4636 // additional middle node at the center of face
4637 INFOS( " Sweep for face " << elem->GetID() << " can not be created" );
4640 else if( nbSame == 2 ) {
4641 if ( nbDouble != 2 ) break;
4642 // ---> 2d order Pentahedron with 15 nodes
4644 if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] ) {
4645 // iBeforeSame is same too
4652 // iAfterSame is same too
4662 aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], nextNod[n2],
4663 prevNod[n4], prevNod[n5], nextNod[n5],
4664 prevNod[n12], midlNod[n2], nextNod[n12],
4665 prevNod[n45], midlNod[n5], nextNod[n45],
4666 prevNod[n14], prevNod[n25], nextNod[n25]);
4670 case SMDSEntity_BiQuad_Quadrangle: { // sweep BiQuadratic QUADRANGLE --->
4672 if( nbSame == 0 && nbDouble == 9 ) {
4673 // ---> tri-quadratic hexahedron with 27 nodes
4674 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
4675 nextNod[0], nextNod[1], nextNod[2], nextNod[3],
4676 prevNod[4], prevNod[5], prevNod[6], prevNod[7],
4677 nextNod[4], nextNod[5], nextNod[6], nextNod[7],
4678 midlNod[0], midlNod[1], midlNod[2], midlNod[3],
4679 prevNod[8], // bottom center
4680 midlNod[4], midlNod[5], midlNod[6], midlNod[7],
4681 nextNod[8], // top center
4682 midlNod[8]);// elem center
4690 case SMDSEntity_Polygon: { // sweep POLYGON
4692 if ( nbNodes == 6 && nbSame == 0 && nbDouble == 0 ) {
4693 // ---> hexagonal prism
4694 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
4695 prevNod[3], prevNod[4], prevNod[5],
4696 nextNod[0], nextNod[1], nextNod[2],
4697 nextNod[3], nextNod[4], nextNod[5]);
4701 case SMDSEntity_Ball:
4706 } // switch ( baseType )
4709 if ( !aNewElem && elem->GetType() == SMDSAbs_Face ) // try to create a polyherdal prism
4711 if ( baseType != SMDSEntity_Polygon )
4713 const std::vector<int>& ind = SMDS_MeshCell::interlacedSmdsOrder(baseType,nbNodes);
4714 SMDS_MeshCell::applyInterlace( ind, prevNod );
4715 SMDS_MeshCell::applyInterlace( ind, nextNod );
4716 SMDS_MeshCell::applyInterlace( ind, midlNod );
4717 SMDS_MeshCell::applyInterlace( ind, itNN );
4718 SMDS_MeshCell::applyInterlace( ind, isSingleNode );
4719 baseType = SMDSEntity_Polygon; // WARNING: change baseType !!!!
4721 vector<const SMDS_MeshNode*> polyedre_nodes (nbNodes*2 + 4*nbNodes);
4722 vector<int> quantities (nbNodes + 2);
4723 polyedre_nodes.clear();
4727 for (int inode = 0; inode < nbNodes; inode++)
4728 polyedre_nodes.push_back( prevNod[inode] );
4729 quantities.push_back( nbNodes );
4732 polyedre_nodes.push_back( nextNod[0] );
4733 for (int inode = nbNodes; inode-1; --inode )
4734 polyedre_nodes.push_back( nextNod[inode-1] );
4735 quantities.push_back( nbNodes );
4743 const int iQuad = elem->IsQuadratic();
4744 for (int iface = 0; iface < nbNodes; iface += 1+iQuad )
4746 const int prevNbNodes = polyedre_nodes.size(); // to detect degenerated face
4747 int inextface = (iface+1+iQuad) % nbNodes;
4748 int imid = (iface+1) % nbNodes;
4749 polyedre_nodes.push_back( prevNod[inextface] ); // 0
4750 if ( iQuad ) polyedre_nodes.push_back( prevNod[imid] ); // 4
4751 polyedre_nodes.push_back( prevNod[iface] ); // 1
4752 if ( prevNod[iface] != nextNod[iface] ) // 1 != 2
4754 if ( midlNod[ iface ]) polyedre_nodes.push_back( midlNod[ iface ]); // 5
4755 polyedre_nodes.push_back( nextNod[iface] ); // 2
4757 if ( iQuad ) polyedre_nodes.push_back( nextNod[imid] ); // 6
4758 if ( prevNod[inextface] != nextNod[inextface] ) // 0 != 3
4760 polyedre_nodes.push_back( nextNod[inextface] ); // 3
4761 if ( midlNod[ inextface ]) polyedre_nodes.push_back( midlNod[ inextface ]);// 7
4763 const int nbFaceNodes = polyedre_nodes.size() - prevNbNodes;
4764 if ( nbFaceNodes > 2 )
4765 quantities.push_back( nbFaceNodes );
4766 else // degenerated face
4767 polyedre_nodes.resize( prevNbNodes );
4769 aNewElem = aMesh->AddPolyhedralVolume (polyedre_nodes, quantities);
4771 } // try to create a polyherdal prism
4774 newElems.push_back( aNewElem );
4775 myLastCreatedElems.Append(aNewElem);
4776 srcElements.Append( elem );
4779 // set new prev nodes
4780 for ( iNode = 0; iNode < nbNodes; iNode++ )
4781 prevNod[ iNode ] = nextNod[ iNode ];
4786 //=======================================================================
4788 * \brief Create 1D and 2D elements around swept elements
4789 * \param mapNewNodes - source nodes and ones generated from them
4790 * \param newElemsMap - source elements and ones generated from them
4791 * \param elemNewNodesMap - nodes generated from each node of each element
4792 * \param elemSet - all swept elements
4793 * \param nbSteps - number of sweeping steps
4794 * \param srcElements - to append elem for each generated element
4796 //=======================================================================
4798 void SMESH_MeshEditor::makeWalls (TNodeOfNodeListMap & mapNewNodes,
4799 TTElemOfElemListMap & newElemsMap,
4800 TElemOfVecOfNnlmiMap & elemNewNodesMap,
4801 TIDSortedElemSet& elemSet,
4803 SMESH_SequenceOfElemPtr& srcElements)
4805 ASSERT( newElemsMap.size() == elemNewNodesMap.size() );
4806 SMESHDS_Mesh* aMesh = GetMeshDS();
4808 // Find nodes belonging to only one initial element - sweep them into edges.
4810 TNodeOfNodeListMapItr nList = mapNewNodes.begin();
4811 for ( ; nList != mapNewNodes.end(); nList++ )
4813 const SMDS_MeshNode* node =
4814 static_cast<const SMDS_MeshNode*>( nList->first );
4815 if ( newElemsMap.count( node ))
4816 continue; // node was extruded into edge
4817 SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator();
4818 int nbInitElems = 0;
4819 const SMDS_MeshElement* el = 0;
4820 SMDSAbs_ElementType highType = SMDSAbs_Edge; // count most complex elements only
4821 while ( eIt->more() && nbInitElems < 2 ) {
4822 const SMDS_MeshElement* e = eIt->next();
4823 SMDSAbs_ElementType type = e->GetType();
4824 if ( type == SMDSAbs_Volume || type < highType ) continue;
4825 if ( type > highType ) {
4830 nbInitElems += elemSet.count(el);
4832 if ( nbInitElems < 2 ) {
4833 bool NotCreateEdge = el && el->IsMediumNode(node);
4834 if(!NotCreateEdge) {
4835 vector<TNodeOfNodeListMapItr> newNodesItVec( 1, nList );
4836 list<const SMDS_MeshElement*> newEdges;
4837 sweepElement( node, newNodesItVec, newEdges, nbSteps, srcElements );
4842 // Make a ceiling for each element ie an equal element of last new nodes.
4843 // Find free links of faces - make edges and sweep them into faces.
4845 ElemFeatures polyFace( SMDSAbs_Face, /*isPoly=*/true ), anyFace;
4847 TTElemOfElemListMap::iterator itElem = newElemsMap.begin();
4848 TElemOfVecOfNnlmiMap::iterator itElemNodes = elemNewNodesMap.begin();
4849 for ( ; itElem != newElemsMap.end(); itElem++, itElemNodes++ )
4851 const SMDS_MeshElement* elem = itElem->first;
4852 vector<TNodeOfNodeListMapItr>& vecNewNodes = itElemNodes->second;
4854 if(itElem->second.size()==0) continue;
4856 const bool isQuadratic = elem->IsQuadratic();
4858 if ( elem->GetType() == SMDSAbs_Edge ) {
4859 // create a ceiling edge
4860 if ( !isQuadratic ) {
4861 if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
4862 vecNewNodes[ 1 ]->second.back())) {
4863 myLastCreatedElems.Append(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
4864 vecNewNodes[ 1 ]->second.back()));
4865 srcElements.Append( elem );
4869 if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
4870 vecNewNodes[ 1 ]->second.back(),
4871 vecNewNodes[ 2 ]->second.back())) {
4872 myLastCreatedElems.Append(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
4873 vecNewNodes[ 1 ]->second.back(),
4874 vecNewNodes[ 2 ]->second.back()));
4875 srcElements.Append( elem );
4879 if ( elem->GetType() != SMDSAbs_Face )
4882 bool hasFreeLinks = false;
4884 TIDSortedElemSet avoidSet;
4885 avoidSet.insert( elem );
4887 set<const SMDS_MeshNode*> aFaceLastNodes;
4888 int iNode, nbNodes = vecNewNodes.size();
4889 if ( !isQuadratic ) {
4890 // loop on the face nodes
4891 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4892 aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4893 // look for free links of the face
4894 int iNext = ( iNode + 1 == nbNodes ) ? 0 : iNode + 1;
4895 const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
4896 const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
4897 // check if a link n1-n2 is free
4898 if ( ! SMESH_MeshAlgos::FindFaceInSet ( n1, n2, elemSet, avoidSet )) {
4899 hasFreeLinks = true;
4900 // make a new edge and a ceiling for a new edge
4901 const SMDS_MeshElement* edge;
4902 if ( ! ( edge = aMesh->FindEdge( n1, n2 ))) {
4903 myLastCreatedElems.Append( edge = aMesh->AddEdge( n1, n2 )); // free link edge
4904 srcElements.Append( myLastCreatedElems.Last() );
4906 n1 = vecNewNodes[ iNode ]->second.back();
4907 n2 = vecNewNodes[ iNext ]->second.back();
4908 if ( !aMesh->FindEdge( n1, n2 )) {
4909 myLastCreatedElems.Append(aMesh->AddEdge( n1, n2 )); // new edge ceiling
4910 srcElements.Append( edge );
4915 else { // elem is quadratic face
4916 int nbn = nbNodes/2;
4917 for ( iNode = 0; iNode < nbn; iNode++ ) {
4918 aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4919 int iNext = ( iNode + 1 == nbn ) ? 0 : iNode + 1;
4920 const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
4921 const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
4922 const SMDS_MeshNode* n3 = vecNewNodes[ iNode+nbn ]->first;
4923 // check if a link is free
4924 if ( ! SMESH_MeshAlgos::FindFaceInSet ( n1, n2, elemSet, avoidSet ) &&
4925 ! SMESH_MeshAlgos::FindFaceInSet ( n1, n3, elemSet, avoidSet ) &&
4926 ! SMESH_MeshAlgos::FindFaceInSet ( n3, n2, elemSet, avoidSet ) ) {
4927 hasFreeLinks = true;
4928 // make an edge and a ceiling for a new edge
4930 if ( !aMesh->FindEdge( n1, n2, n3 )) {
4931 myLastCreatedElems.Append(aMesh->AddEdge( n1, n2, n3 )); // free link edge
4932 srcElements.Append( elem );
4934 n1 = vecNewNodes[ iNode ]->second.back();
4935 n2 = vecNewNodes[ iNext ]->second.back();
4936 n3 = vecNewNodes[ iNode+nbn ]->second.back();
4937 if ( !aMesh->FindEdge( n1, n2, n3 )) {
4938 myLastCreatedElems.Append(aMesh->AddEdge( n1, n2, n3 )); // ceiling edge
4939 srcElements.Append( elem );
4943 for ( iNode = nbn; iNode < nbNodes; iNode++ ) {
4944 aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4948 // sweep free links into faces
4950 if ( hasFreeLinks ) {
4951 list<const SMDS_MeshElement*> & newVolumes = itElem->second;
4952 int iVol, volNb, nbVolumesByStep = newVolumes.size() / nbSteps;
4954 set<const SMDS_MeshNode*> initNodeSet, topNodeSet, faceNodeSet;
4955 set<const SMDS_MeshNode*> initNodeSetNoCenter/*, topNodeSetNoCenter*/;
4956 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4957 initNodeSet.insert( vecNewNodes[ iNode ]->first );
4958 topNodeSet .insert( vecNewNodes[ iNode ]->second.back() );
4960 if ( isQuadratic && nbNodes % 2 ) { // node set for the case of a biquadratic
4961 initNodeSetNoCenter = initNodeSet; // swept face and a not biquadratic volume
4962 initNodeSetNoCenter.erase( vecNewNodes.back()->first );
4964 for ( volNb = 0; volNb < nbVolumesByStep; volNb++ ) {
4965 list<const SMDS_MeshElement*>::iterator v = newVolumes.begin();
4966 std::advance( v, volNb );
4967 // find indices of free faces of a volume and their source edges
4968 list< int > freeInd;
4969 list< const SMDS_MeshElement* > srcEdges; // source edges of free faces
4970 SMDS_VolumeTool vTool( *v, /*ignoreCentralNodes=*/false );
4971 int iF, nbF = vTool.NbFaces();
4972 for ( iF = 0; iF < nbF; iF ++ ) {
4973 if (vTool.IsFreeFace( iF ) &&
4974 vTool.GetFaceNodes( iF, faceNodeSet ) &&
4975 initNodeSet != faceNodeSet) // except an initial face
4977 if ( nbSteps == 1 && faceNodeSet == topNodeSet )
4979 if ( faceNodeSet == initNodeSetNoCenter )
4981 freeInd.push_back( iF );
4982 // find source edge of a free face iF
4983 vector<const SMDS_MeshNode*> commonNodes; // shared by the initial and free faces
4984 vector<const SMDS_MeshNode*>::iterator lastCommom;
4985 commonNodes.resize( nbNodes, 0 );
4986 lastCommom = std::set_intersection( faceNodeSet.begin(), faceNodeSet.end(),
4987 initNodeSet.begin(), initNodeSet.end(),
4988 commonNodes.begin());
4989 if ( std::distance( commonNodes.begin(), lastCommom ) == 3 )
4990 srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1],commonNodes[2]));
4992 srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1]));
4994 if ( !srcEdges.back() )
4996 cout << "SMESH_MeshEditor::makeWalls(), no source edge found for a free face #"
4997 << iF << " of volume #" << vTool.ID() << endl;
5002 if ( freeInd.empty() )
5005 // create wall faces for all steps;
5006 // if such a face has been already created by sweep of edge,
5007 // assure that its orientation is OK
5008 for ( int iStep = 0; iStep < nbSteps; iStep++ )
5010 vTool.Set( *v, /*ignoreCentralNodes=*/false );
5011 vTool.SetExternalNormal();
5012 const int nextShift = vTool.IsForward() ? +1 : -1;
5013 list< int >::iterator ind = freeInd.begin();
5014 list< const SMDS_MeshElement* >::iterator srcEdge = srcEdges.begin();
5015 for ( ; ind != freeInd.end(); ++ind, ++srcEdge ) // loop on free faces
5017 const SMDS_MeshNode** nodes = vTool.GetFaceNodes( *ind );
5018 int nbn = vTool.NbFaceNodes( *ind );
5019 const SMDS_MeshElement * f = 0;
5020 if ( nbn == 3 ) ///// triangle
5022 f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ]);
5024 nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
5026 const SMDS_MeshNode* newOrder[3] = { nodes[ 1 - nextShift ],
5028 nodes[ 1 + nextShift ] };
5030 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5032 myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
5036 else if ( nbn == 4 ) ///// quadrangle
5038 f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ], nodes[ 3 ]);
5040 nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
5042 const SMDS_MeshNode* newOrder[4] = { nodes[ 0 ], nodes[ 2-nextShift ],
5043 nodes[ 2 ], nodes[ 2+nextShift ] };
5045 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5047 myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
5048 newOrder[ 2 ], newOrder[ 3 ]));
5051 else if ( nbn == 6 && isQuadratic ) /////// quadratic triangle
5053 f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[1], nodes[3], nodes[5] );
5055 nodes[2] != f->GetNodeWrap( f->GetNodeIndex( nodes[0] ) + 2*nextShift ))
5057 const SMDS_MeshNode* newOrder[6] = { nodes[2 - 2*nextShift],
5059 nodes[2 + 2*nextShift],
5060 nodes[3 - 2*nextShift],
5062 nodes[3 + 2*nextShift]};
5064 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5066 myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ],
5074 else if ( nbn == 8 && isQuadratic ) /////// quadratic quadrangle
5076 f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[6],
5077 nodes[1], nodes[3], nodes[5], nodes[7] );
5079 nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
5081 const SMDS_MeshNode* newOrder[8] = { nodes[0],
5082 nodes[4 - 2*nextShift],
5084 nodes[4 + 2*nextShift],
5086 nodes[5 - 2*nextShift],
5088 nodes[5 + 2*nextShift] };
5090 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5092 myLastCreatedElems.Append(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
5093 newOrder[ 2 ], newOrder[ 3 ],
5094 newOrder[ 4 ], newOrder[ 5 ],
5095 newOrder[ 6 ], newOrder[ 7 ]));
5098 else if ( nbn == 9 && isQuadratic ) /////// bi-quadratic quadrangle
5100 f = aMesh->FindElement( vector<const SMDS_MeshNode*>( nodes, nodes+nbn ),
5101 SMDSAbs_Face, /*noMedium=*/false);
5103 nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
5105 const SMDS_MeshNode* newOrder[9] = { nodes[0],
5106 nodes[4 - 2*nextShift],
5108 nodes[4 + 2*nextShift],
5110 nodes[5 - 2*nextShift],
5112 nodes[5 + 2*nextShift],
5115 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
5117 myLastCreatedElems.Append(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
5118 newOrder[ 2 ], newOrder[ 3 ],
5119 newOrder[ 4 ], newOrder[ 5 ],
5120 newOrder[ 6 ], newOrder[ 7 ],
5124 else //////// polygon
5126 vector<const SMDS_MeshNode*> polygon_nodes ( nodes, nodes+nbn );
5127 const SMDS_MeshFace * f = aMesh->FindFace( polygon_nodes );
5129 nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + nextShift ))
5131 if ( !vTool.IsForward() )
5132 std::reverse( polygon_nodes.begin(), polygon_nodes.end());
5134 aMesh->ChangeElementNodes( f, &polygon_nodes[0], nbn );
5136 AddElement( polygon_nodes, polyFace.SetQuad( (*v)->IsQuadratic() ));
5140 while ( srcElements.Length() < myLastCreatedElems.Length() )
5141 srcElements.Append( *srcEdge );
5143 } // loop on free faces
5145 // go to the next volume
5147 while ( iVol++ < nbVolumesByStep ) v++;
5150 } // loop on volumes of one step
5151 } // sweep free links into faces
5153 // Make a ceiling face with a normal external to a volume
5155 // use SMDS_VolumeTool to get a correctly ordered nodes of a ceiling face
5156 SMDS_VolumeTool lastVol( itElem->second.back(), /*ignoreCentralNodes=*/false );
5157 int iF = lastVol.GetFaceIndex( aFaceLastNodes );
5159 if ( iF < 0 && isQuadratic && nbNodes % 2 ) { // remove a central node of biquadratic
5160 aFaceLastNodes.erase( vecNewNodes.back()->second.back() );
5161 iF = lastVol.GetFaceIndex( aFaceLastNodes );
5165 lastVol.SetExternalNormal();
5166 const SMDS_MeshNode** nodes = lastVol.GetFaceNodes( iF );
5167 const int nbn = lastVol.NbFaceNodes( iF );
5168 vector<const SMDS_MeshNode*> nodeVec( nodes, nodes+nbn );
5169 if ( !hasFreeLinks ||
5170 !aMesh->FindElement( nodeVec, SMDSAbs_Face, /*noMedium=*/false) )
5172 const vector<int>& interlace =
5173 SMDS_MeshCell::interlacedSmdsOrder( elem->GetEntityType(), nbn );
5174 SMDS_MeshCell::applyInterlaceRev( interlace, nodeVec );
5176 if ( const SMDS_MeshElement* face = AddElement( nodeVec, anyFace.Init( elem )))
5177 myLastCreatedElems.Append( face );
5179 while ( srcElements.Length() < myLastCreatedElems.Length() )
5180 srcElements.Append( elem );
5183 } // loop on swept elements
5186 //=======================================================================
5187 //function : RotationSweep
5189 //=======================================================================
5191 SMESH_MeshEditor::PGroupIDs
5192 SMESH_MeshEditor::RotationSweep(TIDSortedElemSet theElemSets[2],
5193 const gp_Ax1& theAxis,
5194 const double theAngle,
5195 const int theNbSteps,
5196 const double theTol,
5197 const bool theMakeGroups,
5198 const bool theMakeWalls)
5200 myLastCreatedElems.Clear();
5201 myLastCreatedNodes.Clear();
5203 // source elements for each generated one
5204 SMESH_SequenceOfElemPtr srcElems, srcNodes;
5206 MESSAGE( "RotationSweep()");
5208 aTrsf.SetRotation( theAxis, theAngle );
5210 aTrsf2.SetRotation( theAxis, theAngle/2. );
5212 gp_Lin aLine( theAxis );
5213 double aSqTol = theTol * theTol;
5215 SMESHDS_Mesh* aMesh = GetMeshDS();
5217 TNodeOfNodeListMap mapNewNodes;
5218 TElemOfVecOfNnlmiMap mapElemNewNodes;
5219 TTElemOfElemListMap newElemsMap;
5221 const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
5222 myMesh->NbFaces(ORDER_QUADRATIC) +
5223 myMesh->NbVolumes(ORDER_QUADRATIC) );
5224 // loop on theElemSets
5225 setElemsFirst( theElemSets );
5226 TIDSortedElemSet::iterator itElem;
5227 for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
5229 TIDSortedElemSet& theElems = theElemSets[ is2ndSet ];
5230 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
5231 const SMDS_MeshElement* elem = *itElem;
5232 if ( !elem || elem->GetType() == SMDSAbs_Volume )
5234 vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5235 newNodesItVec.reserve( elem->NbNodes() );
5237 // loop on elem nodes
5238 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5239 while ( itN->more() )
5241 const SMDS_MeshNode* node = cast2Node( itN->next() );
5243 gp_XYZ aXYZ( node->X(), node->Y(), node->Z() );
5245 aXYZ.Coord( coord[0], coord[1], coord[2] );
5246 bool isOnAxis = ( aLine.SquareDistance( aXYZ ) <= aSqTol );
5248 // check if a node has been already sweeped
5249 TNodeOfNodeListMapItr nIt =
5250 mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5251 list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5252 if ( listNewNodes.empty() )
5254 // check if we are to create medium nodes between corner ones
5255 bool needMediumNodes = false;
5256 if ( isQuadraticMesh )
5258 SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
5259 while (it->more() && !needMediumNodes )
5261 const SMDS_MeshElement* invElem = it->next();
5262 if ( invElem != elem && !theElems.count( invElem )) continue;
5263 needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
5264 if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
5265 needMediumNodes = true;
5270 const SMDS_MeshNode * newNode = node;
5271 for ( int i = 0; i < theNbSteps; i++ ) {
5273 if ( needMediumNodes ) // create a medium node
5275 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
5276 newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5277 myLastCreatedNodes.Append(newNode);
5278 srcNodes.Append( node );
5279 listNewNodes.push_back( newNode );
5280 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
5283 aTrsf.Transforms( coord[0], coord[1], coord[2] );
5285 // create a corner node
5286 newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5287 myLastCreatedNodes.Append(newNode);
5288 srcNodes.Append( node );
5289 listNewNodes.push_back( newNode );
5292 listNewNodes.push_back( newNode );
5293 // if ( needMediumNodes )
5294 // listNewNodes.push_back( newNode );
5298 newNodesItVec.push_back( nIt );
5300 // make new elements
5301 sweepElement( elem, newNodesItVec, newElemsMap[elem], theNbSteps, srcElems );
5306 makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElemSets[0], theNbSteps, srcElems );
5308 PGroupIDs newGroupIDs;
5309 if ( theMakeGroups )
5310 newGroupIDs = generateGroups( srcNodes, srcElems, "rotated");
5315 //=======================================================================
5316 //function : ExtrusParam
5317 //purpose : standard construction
5318 //=======================================================================
5320 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const gp_Vec& theStep,
5321 const int theNbSteps,
5323 const double theTolerance):
5325 myFlags( theFlags ),
5326 myTolerance( theTolerance ),
5327 myElemsToUse( NULL )
5329 mySteps = new TColStd_HSequenceOfReal;
5330 const double stepSize = theStep.Magnitude();
5331 for (int i=1; i<=theNbSteps; i++ )
5332 mySteps->Append( stepSize );
5334 if (( theFlags & EXTRUSION_FLAG_SEW ) &&
5335 ( theTolerance > 0 ))
5337 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDirAndSew;
5341 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDir;
5345 //=======================================================================
5346 //function : ExtrusParam
5347 //purpose : steps are given explicitly
5348 //=======================================================================
5350 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const gp_Dir& theDir,
5351 Handle(TColStd_HSequenceOfReal) theSteps,
5353 const double theTolerance):
5355 mySteps( theSteps ),
5356 myFlags( theFlags ),
5357 myTolerance( theTolerance ),
5358 myElemsToUse( NULL )
5360 if (( theFlags & EXTRUSION_FLAG_SEW ) &&
5361 ( theTolerance > 0 ))
5363 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDirAndSew;
5367 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByDir;
5371 //=======================================================================
5372 //function : ExtrusParam
5373 //purpose : for extrusion by normal
5374 //=======================================================================
5376 SMESH_MeshEditor::ExtrusParam::ExtrusParam( const double theStepSize,
5377 const int theNbSteps,
5381 mySteps( new TColStd_HSequenceOfReal ),
5382 myFlags( theFlags ),
5384 myElemsToUse( NULL )
5386 for (int i = 0; i < theNbSteps; i++ )
5387 mySteps->Append( theStepSize );
5391 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByNormal1D;
5395 myMakeNodesFun = & SMESH_MeshEditor::ExtrusParam::makeNodesByNormal2D;
5399 //=======================================================================
5400 //function : ExtrusParam::SetElementsToUse
5401 //purpose : stores elements to use for extrusion by normal, depending on
5402 // state of EXTRUSION_FLAG_USE_INPUT_ELEMS_ONLY flag
5403 //=======================================================================
5405 void SMESH_MeshEditor::ExtrusParam::SetElementsToUse( const TIDSortedElemSet& elems )
5407 myElemsToUse = ToUseInpElemsOnly() ? & elems : 0;
5410 //=======================================================================
5411 //function : ExtrusParam::beginStepIter
5412 //purpose : prepare iteration on steps
5413 //=======================================================================
5415 void SMESH_MeshEditor::ExtrusParam::beginStepIter( bool withMediumNodes )
5417 myWithMediumNodes = withMediumNodes;
5421 //=======================================================================
5422 //function : ExtrusParam::moreSteps
5423 //purpose : are there more steps?
5424 //=======================================================================
5426 bool SMESH_MeshEditor::ExtrusParam::moreSteps()
5428 return myNextStep <= mySteps->Length() || !myCurSteps.empty();
5430 //=======================================================================
5431 //function : ExtrusParam::nextStep
5432 //purpose : returns the next step
5433 //=======================================================================
5435 double SMESH_MeshEditor::ExtrusParam::nextStep()
5438 if ( !myCurSteps.empty() )
5440 res = myCurSteps.back();
5441 myCurSteps.pop_back();
5443 else if ( myNextStep <= mySteps->Length() )
5445 myCurSteps.push_back( mySteps->Value( myNextStep ));
5447 if ( myWithMediumNodes )
5449 myCurSteps.back() /= 2.;
5450 myCurSteps.push_back( myCurSteps.back() );
5457 //=======================================================================
5458 //function : ExtrusParam::makeNodesByDir
5459 //purpose : create nodes for standard extrusion
5460 //=======================================================================
5462 int SMESH_MeshEditor::ExtrusParam::
5463 makeNodesByDir( SMESHDS_Mesh* mesh,
5464 const SMDS_MeshNode* srcNode,
5465 std::list<const SMDS_MeshNode*> & newNodes,
5466 const bool makeMediumNodes)
5468 gp_XYZ p = SMESH_TNodeXYZ( srcNode );
5471 for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5473 p += myDir.XYZ() * nextStep();
5474 const SMDS_MeshNode * newNode = mesh->AddNode( p.X(), p.Y(), p.Z() );
5475 newNodes.push_back( newNode );
5480 //=======================================================================
5481 //function : ExtrusParam::makeNodesByDirAndSew
5482 //purpose : create nodes for standard extrusion with sewing
5483 //=======================================================================
5485 int SMESH_MeshEditor::ExtrusParam::
5486 makeNodesByDirAndSew( SMESHDS_Mesh* mesh,
5487 const SMDS_MeshNode* srcNode,
5488 std::list<const SMDS_MeshNode*> & newNodes,
5489 const bool makeMediumNodes)
5491 gp_XYZ P1 = SMESH_TNodeXYZ( srcNode );
5494 for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5496 P1 += myDir.XYZ() * nextStep();
5498 // try to search in sequence of existing nodes
5499 // if myNodes.Length()>0 we 'nave to use given sequence
5500 // else - use all nodes of mesh
5501 const SMDS_MeshNode * node = 0;
5502 if ( myNodes.Length() > 0 ) {
5504 for(i=1; i<=myNodes.Length(); i++) {
5505 gp_XYZ P2 = SMESH_TNodeXYZ( myNodes.Value(i) );
5506 if (( P1 - P2 ).SquareModulus() < myTolerance * myTolerance )
5508 node = myNodes.Value(i);
5514 SMDS_NodeIteratorPtr itn = mesh->nodesIterator();
5515 while(itn->more()) {
5516 SMESH_TNodeXYZ P2( itn->next() );
5517 if (( P1 - P2 ).SquareModulus() < myTolerance * myTolerance )
5526 node = mesh->AddNode( P1.X(), P1.Y(), P1.Z() );
5528 newNodes.push_back( node );
5535 //=======================================================================
5536 //function : ExtrusParam::makeNodesByNormal2D
5537 //purpose : create nodes for extrusion using normals of faces
5538 //=======================================================================
5540 int SMESH_MeshEditor::ExtrusParam::
5541 makeNodesByNormal2D( SMESHDS_Mesh* mesh,
5542 const SMDS_MeshNode* srcNode,
5543 std::list<const SMDS_MeshNode*> & newNodes,
5544 const bool makeMediumNodes)
5546 const bool alongAvgNorm = ( myFlags & EXTRUSION_FLAG_BY_AVG_NORMAL );
5548 gp_XYZ p = SMESH_TNodeXYZ( srcNode );
5550 // get normals to faces sharing srcNode
5551 vector< gp_XYZ > norms, baryCenters;
5552 gp_XYZ norm, avgNorm( 0,0,0 );
5553 SMDS_ElemIteratorPtr faceIt = srcNode->GetInverseElementIterator( SMDSAbs_Face );
5554 while ( faceIt->more() )
5556 const SMDS_MeshElement* face = faceIt->next();
5557 if ( myElemsToUse && !myElemsToUse->count( face ))
5559 if ( SMESH_MeshAlgos::FaceNormal( face, norm, /*normalized=*/true ))
5561 norms.push_back( norm );
5563 if ( !alongAvgNorm )
5567 for ( SMDS_ElemIteratorPtr nIt = face->nodesIterator(); nIt->more(); ++nbN )
5568 bc += SMESH_TNodeXYZ( nIt->next() );
5569 baryCenters.push_back( bc / nbN );
5574 if ( norms.empty() ) return 0;
5576 double normSize = avgNorm.Modulus();
5577 if ( normSize < std::numeric_limits<double>::min() )
5580 if ( myFlags & EXTRUSION_FLAG_BY_AVG_NORMAL ) // extrude along avgNorm
5583 return makeNodesByDir( mesh, srcNode, newNodes, makeMediumNodes );
5586 avgNorm /= normSize;
5589 for ( beginStepIter( makeMediumNodes ); moreSteps(); ++nbNodes ) // loop on steps
5592 double stepSize = nextStep();
5594 if ( norms.size() > 1 )
5596 for ( size_t iF = 0; iF < norms.size(); ++iF ) // loop on faces
5598 // translate plane of a face
5599 baryCenters[ iF ] += norms[ iF ] * stepSize;
5601 // find point of intersection of the face plane located at baryCenters[ iF ]
5602 // and avgNorm located at pNew
5603 double d = -( norms[ iF ] * baryCenters[ iF ]); // d of plane equation ax+by+cz+d=0
5604 double dot = ( norms[ iF ] * avgNorm );
5605 if ( dot < std::numeric_limits<double>::min() )
5606 dot = stepSize * 1e-3;
5607 double step = -( norms[ iF ] * pNew + d ) / dot;
5608 pNew += step * avgNorm;
5613 pNew += stepSize * avgNorm;
5617 const SMDS_MeshNode * newNode = mesh->AddNode( p.X(), p.Y(), p.Z() );
5618 newNodes.push_back( newNode );
5623 //=======================================================================
5624 //function : ExtrusParam::makeNodesByNormal1D
5625 //purpose : create nodes for extrusion using normals of edges
5626 //=======================================================================
5628 int SMESH_MeshEditor::ExtrusParam::
5629 makeNodesByNormal1D( SMESHDS_Mesh* mesh,
5630 const SMDS_MeshNode* srcNode,
5631 std::list<const SMDS_MeshNode*> & newNodes,
5632 const bool makeMediumNodes)
5634 throw SALOME_Exception("Extrusion 1D by Normal not implemented");
5638 //=======================================================================
5639 //function : ExtrusionSweep
5641 //=======================================================================
5643 SMESH_MeshEditor::PGroupIDs
5644 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet theElems[2],
5645 const gp_Vec& theStep,
5646 const int theNbSteps,
5647 TTElemOfElemListMap& newElemsMap,
5649 const double theTolerance)
5651 ExtrusParam aParams( theStep, theNbSteps, theFlags, theTolerance );
5652 return ExtrusionSweep( theElems, aParams, newElemsMap );
5656 //=======================================================================
5657 //function : ExtrusionSweep
5659 //=======================================================================
5661 SMESH_MeshEditor::PGroupIDs
5662 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet theElemSets[2],
5663 ExtrusParam& theParams,
5664 TTElemOfElemListMap& newElemsMap)
5666 myLastCreatedElems.Clear();
5667 myLastCreatedNodes.Clear();
5669 // source elements for each generated one
5670 SMESH_SequenceOfElemPtr srcElems, srcNodes;
5672 SMESHDS_Mesh* aMesh = GetMeshDS();
5674 setElemsFirst( theElemSets );
5675 const int nbSteps = theParams.NbSteps();
5676 theParams.SetElementsToUse( theElemSets[0] );
5678 TNodeOfNodeListMap mapNewNodes;
5679 //TNodeOfNodeVecMap mapNewNodes;
5680 TElemOfVecOfNnlmiMap mapElemNewNodes;
5681 //TElemOfVecOfMapNodesMap mapElemNewNodes;
5683 const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
5684 myMesh->NbFaces(ORDER_QUADRATIC) +
5685 myMesh->NbVolumes(ORDER_QUADRATIC) );
5687 TIDSortedElemSet::iterator itElem;
5688 for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
5690 TIDSortedElemSet& theElems = theElemSets[ is2ndSet ];
5691 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
5693 // check element type
5694 const SMDS_MeshElement* elem = *itElem;
5695 if ( !elem || elem->GetType() == SMDSAbs_Volume )
5698 const size_t nbNodes = elem->NbNodes();
5699 vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5700 newNodesItVec.reserve( nbNodes );
5702 // loop on elem nodes
5703 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5704 while ( itN->more() )
5706 // check if a node has been already sweeped
5707 const SMDS_MeshNode* node = cast2Node( itN->next() );
5708 TNodeOfNodeListMap::iterator nIt =
5709 mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5710 list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5711 if ( listNewNodes.empty() )
5715 // check if we are to create medium nodes between corner ones
5716 bool needMediumNodes = false;
5717 if ( isQuadraticMesh )
5719 SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
5720 while (it->more() && !needMediumNodes )
5722 const SMDS_MeshElement* invElem = it->next();
5723 if ( invElem != elem && !theElems.count( invElem )) continue;
5724 needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
5725 if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
5726 needMediumNodes = true;
5729 // create nodes for all steps
5730 if ( theParams.MakeNodes( GetMeshDS(), node, listNewNodes, needMediumNodes ))
5732 list<const SMDS_MeshNode*>::iterator newNodesIt = listNewNodes.begin();
5733 for ( ; newNodesIt != listNewNodes.end(); ++newNodesIt )
5735 myLastCreatedNodes.Append( *newNodesIt );
5736 srcNodes.Append( node );
5741 break; // newNodesItVec will be shorter than nbNodes
5744 newNodesItVec.push_back( nIt );
5746 // make new elements
5747 if ( newNodesItVec.size() == nbNodes )
5748 sweepElement( elem, newNodesItVec, newElemsMap[elem], nbSteps, srcElems );
5752 if ( theParams.ToMakeBoundary() ) {
5753 makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElemSets[0], nbSteps, srcElems );
5755 PGroupIDs newGroupIDs;
5756 if ( theParams.ToMakeGroups() )
5757 newGroupIDs = generateGroups( srcNodes, srcElems, "extruded");
5762 //=======================================================================
5763 //function : ExtrusionAlongTrack
5765 //=======================================================================
5766 SMESH_MeshEditor::Extrusion_Error
5767 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet theElements[2],
5768 SMESH_subMesh* theTrack,
5769 const SMDS_MeshNode* theN1,
5770 const bool theHasAngles,
5771 list<double>& theAngles,
5772 const bool theLinearVariation,
5773 const bool theHasRefPoint,
5774 const gp_Pnt& theRefPoint,
5775 const bool theMakeGroups)
5777 MESSAGE("ExtrusionAlongTrack");
5778 myLastCreatedElems.Clear();
5779 myLastCreatedNodes.Clear();
5782 std::list<double> aPrms;
5783 TIDSortedElemSet::iterator itElem;
5786 TopoDS_Edge aTrackEdge;
5787 TopoDS_Vertex aV1, aV2;
5789 SMDS_ElemIteratorPtr aItE;
5790 SMDS_NodeIteratorPtr aItN;
5791 SMDSAbs_ElementType aTypeE;
5793 TNodeOfNodeListMap mapNewNodes;
5796 aNbE = theElements[0].size() + theElements[1].size();
5799 return EXTR_NO_ELEMENTS;
5801 // 1.1 Track Pattern
5804 SMESHDS_SubMesh* pSubMeshDS = theTrack->GetSubMeshDS();
5806 aItE = pSubMeshDS->GetElements();
5807 while ( aItE->more() ) {
5808 const SMDS_MeshElement* pE = aItE->next();
5809 aTypeE = pE->GetType();
5810 // Pattern must contain links only
5811 if ( aTypeE != SMDSAbs_Edge )
5812 return EXTR_PATH_NOT_EDGE;
5815 list<SMESH_MeshEditor_PathPoint> fullList;
5817 const TopoDS_Shape& aS = theTrack->GetSubShape();
5818 // Sub-shape for the Pattern must be an Edge or Wire
5819 if( aS.ShapeType() == TopAbs_EDGE ) {
5820 aTrackEdge = TopoDS::Edge( aS );
5821 // the Edge must not be degenerated
5822 if ( SMESH_Algo::isDegenerated( aTrackEdge ) )
5823 return EXTR_BAD_PATH_SHAPE;
5824 TopExp::Vertices( aTrackEdge, aV1, aV2 );
5825 aItN = theTrack->GetFather()->GetSubMesh( aV1 )->GetSubMeshDS()->GetNodes();
5826 const SMDS_MeshNode* aN1 = aItN->next();
5827 aItN = theTrack->GetFather()->GetSubMesh( aV2 )->GetSubMeshDS()->GetNodes();
5828 const SMDS_MeshNode* aN2 = aItN->next();
5829 // starting node must be aN1 or aN2
5830 if ( !( aN1 == theN1 || aN2 == theN1 ) )
5831 return EXTR_BAD_STARTING_NODE;
5832 aItN = pSubMeshDS->GetNodes();
5833 while ( aItN->more() ) {
5834 const SMDS_MeshNode* pNode = aItN->next();
5835 const SMDS_EdgePosition* pEPos =
5836 static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
5837 double aT = pEPos->GetUParameter();
5838 aPrms.push_back( aT );
5840 //Extrusion_Error err =
5841 MakeEdgePathPoints(aPrms, aTrackEdge, (aN1==theN1), fullList);
5842 } else if( aS.ShapeType() == TopAbs_WIRE ) {
5843 list< SMESH_subMesh* > LSM;
5844 TopTools_SequenceOfShape Edges;
5845 SMESH_subMeshIteratorPtr itSM = theTrack->getDependsOnIterator(false,true);
5846 while(itSM->more()) {
5847 SMESH_subMesh* SM = itSM->next();
5849 const TopoDS_Shape& aS = SM->GetSubShape();
5852 list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
5853 int startNid = theN1->GetID();
5854 TColStd_MapOfInteger UsedNums;
5856 int NbEdges = Edges.Length();
5858 for(; i<=NbEdges; i++) {
5860 list< SMESH_subMesh* >::iterator itLSM = LSM.begin();
5861 for(; itLSM!=LSM.end(); itLSM++) {
5863 if(UsedNums.Contains(k)) continue;
5864 aTrackEdge = TopoDS::Edge( Edges.Value(k) );
5865 SMESH_subMesh* locTrack = *itLSM;
5866 SMESHDS_SubMesh* locMeshDS = locTrack->GetSubMeshDS();
5867 TopExp::Vertices( aTrackEdge, aV1, aV2 );
5868 aItN = locTrack->GetFather()->GetSubMesh(aV1)->GetSubMeshDS()->GetNodes();
5869 const SMDS_MeshNode* aN1 = aItN->next();
5870 aItN = locTrack->GetFather()->GetSubMesh(aV2)->GetSubMeshDS()->GetNodes();
5871 const SMDS_MeshNode* aN2 = aItN->next();
5872 // starting node must be aN1 or aN2
5873 if ( !( aN1->GetID() == startNid || aN2->GetID() == startNid ) ) continue;
5874 // 2. Collect parameters on the track edge
5876 aItN = locMeshDS->GetNodes();
5877 while ( aItN->more() ) {
5878 const SMDS_MeshNode* pNode = aItN->next();
5879 const SMDS_EdgePosition* pEPos =
5880 static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
5881 double aT = pEPos->GetUParameter();
5882 aPrms.push_back( aT );
5884 list<SMESH_MeshEditor_PathPoint> LPP;
5885 //Extrusion_Error err =
5886 MakeEdgePathPoints(aPrms, aTrackEdge,(aN1->GetID()==startNid), LPP);
5887 LLPPs.push_back(LPP);
5889 // update startN for search following egde
5890 if( aN1->GetID() == startNid ) startNid = aN2->GetID();
5891 else startNid = aN1->GetID();
5895 list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
5896 list<SMESH_MeshEditor_PathPoint> firstList = *itLLPP;
5897 list<SMESH_MeshEditor_PathPoint>::iterator itPP = firstList.begin();
5898 for(; itPP!=firstList.end(); itPP++) {
5899 fullList.push_back( *itPP );
5901 SMESH_MeshEditor_PathPoint PP1 = fullList.back();
5902 fullList.pop_back();
5904 for(; itLLPP!=LLPPs.end(); itLLPP++) {
5905 list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
5906 itPP = currList.begin();
5907 SMESH_MeshEditor_PathPoint PP2 = currList.front();
5908 gp_Dir D1 = PP1.Tangent();
5909 gp_Dir D2 = PP2.Tangent();
5910 gp_Dir Dnew( gp_Vec( (D1.X()+D2.X())/2, (D1.Y()+D2.Y())/2,
5911 (D1.Z()+D2.Z())/2 ) );
5912 PP1.SetTangent(Dnew);
5913 fullList.push_back(PP1);
5915 for(; itPP!=firstList.end(); itPP++) {
5916 fullList.push_back( *itPP );
5918 PP1 = fullList.back();
5919 fullList.pop_back();
5921 // if wire not closed
5922 fullList.push_back(PP1);
5926 return EXTR_BAD_PATH_SHAPE;
5929 return MakeExtrElements(theElements, fullList, theHasAngles, theAngles, theLinearVariation,
5930 theHasRefPoint, theRefPoint, theMakeGroups);
5934 //=======================================================================
5935 //function : ExtrusionAlongTrack
5937 //=======================================================================
5938 SMESH_MeshEditor::Extrusion_Error
5939 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet theElements[2],
5940 SMESH_Mesh* theTrack,
5941 const SMDS_MeshNode* theN1,
5942 const bool theHasAngles,
5943 list<double>& theAngles,
5944 const bool theLinearVariation,
5945 const bool theHasRefPoint,
5946 const gp_Pnt& theRefPoint,
5947 const bool theMakeGroups)
5949 myLastCreatedElems.Clear();
5950 myLastCreatedNodes.Clear();
5953 std::list<double> aPrms;
5954 TIDSortedElemSet::iterator itElem;
5957 TopoDS_Edge aTrackEdge;
5958 TopoDS_Vertex aV1, aV2;
5960 SMDS_ElemIteratorPtr aItE;
5961 SMDS_NodeIteratorPtr aItN;
5962 SMDSAbs_ElementType aTypeE;
5964 TNodeOfNodeListMap mapNewNodes;
5967 aNbE = theElements[0].size() + theElements[1].size();
5970 return EXTR_NO_ELEMENTS;
5972 // 1.1 Track Pattern
5975 SMESHDS_Mesh* pMeshDS = theTrack->GetMeshDS();
5977 aItE = pMeshDS->elementsIterator();
5978 while ( aItE->more() ) {
5979 const SMDS_MeshElement* pE = aItE->next();
5980 aTypeE = pE->GetType();
5981 // Pattern must contain links only
5982 if ( aTypeE != SMDSAbs_Edge )
5983 return EXTR_PATH_NOT_EDGE;
5986 list<SMESH_MeshEditor_PathPoint> fullList;
5988 const TopoDS_Shape& aS = theTrack->GetShapeToMesh();
5990 if ( !theTrack->HasShapeToMesh() ) {
5991 //Mesh without shape
5992 const SMDS_MeshNode* currentNode = NULL;
5993 const SMDS_MeshNode* prevNode = theN1;
5994 std::vector<const SMDS_MeshNode*> aNodesList;
5995 aNodesList.push_back(theN1);
5996 int nbEdges = 0, conn=0;
5997 const SMDS_MeshElement* prevElem = NULL;
5998 const SMDS_MeshElement* currentElem = NULL;
5999 int totalNbEdges = theTrack->NbEdges();
6000 SMDS_ElemIteratorPtr nIt;
6003 if( !theTrack->GetMeshDS()->Contains(theN1) ) {
6004 return EXTR_BAD_STARTING_NODE;
6007 conn = nbEdgeConnectivity(theN1);
6009 return EXTR_PATH_NOT_EDGE;
6011 aItE = theN1->GetInverseElementIterator();
6012 prevElem = aItE->next();
6013 currentElem = prevElem;
6015 if(totalNbEdges == 1 ) {
6016 nIt = currentElem->nodesIterator();
6017 currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
6018 if(currentNode == prevNode)
6019 currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
6020 aNodesList.push_back(currentNode);
6022 nIt = currentElem->nodesIterator();
6023 while( nIt->more() ) {
6024 currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
6025 if(currentNode == prevNode)
6026 currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
6027 aNodesList.push_back(currentNode);
6029 //case of the closed mesh
6030 if(currentNode == theN1) {
6035 conn = nbEdgeConnectivity(currentNode);
6037 return EXTR_PATH_NOT_EDGE;
6038 }else if( conn == 1 && nbEdges > 0 ) {
6043 prevNode = currentNode;
6044 aItE = currentNode->GetInverseElementIterator();
6045 currentElem = aItE->next();
6046 if( currentElem == prevElem)
6047 currentElem = aItE->next();
6048 nIt = currentElem->nodesIterator();
6049 prevElem = currentElem;
6055 if(nbEdges != totalNbEdges)
6056 return EXTR_PATH_NOT_EDGE;
6058 TopTools_SequenceOfShape Edges;
6059 double x1,x2,y1,y2,z1,z2;
6060 list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
6061 int startNid = theN1->GetID();
6062 for(int i = 1; i < aNodesList.size(); i++) {
6063 x1 = aNodesList[i-1]->X();x2 = aNodesList[i]->X();
6064 y1 = aNodesList[i-1]->Y();y2 = aNodesList[i]->Y();
6065 z1 = aNodesList[i-1]->Z();z2 = aNodesList[i]->Z();
6066 TopoDS_Edge e = BRepBuilderAPI_MakeEdge(gp_Pnt(x1,y1,z1),gp_Pnt(x2,y2,z2));
6067 list<SMESH_MeshEditor_PathPoint> LPP;
6069 MakeEdgePathPoints(aPrms, e, (aNodesList[i-1]->GetID()==startNid), LPP);
6070 LLPPs.push_back(LPP);
6071 if( aNodesList[i-1]->GetID() == startNid ) startNid = aNodesList[i]->GetID();
6072 else startNid = aNodesList[i-1]->GetID();
6076 list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
6077 list<SMESH_MeshEditor_PathPoint> firstList = *itLLPP;
6078 list<SMESH_MeshEditor_PathPoint>::iterator itPP = firstList.begin();
6079 for(; itPP!=firstList.end(); itPP++) {
6080 fullList.push_back( *itPP );
6083 SMESH_MeshEditor_PathPoint PP1 = fullList.back();
6084 SMESH_MeshEditor_PathPoint PP2;
6085 fullList.pop_back();
6087 for(; itLLPP!=LLPPs.end(); itLLPP++) {
6088 list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
6089 itPP = currList.begin();
6090 PP2 = currList.front();
6091 gp_Dir D1 = PP1.Tangent();
6092 gp_Dir D2 = PP2.Tangent();
6093 gp_Dir Dnew( gp_Vec( (D1.X()+D2.X())/2, (D1.Y()+D2.Y())/2,
6094 (D1.Z()+D2.Z())/2 ) );
6095 PP1.SetTangent(Dnew);
6096 fullList.push_back(PP1);
6098 for(; itPP!=currList.end(); itPP++) {
6099 fullList.push_back( *itPP );
6101 PP1 = fullList.back();
6102 fullList.pop_back();
6104 fullList.push_back(PP1);
6106 } // Sub-shape for the Pattern must be an Edge or Wire
6107 else if( aS.ShapeType() == TopAbs_EDGE ) {
6108 aTrackEdge = TopoDS::Edge( aS );
6109 // the Edge must not be degenerated
6110 if ( SMESH_Algo::isDegenerated( aTrackEdge ) )
6111 return EXTR_BAD_PATH_SHAPE;
6112 TopExp::Vertices( aTrackEdge, aV1, aV2 );
6113 const SMDS_MeshNode* aN1 = SMESH_Algo::VertexNode( aV1, pMeshDS );
6114 const SMDS_MeshNode* aN2 = SMESH_Algo::VertexNode( aV2, pMeshDS );
6115 // starting node must be aN1 or aN2
6116 if ( !( aN1 == theN1 || aN2 == theN1 ) )
6117 return EXTR_BAD_STARTING_NODE;
6118 aItN = pMeshDS->nodesIterator();
6119 while ( aItN->more() ) {
6120 const SMDS_MeshNode* pNode = aItN->next();
6121 if( pNode==aN1 || pNode==aN2 ) continue;
6122 const SMDS_EdgePosition* pEPos =
6123 static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
6124 double aT = pEPos->GetUParameter();
6125 aPrms.push_back( aT );
6127 //Extrusion_Error err =
6128 MakeEdgePathPoints(aPrms, aTrackEdge, (aN1==theN1), fullList);
6130 else if( aS.ShapeType() == TopAbs_WIRE ) {
6131 list< SMESH_subMesh* > LSM;
6132 TopTools_SequenceOfShape Edges;
6133 TopExp_Explorer eExp(aS, TopAbs_EDGE);
6134 for(; eExp.More(); eExp.Next()) {
6135 TopoDS_Edge E = TopoDS::Edge( eExp.Current() );
6136 if( SMESH_Algo::isDegenerated(E) ) continue;
6137 SMESH_subMesh* SM = theTrack->GetSubMesh(E);
6143 list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
6144 TopoDS_Vertex aVprev;
6145 TColStd_MapOfInteger UsedNums;
6146 int NbEdges = Edges.Length();
6148 for(; i<=NbEdges; i++) {
6150 list< SMESH_subMesh* >::iterator itLSM = LSM.begin();
6151 for(; itLSM!=LSM.end(); itLSM++) {
6153 if(UsedNums.Contains(k)) continue;
6154 aTrackEdge = TopoDS::Edge( Edges.Value(k) );
6155 SMESH_subMesh* locTrack = *itLSM;
6156 SMESHDS_SubMesh* locMeshDS = locTrack->GetSubMeshDS();
6157 TopExp::Vertices( aTrackEdge, aV1, aV2 );
6158 bool aN1isOK = false, aN2isOK = false;
6159 if ( aVprev.IsNull() ) {
6160 // if previous vertex is not yet defined, it means that we in the beginning of wire
6161 // and we have to find initial vertex corresponding to starting node theN1
6162 const SMDS_MeshNode* aN1 = SMESH_Algo::VertexNode( aV1, pMeshDS );
6163 const SMDS_MeshNode* aN2 = SMESH_Algo::VertexNode( aV2, pMeshDS );
6164 // starting node must be aN1 or aN2
6165 aN1isOK = ( aN1 && aN1 == theN1 );
6166 aN2isOK = ( aN2 && aN2 == theN1 );
6169 // we have specified ending vertex of the previous edge on the previous iteration
6170 // and we have just to check that it corresponds to any vertex in current segment
6171 aN1isOK = aVprev.IsSame( aV1 );
6172 aN2isOK = aVprev.IsSame( aV2 );
6174 if ( !aN1isOK && !aN2isOK ) continue;
6175 // 2. Collect parameters on the track edge
6177 aItN = locMeshDS->GetNodes();
6178 while ( aItN->more() ) {
6179 const SMDS_MeshNode* pNode = aItN->next();
6180 const SMDS_EdgePosition* pEPos =
6181 static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
6182 double aT = pEPos->GetUParameter();
6183 aPrms.push_back( aT );
6185 list<SMESH_MeshEditor_PathPoint> LPP;
6186 //Extrusion_Error err =
6187 MakeEdgePathPoints(aPrms, aTrackEdge, aN1isOK, LPP);
6188 LLPPs.push_back(LPP);
6190 // update startN for search following egde
6191 if ( aN1isOK ) aVprev = aV2;
6196 list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
6197 list<SMESH_MeshEditor_PathPoint>& firstList = *itLLPP;
6198 fullList.splice( fullList.end(), firstList );
6200 SMESH_MeshEditor_PathPoint PP1 = fullList.back();
6201 fullList.pop_back();
6203 for(; itLLPP!=LLPPs.end(); itLLPP++) {
6204 list<SMESH_MeshEditor_PathPoint>& currList = *itLLPP;
6205 SMESH_MeshEditor_PathPoint PP2 = currList.front();
6206 gp_Dir D1 = PP1.Tangent();
6207 gp_Dir D2 = PP2.Tangent();
6208 gp_Dir Dnew( ( D1.XYZ() + D2.XYZ() ) / 2 );
6209 PP1.SetTangent(Dnew);
6210 fullList.push_back(PP1);
6211 fullList.splice( fullList.end(), currList, ++currList.begin(), currList.end() );
6212 PP1 = fullList.back();
6213 fullList.pop_back();
6215 // if wire not closed
6216 fullList.push_back(PP1);
6220 return EXTR_BAD_PATH_SHAPE;
6223 return MakeExtrElements(theElements, fullList, theHasAngles, theAngles, theLinearVariation,
6224 theHasRefPoint, theRefPoint, theMakeGroups);
6228 //=======================================================================
6229 //function : MakeEdgePathPoints
6230 //purpose : auxilary for ExtrusionAlongTrack
6231 //=======================================================================
6232 SMESH_MeshEditor::Extrusion_Error
6233 SMESH_MeshEditor::MakeEdgePathPoints(std::list<double>& aPrms,
6234 const TopoDS_Edge& aTrackEdge,
6236 list<SMESH_MeshEditor_PathPoint>& LPP)
6238 Standard_Real aTx1, aTx2, aL2, aTolVec, aTolVec2;
6240 aTolVec2=aTolVec*aTolVec;
6242 TopoDS_Vertex aV1, aV2;
6243 TopExp::Vertices( aTrackEdge, aV1, aV2 );
6244 aT1=BRep_Tool::Parameter( aV1, aTrackEdge );
6245 aT2=BRep_Tool::Parameter( aV2, aTrackEdge );
6246 // 2. Collect parameters on the track edge
6247 aPrms.push_front( aT1 );
6248 aPrms.push_back( aT2 );
6251 if( FirstIsStart ) {
6262 SMESH_MeshEditor_PathPoint aPP;
6263 Handle(Geom_Curve) aC3D = BRep_Tool::Curve( aTrackEdge, aTx1, aTx2 );
6264 std::list<double>::iterator aItD = aPrms.begin();
6265 for(; aItD != aPrms.end(); ++aItD) {
6269 aC3D->D1( aT, aP3D, aVec );
6270 aL2 = aVec.SquareMagnitude();
6271 if ( aL2 < aTolVec2 )
6272 return EXTR_CANT_GET_TANGENT;
6273 gp_Dir aTgt( aVec );
6275 aPP.SetTangent( aTgt );
6276 aPP.SetParameter( aT );
6283 //=======================================================================
6284 //function : MakeExtrElements
6285 //purpose : auxilary for ExtrusionAlongTrack
6286 //=======================================================================
6287 SMESH_MeshEditor::Extrusion_Error
6288 SMESH_MeshEditor::MakeExtrElements(TIDSortedElemSet theElemSets[2],
6289 list<SMESH_MeshEditor_PathPoint>& fullList,
6290 const bool theHasAngles,
6291 list<double>& theAngles,
6292 const bool theLinearVariation,
6293 const bool theHasRefPoint,
6294 const gp_Pnt& theRefPoint,
6295 const bool theMakeGroups)
6297 const int aNbTP = fullList.size();
6299 if( theHasAngles && !theAngles.empty() && theLinearVariation )
6300 LinearAngleVariation(aNbTP-1, theAngles);
6301 // fill vector of path points with angles
6302 vector<SMESH_MeshEditor_PathPoint> aPPs;
6303 list<SMESH_MeshEditor_PathPoint>::iterator itPP = fullList.begin();
6304 list<double>::iterator itAngles = theAngles.begin();
6305 aPPs.push_back( *itPP++ );
6306 for( ; itPP != fullList.end(); itPP++) {
6307 aPPs.push_back( *itPP );
6308 if ( theHasAngles && itAngles != theAngles.end() )
6309 aPPs.back().SetAngle( *itAngles++ );
6312 TNodeOfNodeListMap mapNewNodes;
6313 TElemOfVecOfNnlmiMap mapElemNewNodes;
6314 TTElemOfElemListMap newElemsMap;
6315 TIDSortedElemSet::iterator itElem;
6316 // source elements for each generated one
6317 SMESH_SequenceOfElemPtr srcElems, srcNodes;
6319 // 3. Center of rotation aV0
6320 gp_Pnt aV0 = theRefPoint;
6321 if ( !theHasRefPoint )
6323 gp_XYZ aGC( 0.,0.,0. );
6324 TIDSortedElemSet newNodes;
6326 for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
6328 TIDSortedElemSet& theElements = theElemSets[ is2ndSet ];
6329 itElem = theElements.begin();
6330 for ( ; itElem != theElements.end(); itElem++ ) {
6331 const SMDS_MeshElement* elem = *itElem;
6333 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6334 while ( itN->more() ) {
6335 const SMDS_MeshElement* node = itN->next();
6336 if ( newNodes.insert( node ).second )
6337 aGC += SMESH_TNodeXYZ( node );
6341 aGC /= newNodes.size();
6343 } // if (!theHasRefPoint) {
6345 // 4. Processing the elements
6346 SMESHDS_Mesh* aMesh = GetMeshDS();
6348 setElemsFirst( theElemSets );
6349 for ( int is2ndSet = 0; is2ndSet < 2; ++is2ndSet )
6351 TIDSortedElemSet& theElements = theElemSets[ is2ndSet ];
6352 for ( itElem = theElements.begin(); itElem != theElements.end(); itElem++ ) {
6353 // check element type
6354 const SMDS_MeshElement* elem = *itElem;
6357 // SMDSAbs_ElementType aTypeE = elem->GetType();
6358 // if ( aTypeE != SMDSAbs_Face && aTypeE != SMDSAbs_Edge )
6361 vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
6362 newNodesItVec.reserve( elem->NbNodes() );
6364 // loop on elem nodes
6366 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6367 while ( itN->more() )
6370 // check if a node has been already processed
6371 const SMDS_MeshNode* node =
6372 static_cast<const SMDS_MeshNode*>( itN->next() );
6373 TNodeOfNodeListMap::iterator nIt = mapNewNodes.find( node );
6374 if ( nIt == mapNewNodes.end() ) {
6375 nIt = mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
6376 list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
6379 Standard_Real aAngle1x, aAngleT1T0, aTolAng;
6380 gp_Pnt aP0x, aP1x, aPN0, aPN1, aV0x, aV1x;
6381 gp_Ax1 anAx1, anAxT1T0;
6382 gp_Dir aDT1x, aDT0x, aDT1T0;
6387 aPN0 = SMESH_TNodeXYZ( node );
6389 const SMESH_MeshEditor_PathPoint& aPP0 = aPPs[0];
6391 aDT0x= aPP0.Tangent();
6392 //cout<<"j = 0 PP: Pnt("<<aP0x.X()<<","<<aP0x.Y()<<","<<aP0x.Z()<<")"<<endl;
6394 for ( int j = 1; j < aNbTP; ++j ) {
6395 const SMESH_MeshEditor_PathPoint& aPP1 = aPPs[j];
6397 aDT1x = aPP1.Tangent();
6398 aAngle1x = aPP1.Angle();
6400 gp_Trsf aTrsf, aTrsfRot, aTrsfRotT1T0;
6402 gp_Vec aV01x( aP0x, aP1x );
6403 aTrsf.SetTranslation( aV01x );
6406 aV1x = aV0x.Transformed( aTrsf );
6407 aPN1 = aPN0.Transformed( aTrsf );
6409 // rotation 1 [ T1,T0 ]
6410 aAngleT1T0=-aDT1x.Angle( aDT0x );
6411 if (fabs(aAngleT1T0) > aTolAng) {
6413 anAxT1T0.SetLocation( aV1x );
6414 anAxT1T0.SetDirection( aDT1T0 );
6415 aTrsfRotT1T0.SetRotation( anAxT1T0, aAngleT1T0 );
6417 aPN1 = aPN1.Transformed( aTrsfRotT1T0 );
6421 if ( theHasAngles ) {
6422 anAx1.SetLocation( aV1x );
6423 anAx1.SetDirection( aDT1x );
6424 aTrsfRot.SetRotation( anAx1, aAngle1x );
6426 aPN1 = aPN1.Transformed( aTrsfRot );
6430 //MESSAGE("elem->IsQuadratic " << elem->IsQuadratic() << " " << elem->IsMediumNode(node));
6431 if( elem->IsQuadratic() && !elem->IsMediumNode(node) ) {
6432 // create additional node
6433 double x = ( aPN1.X() + aPN0.X() )/2.;
6434 double y = ( aPN1.Y() + aPN0.Y() )/2.;
6435 double z = ( aPN1.Z() + aPN0.Z() )/2.;
6436 const SMDS_MeshNode* newNode = aMesh->AddNode(x,y,z);
6437 myLastCreatedNodes.Append(newNode);
6438 srcNodes.Append( node );
6439 listNewNodes.push_back( newNode );
6441 const SMDS_MeshNode* newNode = aMesh->AddNode( aPN1.X(), aPN1.Y(), aPN1.Z() );
6442 myLastCreatedNodes.Append(newNode);
6443 srcNodes.Append( node );
6444 listNewNodes.push_back( newNode );
6454 // if current elem is quadratic and current node is not medium
6455 // we have to check - may be it is needed to insert additional nodes
6456 if( elem->IsQuadratic() && !elem->IsMediumNode(node) ) {
6457 list< const SMDS_MeshNode* > & listNewNodes = nIt->second;
6458 if(listNewNodes.size()==aNbTP-1) {
6459 vector<const SMDS_MeshNode*> aNodes(2*(aNbTP-1));
6460 gp_XYZ P(node->X(), node->Y(), node->Z());
6461 list< const SMDS_MeshNode* >::iterator it = listNewNodes.begin();
6463 for(i=0; i<aNbTP-1; i++) {
6464 const SMDS_MeshNode* N = *it;
6465 double x = ( N->X() + P.X() )/2.;
6466 double y = ( N->Y() + P.Y() )/2.;
6467 double z = ( N->Z() + P.Z() )/2.;
6468 const SMDS_MeshNode* newN = aMesh->AddNode(x,y,z);
6469 srcNodes.Append( node );
6470 myLastCreatedNodes.Append(newN);
6473 P = gp_XYZ(N->X(),N->Y(),N->Z());
6475 listNewNodes.clear();
6476 for(i=0; i<2*(aNbTP-1); i++) {
6477 listNewNodes.push_back(aNodes[i]);
6483 newNodesItVec.push_back( nIt );
6485 // make new elements
6486 //sweepElement( aMesh, elem, newNodesItVec, newElemsMap[elem],
6487 // newNodesItVec[0]->second.size(), myLastCreatedElems );
6488 sweepElement( elem, newNodesItVec, newElemsMap[elem], aNbTP-1, srcElems );
6492 makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElemSets[0], aNbTP-1, srcElems );
6494 if ( theMakeGroups )
6495 generateGroups( srcNodes, srcElems, "extruded");
6501 //=======================================================================
6502 //function : LinearAngleVariation
6503 //purpose : auxilary for ExtrusionAlongTrack
6504 //=======================================================================
6505 void SMESH_MeshEditor::LinearAngleVariation(const int nbSteps,
6506 list<double>& Angles)
6508 int nbAngles = Angles.size();
6509 if( nbSteps > nbAngles ) {
6510 vector<double> theAngles(nbAngles);
6511 list<double>::iterator it = Angles.begin();
6513 for(; it!=Angles.end(); it++) {
6515 theAngles[i] = (*it);
6518 double rAn2St = double( nbAngles ) / double( nbSteps );
6519 double angPrev = 0, angle;
6520 for ( int iSt = 0; iSt < nbSteps; ++iSt ) {
6521 double angCur = rAn2St * ( iSt+1 );
6522 double angCurFloor = floor( angCur );
6523 double angPrevFloor = floor( angPrev );
6524 if ( angPrevFloor == angCurFloor )
6525 angle = rAn2St * theAngles[ int( angCurFloor ) ];
6527 int iP = int( angPrevFloor );
6528 double angPrevCeil = ceil(angPrev);
6529 angle = ( angPrevCeil - angPrev ) * theAngles[ iP ];
6531 int iC = int( angCurFloor );
6532 if ( iC < nbAngles )
6533 angle += ( angCur - angCurFloor ) * theAngles[ iC ];
6535 iP = int( angPrevCeil );
6537 angle += theAngles[ iC ];
6539 res.push_back(angle);
6544 for(; it!=res.end(); it++)
6545 Angles.push_back( *it );
6550 //================================================================================
6552 * \brief Move or copy theElements applying theTrsf to their nodes
6553 * \param theElems - elements to transform, if theElems is empty then apply to all mesh nodes
6554 * \param theTrsf - transformation to apply
6555 * \param theCopy - if true, create translated copies of theElems
6556 * \param theMakeGroups - if true and theCopy, create translated groups
6557 * \param theTargetMesh - mesh to copy translated elements into
6558 * \return SMESH_MeshEditor::PGroupIDs - list of ids of created groups
6560 //================================================================================
6562 SMESH_MeshEditor::PGroupIDs
6563 SMESH_MeshEditor::Transform (TIDSortedElemSet & theElems,
6564 const gp_Trsf& theTrsf,
6566 const bool theMakeGroups,
6567 SMESH_Mesh* theTargetMesh)
6569 myLastCreatedElems.Clear();
6570 myLastCreatedNodes.Clear();
6572 bool needReverse = false;
6573 string groupPostfix;
6574 switch ( theTrsf.Form() ) {
6576 MESSAGE("gp_PntMirror");
6578 groupPostfix = "mirrored";
6581 MESSAGE("gp_Ax1Mirror");
6582 groupPostfix = "mirrored";
6585 MESSAGE("gp_Ax2Mirror");
6587 groupPostfix = "mirrored";
6590 MESSAGE("gp_Rotation");
6591 groupPostfix = "rotated";
6593 case gp_Translation:
6594 MESSAGE("gp_Translation");
6595 groupPostfix = "translated";
6598 MESSAGE("gp_Scale");
6599 groupPostfix = "scaled";
6601 case gp_CompoundTrsf: // different scale by axis
6602 MESSAGE("gp_CompoundTrsf");
6603 groupPostfix = "scaled";
6607 needReverse = false;
6608 groupPostfix = "transformed";
6611 SMESHDS_Mesh* aTgtMesh = theTargetMesh ? theTargetMesh->GetMeshDS() : 0;
6612 SMESHDS_Mesh* aMesh = GetMeshDS();
6614 SMESH_MeshEditor targetMeshEditor( theTargetMesh );
6615 SMESH_MeshEditor* editor = theTargetMesh ? & targetMeshEditor : theCopy ? this : 0;
6616 SMESH_MeshEditor::ElemFeatures elemType;
6618 // map old node to new one
6619 TNodeNodeMap nodeMap;
6621 // elements sharing moved nodes; those of them which have all
6622 // nodes mirrored but are not in theElems are to be reversed
6623 TIDSortedElemSet inverseElemSet;
6625 // source elements for each generated one
6626 SMESH_SequenceOfElemPtr srcElems, srcNodes;
6628 // issue 021015: EDF 1578 SMESH: Free nodes are removed when translating a mesh
6629 TIDSortedElemSet orphanNode;
6631 if ( theElems.empty() ) // transform the whole mesh
6634 SMDS_ElemIteratorPtr eIt = aMesh->elementsIterator();
6635 while ( eIt->more() ) theElems.insert( eIt->next() );
6637 SMDS_NodeIteratorPtr nIt = aMesh->nodesIterator();
6638 while ( nIt->more() )
6640 const SMDS_MeshNode* node = nIt->next();
6641 if ( node->NbInverseElements() == 0)
6642 orphanNode.insert( node );
6646 // loop on elements to transform nodes : first orphan nodes then elems
6647 TIDSortedElemSet::iterator itElem;
6648 TIDSortedElemSet *elements[] = { &orphanNode, &theElems };
6649 for (int i=0; i<2; i++)
6650 for ( itElem = elements[i]->begin(); itElem != elements[i]->end(); itElem++ )
6652 const SMDS_MeshElement* elem = *itElem;
6656 // loop on elem nodes
6658 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6659 while ( itN->more() )
6661 const SMDS_MeshNode* node = cast2Node( itN->next() );
6662 // check if a node has been already transformed
6663 pair<TNodeNodeMap::iterator,bool> n2n_isnew =
6664 nodeMap.insert( make_pair ( node, node ));
6665 if ( !n2n_isnew.second )
6668 node->GetXYZ( coord );
6669 theTrsf.Transforms( coord[0], coord[1], coord[2] );
6670 if ( theTargetMesh ) {
6671 const SMDS_MeshNode * newNode = aTgtMesh->AddNode( coord[0], coord[1], coord[2] );
6672 n2n_isnew.first->second = newNode;
6673 myLastCreatedNodes.Append(newNode);
6674 srcNodes.Append( node );
6676 else if ( theCopy ) {
6677 const SMDS_MeshNode * newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
6678 n2n_isnew.first->second = newNode;
6679 myLastCreatedNodes.Append(newNode);
6680 srcNodes.Append( node );
6683 aMesh->MoveNode( node, coord[0], coord[1], coord[2] );
6684 // node position on shape becomes invalid
6685 const_cast< SMDS_MeshNode* > ( node )->SetPosition
6686 ( SMDS_SpacePosition::originSpacePosition() );
6689 // keep inverse elements
6690 if ( !theCopy && !theTargetMesh && needReverse ) {
6691 SMDS_ElemIteratorPtr invElemIt = node->GetInverseElementIterator();
6692 while ( invElemIt->more() ) {
6693 const SMDS_MeshElement* iel = invElemIt->next();
6694 inverseElemSet.insert( iel );
6698 } // loop on elems in { &orphanNode, &theElems };
6700 // either create new elements or reverse mirrored ones
6701 if ( !theCopy && !needReverse && !theTargetMesh )
6704 theElems.insert( inverseElemSet.begin(),inverseElemSet.end() );
6706 // Replicate or reverse elements
6708 std::vector<int> iForw;
6709 vector<const SMDS_MeshNode*> nodes;
6710 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
6712 const SMDS_MeshElement* elem = *itElem;
6713 if ( !elem ) continue;
6715 SMDSAbs_GeometryType geomType = elem->GetGeomType();
6716 int nbNodes = elem->NbNodes();
6717 if ( geomType == SMDSGeom_NONE ) continue; // node
6719 nodes.resize( nbNodes );
6721 if ( geomType == SMDSGeom_POLYHEDRA ) // ------------------ polyhedral volume
6723 const SMDS_VtkVolume* aPolyedre = dynamic_cast<const SMDS_VtkVolume*>( elem );
6727 bool allTransformed = true;
6728 int nbFaces = aPolyedre->NbFaces();
6729 for (int iface = 1; iface <= nbFaces && allTransformed; iface++)
6731 int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
6732 for (int inode = 1; inode <= nbFaceNodes && allTransformed; inode++)
6734 const SMDS_MeshNode* node = aPolyedre->GetFaceNode(iface, inode);
6735 TNodeNodeMap::iterator nodeMapIt = nodeMap.find(node);
6736 if ( nodeMapIt == nodeMap.end() )
6737 allTransformed = false; // not all nodes transformed
6739 nodes.push_back((*nodeMapIt).second);
6741 if ( needReverse && allTransformed )
6742 std::reverse( nodes.end() - nbFaceNodes, nodes.end() );
6744 if ( !allTransformed )
6745 continue; // not all nodes transformed
6747 else // ----------------------- the rest element types
6749 while ( iForw.size() < nbNodes ) iForw.push_back( iForw.size() );
6750 const vector<int>& iRev = SMDS_MeshCell::reverseSmdsOrder( elem->GetEntityType(), nbNodes );
6751 const vector<int>& i = needReverse ? iRev : iForw;
6753 // find transformed nodes
6755 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6756 while ( itN->more() ) {
6757 const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( itN->next() );
6758 TNodeNodeMap::iterator nodeMapIt = nodeMap.find( node );
6759 if ( nodeMapIt == nodeMap.end() )
6760 break; // not all nodes transformed
6761 nodes[ i [ iNode++ ]] = (*nodeMapIt).second;
6763 if ( iNode != nbNodes )
6764 continue; // not all nodes transformed
6768 // copy in this or a new mesh
6769 if ( editor->AddElement( nodes, elemType.Init( elem, /*basicOnly=*/false )))
6770 srcElems.Append( elem );
6773 // reverse element as it was reversed by transformation
6775 aMesh->ChangeElementNodes( elem, &nodes[0], nbNodes );
6778 } // loop on elements
6780 if ( editor && editor != this )
6781 myLastCreatedElems = editor->myLastCreatedElems;
6783 PGroupIDs newGroupIDs;
6785 if ( ( theMakeGroups && theCopy ) ||
6786 ( theMakeGroups && theTargetMesh ) )
6787 newGroupIDs = generateGroups( srcNodes, srcElems, groupPostfix, theTargetMesh, false );
6792 //=======================================================================
6794 * \brief Create groups of elements made during transformation
6795 * \param nodeGens - nodes making corresponding myLastCreatedNodes
6796 * \param elemGens - elements making corresponding myLastCreatedElems
6797 * \param postfix - to append to names of new groups
6798 * \param targetMesh - mesh to create groups in
6799 * \param topPresent - is there "top" elements that are created by sweeping
6801 //=======================================================================
6803 SMESH_MeshEditor::PGroupIDs
6804 SMESH_MeshEditor::generateGroups(const SMESH_SequenceOfElemPtr& nodeGens,
6805 const SMESH_SequenceOfElemPtr& elemGens,
6806 const std::string& postfix,
6807 SMESH_Mesh* targetMesh,
6808 const bool topPresent)
6810 PGroupIDs newGroupIDs( new list<int> );
6811 SMESH_Mesh* mesh = targetMesh ? targetMesh : GetMesh();
6813 // Sort existing groups by types and collect their names
6815 // containers to store an old group and generated new ones;
6816 // 1st new group is for result elems of different type than a source one;
6817 // 2nd new group is for same type result elems ("top" group at extrusion)
6819 using boost::make_tuple;
6820 typedef tuple< SMESHDS_GroupBase*, SMESHDS_Group*, SMESHDS_Group* > TOldNewGroup;
6821 vector< list< TOldNewGroup > > groupsByType( SMDSAbs_NbElementTypes );
6822 vector< TOldNewGroup* > orderedOldNewGroups; // in order of old groups
6824 set< string > groupNames;
6826 SMESH_Mesh::GroupIteratorPtr groupIt = GetMesh()->GetGroups();
6827 if ( !groupIt->more() ) return newGroupIDs;
6829 int newGroupID = mesh->GetGroupIds().back()+1;
6830 while ( groupIt->more() )
6832 SMESH_Group * group = groupIt->next();
6833 if ( !group ) continue;
6834 SMESHDS_GroupBase* groupDS = group->GetGroupDS();
6835 if ( !groupDS || groupDS->IsEmpty() ) continue;
6836 groupNames.insert ( group->GetName() );
6837 groupDS->SetStoreName( group->GetName() );
6838 const SMDSAbs_ElementType type = groupDS->GetType();
6839 SMESHDS_Group* newGroup = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
6840 SMESHDS_Group* newTopGroup = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
6841 groupsByType[ type ].push_back( make_tuple( groupDS, newGroup, newTopGroup ));
6842 orderedOldNewGroups.push_back( & groupsByType[ type ].back() );
6845 // Loop on nodes and elements to add them in new groups
6847 vector< const SMDS_MeshElement* > resultElems;
6848 for ( int isNodes = 0; isNodes < 2; ++isNodes )
6850 const SMESH_SequenceOfElemPtr& gens = isNodes ? nodeGens : elemGens;
6851 const SMESH_SequenceOfElemPtr& elems = isNodes ? myLastCreatedNodes : myLastCreatedElems;
6852 if ( gens.Length() != elems.Length() )
6853 throw SALOME_Exception("SMESH_MeshEditor::generateGroups(): invalid args");
6855 // loop on created elements
6856 for (int iElem = 1; iElem <= elems.Length(); ++iElem )
6858 const SMDS_MeshElement* sourceElem = gens( iElem );
6859 if ( !sourceElem ) {
6860 MESSAGE("generateGroups(): NULL source element");
6863 list< TOldNewGroup > & groupsOldNew = groupsByType[ sourceElem->GetType() ];
6864 if ( groupsOldNew.empty() ) { // no groups of this type at all
6865 while ( iElem < gens.Length() && gens( iElem+1 ) == sourceElem )
6866 ++iElem; // skip all elements made by sourceElem
6869 // collect all elements made by the iElem-th sourceElem
6870 resultElems.clear();
6871 if ( const SMDS_MeshElement* resElem = elems( iElem ))
6872 if ( resElem != sourceElem )
6873 resultElems.push_back( resElem );
6874 while ( iElem < gens.Length() && gens( iElem+1 ) == sourceElem )
6875 if ( const SMDS_MeshElement* resElem = elems( ++iElem ))
6876 if ( resElem != sourceElem )
6877 resultElems.push_back( resElem );
6879 const SMDS_MeshElement* topElem = 0;
6880 if ( isNodes ) // there must be a top element
6882 topElem = resultElems.back();
6883 resultElems.pop_back();
6887 vector< const SMDS_MeshElement* >::reverse_iterator resElemIt = resultElems.rbegin();
6888 for ( ; resElemIt != resultElems.rend() ; ++resElemIt )
6889 if ( (*resElemIt)->GetType() == sourceElem->GetType() )
6891 topElem = *resElemIt;
6892 *resElemIt = 0; // erase *resElemIt
6896 // add resultElems to groups originted from ones the sourceElem belongs to
6897 list< TOldNewGroup >::iterator gOldNew, gLast = groupsOldNew.end();
6898 for ( gOldNew = groupsOldNew.begin(); gOldNew != gLast; ++gOldNew )
6900 SMESHDS_GroupBase* oldGroup = gOldNew->get<0>();
6901 if ( oldGroup->Contains( sourceElem )) // sourceElem is in oldGroup
6903 // fill in a new group
6904 SMDS_MeshGroup & newGroup = gOldNew->get<1>()->SMDSGroup();
6905 vector< const SMDS_MeshElement* >::iterator resLast = resultElems.end(), resElemIt;
6906 for ( resElemIt = resultElems.begin(); resElemIt != resLast; ++resElemIt )
6908 newGroup.Add( *resElemIt );
6910 // fill a "top" group
6913 SMDS_MeshGroup & newTopGroup = gOldNew->get<2>()->SMDSGroup();
6914 newTopGroup.Add( topElem );
6918 } // loop on created elements
6919 }// loop on nodes and elements
6921 // Create new SMESH_Groups from SMESHDS_Groups and remove empty SMESHDS_Groups
6923 list<int> topGrouIds;
6924 for ( size_t i = 0; i < orderedOldNewGroups.size(); ++i )
6926 SMESHDS_GroupBase* oldGroupDS = orderedOldNewGroups[i]->get<0>();
6927 SMESHDS_Group* newGroups[2] = { orderedOldNewGroups[i]->get<1>(),
6928 orderedOldNewGroups[i]->get<2>() };
6929 for ( int is2nd = 0; is2nd < 2; ++is2nd )
6931 SMESHDS_Group* newGroupDS = newGroups[ is2nd ];
6932 if ( newGroupDS->IsEmpty() )
6934 mesh->GetMeshDS()->RemoveGroup( newGroupDS );
6939 newGroupDS->SetType( newGroupDS->GetElements()->next()->GetType() );
6942 const bool isTop = ( topPresent &&
6943 newGroupDS->GetType() == oldGroupDS->GetType() &&
6946 string name = oldGroupDS->GetStoreName();
6947 { // remove trailing whitespaces (issue 22599)
6948 size_t size = name.size();
6949 while ( size > 1 && isspace( name[ size-1 ]))
6951 if ( size != name.size() )
6953 name.resize( size );
6954 oldGroupDS->SetStoreName( name.c_str() );
6957 if ( !targetMesh ) {
6958 string suffix = ( isTop ? "top": postfix.c_str() );
6962 while ( !groupNames.insert( name ).second ) // name exists
6963 name = SMESH_Comment( oldGroupDS->GetStoreName() ) << "_" << suffix << "_" << nb++;
6968 newGroupDS->SetStoreName( name.c_str() );
6970 // make a SMESH_Groups
6971 mesh->AddGroup( newGroupDS );
6973 topGrouIds.push_back( newGroupDS->GetID() );
6975 newGroupIDs->push_back( newGroupDS->GetID() );
6979 newGroupIDs->splice( newGroupIDs->end(), topGrouIds );
6984 //================================================================================
6986 * \brief Return list of group of nodes close to each other within theTolerance
6987 * Search among theNodes or in the whole mesh if theNodes is empty using
6988 * an Octree algorithm
6990 //================================================================================
6992 void SMESH_MeshEditor::FindCoincidentNodes (TIDSortedNodeSet & theNodes,
6993 const double theTolerance,
6994 TListOfListOfNodes & theGroupsOfNodes)
6996 myLastCreatedElems.Clear();
6997 myLastCreatedNodes.Clear();
6999 if ( theNodes.empty() )
7000 { // get all nodes in the mesh
7001 SMDS_NodeIteratorPtr nIt = GetMeshDS()->nodesIterator(/*idInceasingOrder=*/true);
7002 while ( nIt->more() )
7003 theNodes.insert( theNodes.end(),nIt->next());
7006 SMESH_OctreeNode::FindCoincidentNodes ( theNodes, &theGroupsOfNodes, theTolerance);
7009 //=======================================================================
7010 //function : SimplifyFace
7011 //purpose : split a chain of nodes into several closed chains
7012 //=======================================================================
7014 int SMESH_MeshEditor::SimplifyFace (const vector<const SMDS_MeshNode *>& faceNodes,
7015 vector<const SMDS_MeshNode *>& poly_nodes,
7016 vector<int>& quantities) const
7018 int nbNodes = faceNodes.size();
7023 set<const SMDS_MeshNode*> nodeSet;
7025 // get simple seq of nodes
7026 //const SMDS_MeshNode* simpleNodes[ nbNodes ];
7027 vector<const SMDS_MeshNode*> simpleNodes( nbNodes );
7028 int iSimple = 0, nbUnique = 0;
7030 simpleNodes[iSimple++] = faceNodes[0];
7032 for (int iCur = 1; iCur < nbNodes; iCur++) {
7033 if (faceNodes[iCur] != simpleNodes[iSimple - 1]) {
7034 simpleNodes[iSimple++] = faceNodes[iCur];
7035 if (nodeSet.insert( faceNodes[iCur] ).second)
7039 int nbSimple = iSimple;
7040 if (simpleNodes[nbSimple - 1] == simpleNodes[0]) {
7050 bool foundLoop = (nbSimple > nbUnique);
7053 set<const SMDS_MeshNode*> loopSet;
7054 for (iSimple = 0; iSimple < nbSimple && !foundLoop; iSimple++) {
7055 const SMDS_MeshNode* n = simpleNodes[iSimple];
7056 if (!loopSet.insert( n ).second) {
7060 int iC = 0, curLast = iSimple;
7061 for (; iC < curLast; iC++) {
7062 if (simpleNodes[iC] == n) break;
7064 int loopLen = curLast - iC;
7066 // create sub-element
7068 quantities.push_back(loopLen);
7069 for (; iC < curLast; iC++) {
7070 poly_nodes.push_back(simpleNodes[iC]);
7073 // shift the rest nodes (place from the first loop position)
7074 for (iC = curLast + 1; iC < nbSimple; iC++) {
7075 simpleNodes[iC - loopLen] = simpleNodes[iC];
7077 nbSimple -= loopLen;
7080 } // for (iSimple = 0; iSimple < nbSimple; iSimple++)
7081 } // while (foundLoop)
7085 quantities.push_back(iSimple);
7086 for (int i = 0; i < iSimple; i++)
7087 poly_nodes.push_back(simpleNodes[i]);
7093 //=======================================================================
7094 //function : MergeNodes
7095 //purpose : In each group, the cdr of nodes are substituted by the first one
7097 //=======================================================================
7099 void SMESH_MeshEditor::MergeNodes (TListOfListOfNodes & theGroupsOfNodes)
7101 MESSAGE("MergeNodes");
7102 myLastCreatedElems.Clear();
7103 myLastCreatedNodes.Clear();
7105 SMESHDS_Mesh* aMesh = GetMeshDS();
7107 TNodeNodeMap nodeNodeMap; // node to replace - new node
7108 set<const SMDS_MeshElement*> elems; // all elements with changed nodes
7109 list< int > rmElemIds, rmNodeIds;
7111 // Fill nodeNodeMap and elems
7113 TListOfListOfNodes::iterator grIt = theGroupsOfNodes.begin();
7114 for ( ; grIt != theGroupsOfNodes.end(); grIt++ ) {
7115 list<const SMDS_MeshNode*>& nodes = *grIt;
7116 list<const SMDS_MeshNode*>::iterator nIt = nodes.begin();
7117 const SMDS_MeshNode* nToKeep = *nIt;
7118 //MESSAGE("node to keep " << nToKeep->GetID());
7119 for ( ++nIt; nIt != nodes.end(); nIt++ ) {
7120 const SMDS_MeshNode* nToRemove = *nIt;
7121 nodeNodeMap.insert( TNodeNodeMap::value_type( nToRemove, nToKeep ));
7122 if ( nToRemove != nToKeep ) {
7123 //MESSAGE(" node to remove " << nToRemove->GetID());
7124 rmNodeIds.push_back( nToRemove->GetID() );
7125 AddToSameGroups( nToKeep, nToRemove, aMesh );
7126 // set _alwaysComputed to a sub-mesh of VERTEX to enable mesh computing
7127 // after MergeNodes() w/o creating node in place of merged ones.
7128 const SMDS_PositionPtr& pos = nToRemove->GetPosition();
7129 if ( pos && pos->GetTypeOfPosition() == SMDS_TOP_VERTEX )
7130 if ( SMESH_subMesh* sm = myMesh->GetSubMeshContaining( nToRemove->getshapeId() ))
7131 sm->SetIsAlwaysComputed( true );
7134 SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
7135 while ( invElemIt->more() ) {
7136 const SMDS_MeshElement* elem = invElemIt->next();
7141 // Change element nodes or remove an element
7143 set<const SMDS_MeshNode*> nodeSet;
7144 vector< const SMDS_MeshNode*> curNodes, uniqueNodes;
7146 ElemFeatures elemType;
7148 set<const SMDS_MeshElement*>::iterator eIt = elems.begin();
7149 for ( ; eIt != elems.end(); eIt++ )
7151 const SMDS_MeshElement* elem = *eIt;
7152 int nbNodes = elem->NbNodes();
7153 int aShapeId = FindShape( elem );
7156 curNodes.resize( nbNodes );
7157 uniqueNodes.resize( nbNodes );
7158 iRepl.resize( nbNodes );
7159 int iUnique = 0, iCur = 0, nbRepl = 0;
7161 // get new seq of nodes
7162 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
7163 while ( itN->more() )
7165 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( itN->next() );
7167 TNodeNodeMap::iterator nnIt = nodeNodeMap.find( n );
7168 if ( nnIt != nodeNodeMap.end() ) { // n sticks
7170 { ////////// BUG 0020185: begin
7171 bool stopRecur = false;
7172 set<const SMDS_MeshNode*> nodesRecur;
7173 nodesRecur.insert(n);
7174 while (!stopRecur) {
7175 TNodeNodeMap::iterator nnIt_i = nodeNodeMap.find( n );
7176 if ( nnIt_i != nodeNodeMap.end() ) { // n sticks
7177 n = (*nnIt_i).second;
7178 if (!nodesRecur.insert(n).second) {
7179 // error: recursive dependancy
7186 } ////////// BUG 0020185: end
7188 curNodes[ iCur ] = n;
7189 bool isUnique = nodeSet.insert( n ).second;
7191 uniqueNodes[ iUnique++ ] = n;
7193 iRepl[ nbRepl++ ] = iCur;
7197 // Analyse element topology after replacement
7200 int nbUniqueNodes = nodeSet.size();
7201 if ( nbNodes != nbUniqueNodes ) // some nodes stick
7203 if (elem->IsPoly()) // Polygons and Polyhedral volumes
7205 if (elem->GetType() == SMDSAbs_Face) // Polygon
7207 elemType.Init( elem );
7208 const bool isQuad = elemType.myIsQuad;
7210 SMDS_MeshCell::applyInterlace // interlace medium and corner nodes
7211 ( SMDS_MeshCell::interlacedSmdsOrder( SMDSEntity_Quad_Polygon, nbNodes ), curNodes );
7213 // a polygon can divide into several elements
7214 vector<const SMDS_MeshNode *> polygons_nodes;
7215 vector<int> quantities;
7216 int nbNew = SimplifyFace( curNodes, polygons_nodes, quantities );
7219 vector<const SMDS_MeshNode *> face_nodes;
7221 for (int iface = 0; iface < nbNew; iface++)
7223 int nbNewNodes = quantities[iface];
7224 face_nodes.assign( polygons_nodes.begin() + inode,
7225 polygons_nodes.begin() + inode + nbNewNodes );
7226 inode += nbNewNodes;
7227 if ( isQuad ) // check if a result elem is a valid quadratic polygon
7229 bool isValid = ( nbNewNodes % 2 == 0 );
7230 for ( int i = 0; i < nbNewNodes && isValid; ++i )
7231 isValid = ( elem->IsMediumNode( face_nodes[i]) == bool( i % 2 ));
7232 elemType.SetQuad( isValid );
7233 if ( isValid ) // put medium nodes after corners
7234 SMDS_MeshCell::applyInterlaceRev
7235 ( SMDS_MeshCell::interlacedSmdsOrder( SMDSEntity_Quad_Polygon,
7236 nbNewNodes ), face_nodes );
7238 SMDS_MeshElement* newElem = AddElement( face_nodes, elemType );
7240 aMesh->SetMeshElementOnShape(newElem, aShapeId);
7243 rmElemIds.push_back(elem->GetID());
7247 else if (elem->GetType() == SMDSAbs_Volume) // Polyhedral volume
7249 if (nbUniqueNodes < 4) {
7250 rmElemIds.push_back(elem->GetID());
7253 // each face has to be analyzed in order to check volume validity
7254 const SMDS_VtkVolume* aPolyedre =
7255 dynamic_cast<const SMDS_VtkVolume*>( elem );
7257 int nbFaces = aPolyedre->NbFaces();
7259 vector<const SMDS_MeshNode *> poly_nodes;
7260 vector<int> quantities;
7262 for (int iface = 1; iface <= nbFaces; iface++) {
7263 int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
7264 vector<const SMDS_MeshNode *> faceNodes (nbFaceNodes);
7266 for (int inode = 1; inode <= nbFaceNodes; inode++) {
7267 const SMDS_MeshNode * faceNode = aPolyedre->GetFaceNode(iface, inode);
7268 TNodeNodeMap::iterator nnIt = nodeNodeMap.find(faceNode);
7269 if (nnIt != nodeNodeMap.end()) { // faceNode sticks
7270 faceNode = (*nnIt).second;
7272 faceNodes[inode - 1] = faceNode;
7275 SimplifyFace(faceNodes, poly_nodes, quantities);
7278 if (quantities.size() > 3) {
7279 // to be done: remove coincident faces
7282 if (quantities.size() > 3)
7284 //aMesh->ChangePolyhedronNodes(elem, poly_nodes, quantities);
7285 const SMDS_MeshElement* newElem =
7286 aMesh->AddPolyhedralVolume(poly_nodes, quantities);
7287 myLastCreatedElems.Append(newElem);
7288 if ( aShapeId && newElem )
7289 aMesh->SetMeshElementOnShape( newElem, aShapeId );
7290 rmElemIds.push_back(elem->GetID());
7294 rmElemIds.push_back(elem->GetID());
7305 // TODO not all the possible cases are solved. Find something more generic?
7306 switch ( nbNodes ) {
7307 case 2: ///////////////////////////////////// EDGE
7308 isOk = false; break;
7309 case 3: ///////////////////////////////////// TRIANGLE
7310 isOk = false; break;
7312 if ( elem->GetType() == SMDSAbs_Volume ) // TETRAHEDRON
7314 else { //////////////////////////////////// QUADRANGLE
7315 if ( nbUniqueNodes < 3 )
7317 else if ( nbRepl == 2 && iRepl[ 1 ] - iRepl[ 0 ] == 2 )
7318 isOk = false; // opposite nodes stick
7319 //MESSAGE("isOk " << isOk);
7322 case 6: ///////////////////////////////////// PENTAHEDRON
7323 if ( nbUniqueNodes == 4 ) {
7324 // ---------------------------------> tetrahedron
7326 iRepl[ 0 ] > 2 && iRepl[ 1 ] > 2 && iRepl[ 2 ] > 2 ) {
7327 // all top nodes stick: reverse a bottom
7328 uniqueNodes[ 0 ] = curNodes [ 1 ];
7329 uniqueNodes[ 1 ] = curNodes [ 0 ];
7331 else if (nbRepl == 3 &&
7332 iRepl[ 0 ] < 3 && iRepl[ 1 ] < 3 && iRepl[ 2 ] < 3 ) {
7333 // all bottom nodes stick: set a top before
7334 uniqueNodes[ 3 ] = uniqueNodes [ 0 ];
7335 uniqueNodes[ 0 ] = curNodes [ 3 ];
7336 uniqueNodes[ 1 ] = curNodes [ 4 ];
7337 uniqueNodes[ 2 ] = curNodes [ 5 ];
7339 else if (nbRepl == 4 &&
7340 iRepl[ 2 ] - iRepl [ 0 ] == 3 && iRepl[ 3 ] - iRepl [ 1 ] == 3 ) {
7341 // a lateral face turns into a line: reverse a bottom
7342 uniqueNodes[ 0 ] = curNodes [ 1 ];
7343 uniqueNodes[ 1 ] = curNodes [ 0 ];
7348 else if ( nbUniqueNodes == 5 ) {
7349 // PENTAHEDRON --------------------> 2 tetrahedrons
7350 if ( nbRepl == 2 && iRepl[ 1 ] - iRepl [ 0 ] == 3 ) {
7351 // a bottom node sticks with a linked top one
7353 SMDS_MeshElement* newElem =
7354 aMesh->AddVolume(curNodes[ 3 ],
7357 curNodes[ iRepl[ 0 ] == 2 ? 1 : 2 ]);
7358 myLastCreatedElems.Append(newElem);
7360 aMesh->SetMeshElementOnShape( newElem, aShapeId );
7361 // 2. : reverse a bottom
7362 uniqueNodes[ 0 ] = curNodes [ 1 ];
7363 uniqueNodes[ 1 ] = curNodes [ 0 ];
7373 if(elem->IsQuadratic()) { // Quadratic quadrangle
7385 MESSAGE("nbRepl=2: " << iRepl[0] << " " << iRepl[1]);
7388 MESSAGE("nbRepl=3: " << iRepl[0] << " " << iRepl[1] << " " << iRepl[2]);
7390 if( iRepl[0]==0 && iRepl[1]==1 && iRepl[2]==4 ) {
7391 uniqueNodes[0] = curNodes[0];
7392 uniqueNodes[1] = curNodes[2];
7393 uniqueNodes[2] = curNodes[3];
7394 uniqueNodes[3] = curNodes[5];
7395 uniqueNodes[4] = curNodes[6];
7396 uniqueNodes[5] = curNodes[7];
7399 if( iRepl[0]==0 && iRepl[1]==3 && iRepl[2]==7 ) {
7400 uniqueNodes[0] = curNodes[0];
7401 uniqueNodes[1] = curNodes[1];
7402 uniqueNodes[2] = curNodes[2];
7403 uniqueNodes[3] = curNodes[4];
7404 uniqueNodes[4] = curNodes[5];
7405 uniqueNodes[5] = curNodes[6];
7408 if( iRepl[0]==0 && iRepl[1]==4 && iRepl[2]==7 ) {
7409 uniqueNodes[0] = curNodes[1];
7410 uniqueNodes[1] = curNodes[2];
7411 uniqueNodes[2] = curNodes[3];
7412 uniqueNodes[3] = curNodes[5];
7413 uniqueNodes[4] = curNodes[6];
7414 uniqueNodes[5] = curNodes[0];
7417 if( iRepl[0]==1 && iRepl[1]==2 && iRepl[2]==5 ) {
7418 uniqueNodes[0] = curNodes[0];
7419 uniqueNodes[1] = curNodes[1];
7420 uniqueNodes[2] = curNodes[3];
7421 uniqueNodes[3] = curNodes[4];
7422 uniqueNodes[4] = curNodes[6];
7423 uniqueNodes[5] = curNodes[7];
7426 if( iRepl[0]==1 && iRepl[1]==4 && iRepl[2]==5 ) {
7427 uniqueNodes[0] = curNodes[0];
7428 uniqueNodes[1] = curNodes[2];
7429 uniqueNodes[2] = curNodes[3];
7430 uniqueNodes[3] = curNodes[1];
7431 uniqueNodes[4] = curNodes[6];
7432 uniqueNodes[5] = curNodes[7];
7435 if( iRepl[0]==2 && iRepl[1]==3 && iRepl[2]==6 ) {
7436 uniqueNodes[0] = curNodes[0];
7437 uniqueNodes[1] = curNodes[1];
7438 uniqueNodes[2] = curNodes[2];
7439 uniqueNodes[3] = curNodes[4];
7440 uniqueNodes[4] = curNodes[5];
7441 uniqueNodes[5] = curNodes[7];
7444 if( iRepl[0]==2 && iRepl[1]==5 && iRepl[2]==6 ) {
7445 uniqueNodes[0] = curNodes[0];
7446 uniqueNodes[1] = curNodes[1];
7447 uniqueNodes[2] = curNodes[3];
7448 uniqueNodes[3] = curNodes[4];
7449 uniqueNodes[4] = curNodes[2];
7450 uniqueNodes[5] = curNodes[7];
7453 if( iRepl[0]==3 && iRepl[1]==6 && iRepl[2]==7 ) {
7454 uniqueNodes[0] = curNodes[0];
7455 uniqueNodes[1] = curNodes[1];
7456 uniqueNodes[2] = curNodes[2];
7457 uniqueNodes[3] = curNodes[4];
7458 uniqueNodes[4] = curNodes[5];
7459 uniqueNodes[5] = curNodes[3];
7464 MESSAGE("nbRepl=4: " << iRepl[0] << " " << iRepl[1] << " " << iRepl[2] << " " << iRepl[3]);
7467 MESSAGE("nbRepl=5: " << iRepl[0] << " " << iRepl[1] << " " << iRepl[2] << " " << iRepl[3] << " " << iRepl[4]);
7471 //////////////////////////////////// HEXAHEDRON
7473 SMDS_VolumeTool hexa (elem);
7474 hexa.SetExternalNormal();
7475 if ( nbUniqueNodes == 4 && nbRepl == 4 ) {
7476 //////////////////////// HEX ---> 1 tetrahedron
7477 for ( int iFace = 0; iFace < 6; iFace++ ) {
7478 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7479 if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7480 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7481 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7482 // one face turns into a point ...
7483 int iOppFace = hexa.GetOppFaceIndex( iFace );
7484 ind = hexa.GetFaceNodesIndices( iOppFace );
7486 for ( iCur = 0; iCur < 4 && nbStick < 2; iCur++ ) {
7487 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7490 if ( nbStick == 1 ) {
7491 // ... and the opposite one - into a triangle.
7493 ind = hexa.GetFaceNodesIndices( iFace );
7494 uniqueNodes[ 3 ] = curNodes[ind[ 0 ]];
7501 else if ( nbUniqueNodes == 6 && nbRepl == 2 ) {
7502 //////////////////////// HEX ---> 1 prism
7503 int nbTria = 0, iTria[3];
7504 const int *ind; // indices of face nodes
7505 // look for triangular faces
7506 for ( int iFace = 0; iFace < 6 && nbTria < 3; iFace++ ) {
7507 ind = hexa.GetFaceNodesIndices( iFace );
7508 TIDSortedNodeSet faceNodes;
7509 for ( iCur = 0; iCur < 4; iCur++ )
7510 faceNodes.insert( curNodes[ind[iCur]] );
7511 if ( faceNodes.size() == 3 )
7512 iTria[ nbTria++ ] = iFace;
7514 // check if triangles are opposite
7515 if ( nbTria == 2 && iTria[0] == hexa.GetOppFaceIndex( iTria[1] ))
7518 // set nodes of the bottom triangle
7519 ind = hexa.GetFaceNodesIndices( iTria[ 0 ]);
7521 for ( iCur = 0; iCur < 4; iCur++ )
7522 if ( ind[iCur] != iRepl[0] && ind[iCur] != iRepl[1])
7523 indB.push_back( ind[iCur] );
7524 if ( !hexa.IsForward() )
7525 std::swap( indB[0], indB[2] );
7526 for ( iCur = 0; iCur < 3; iCur++ )
7527 uniqueNodes[ iCur ] = curNodes[indB[iCur]];
7528 // set nodes of the top triangle
7529 const int *indT = hexa.GetFaceNodesIndices( iTria[ 1 ]);
7530 for ( iCur = 0; iCur < 3; ++iCur )
7531 for ( int j = 0; j < 4; ++j )
7532 if ( hexa.IsLinked( indB[ iCur ], indT[ j ] ))
7534 uniqueNodes[ iCur + 3 ] = curNodes[ indT[ j ]];
7540 else if (nbUniqueNodes == 5 && nbRepl == 4 ) {
7541 //////////////////// HEXAHEDRON ---> 2 tetrahedrons
7542 for ( int iFace = 0; iFace < 6; iFace++ ) {
7543 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7544 if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7545 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7546 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7547 // one face turns into a point ...
7548 int iOppFace = hexa.GetOppFaceIndex( iFace );
7549 ind = hexa.GetFaceNodesIndices( iOppFace );
7551 iUnique = 2; // reverse a tetrahedron 1 bottom
7552 for ( iCur = 0; iCur < 4 && nbStick == 0; iCur++ ) {
7553 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7555 else if ( iUnique >= 0 )
7556 uniqueNodes[ iUnique-- ] = curNodes[ind[ iCur ]];
7558 if ( nbStick == 0 ) {
7559 // ... and the opposite one is a quadrangle
7561 const int* indTop = hexa.GetFaceNodesIndices( iFace );
7562 uniqueNodes[ 3 ] = curNodes[indTop[ 0 ]];
7565 SMDS_MeshElement* newElem =
7566 aMesh->AddVolume(curNodes[ind[ 0 ]],
7569 curNodes[indTop[ 0 ]]);
7570 myLastCreatedElems.Append(newElem);
7572 aMesh->SetMeshElementOnShape( newElem, aShapeId );
7579 else if ( nbUniqueNodes == 6 && nbRepl == 4 ) {
7580 ////////////////// HEXAHEDRON ---> 2 tetrahedrons or 1 prism
7581 // find indices of quad and tri faces
7582 int iQuadFace[ 6 ], iTriFace[ 6 ], nbQuad = 0, nbTri = 0, iFace;
7583 for ( iFace = 0; iFace < 6; iFace++ ) {
7584 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7586 for ( iCur = 0; iCur < 4; iCur++ )
7587 nodeSet.insert( curNodes[ind[ iCur ]] );
7588 nbUniqueNodes = nodeSet.size();
7589 if ( nbUniqueNodes == 3 )
7590 iTriFace[ nbTri++ ] = iFace;
7591 else if ( nbUniqueNodes == 4 )
7592 iQuadFace[ nbQuad++ ] = iFace;
7594 if (nbQuad == 2 && nbTri == 4 &&
7595 hexa.GetOppFaceIndex( iQuadFace[ 0 ] ) == iQuadFace[ 1 ]) {
7596 // 2 opposite quadrangles stuck with a diagonal;
7597 // sample groups of merged indices: (0-4)(2-6)
7598 // --------------------------------------------> 2 tetrahedrons
7599 const int *ind1 = hexa.GetFaceNodesIndices( iQuadFace[ 0 ]); // indices of quad1 nodes
7600 const int *ind2 = hexa.GetFaceNodesIndices( iQuadFace[ 1 ]);
7601 int i0, i1d, i2, i3d, i0t, i2t; // d-daigonal, t-top
7602 if (curNodes[ind1[ 0 ]] == curNodes[ind2[ 0 ]] &&
7603 curNodes[ind1[ 2 ]] == curNodes[ind2[ 2 ]]) {
7604 // stuck with 0-2 diagonal
7612 else if (curNodes[ind1[ 1 ]] == curNodes[ind2[ 3 ]] &&
7613 curNodes[ind1[ 3 ]] == curNodes[ind2[ 1 ]]) {
7614 // stuck with 1-3 diagonal
7626 uniqueNodes[ 0 ] = curNodes [ i0 ];
7627 uniqueNodes[ 1 ] = curNodes [ i1d ];
7628 uniqueNodes[ 2 ] = curNodes [ i3d ];
7629 uniqueNodes[ 3 ] = curNodes [ i0t ];
7632 SMDS_MeshElement* newElem = aMesh->AddVolume(curNodes[ i1d ],
7636 myLastCreatedElems.Append(newElem);
7638 aMesh->SetMeshElementOnShape( newElem, aShapeId );
7641 else if (( nbTri == 2 && nbQuad == 3 ) || // merged (0-4)(1-5)
7642 ( nbTri == 4 && nbQuad == 2 )) { // merged (7-4)(1-5)
7643 // --------------------------------------------> prism
7644 // find 2 opposite triangles
7646 for ( iFace = 0; iFace + 1 < nbTri; iFace++ ) {
7647 if ( hexa.GetOppFaceIndex( iTriFace[ iFace ] ) == iTriFace[ iFace + 1 ]) {
7648 // find indices of kept and replaced nodes
7649 // and fill unique nodes of 2 opposite triangles
7650 const int *ind1 = hexa.GetFaceNodesIndices( iTriFace[ iFace ]);
7651 const int *ind2 = hexa.GetFaceNodesIndices( iTriFace[ iFace + 1 ]);
7652 const SMDS_MeshNode** hexanodes = hexa.GetNodes();
7653 // fill unique nodes
7656 for ( iCur = 0; iCur < 4 && isOk; iCur++ ) {
7657 const SMDS_MeshNode* n = curNodes[ind1[ iCur ]];
7658 const SMDS_MeshNode* nInit = hexanodes[ind1[ iCur ]];
7660 // iCur of a linked node of the opposite face (make normals co-directed):
7661 int iCurOpp = ( iCur == 1 || iCur == 3 ) ? 4 - iCur : iCur;
7662 // check that correspondent corners of triangles are linked
7663 if ( !hexa.IsLinked( ind1[ iCur ], ind2[ iCurOpp ] ))
7666 uniqueNodes[ iUnique ] = n;
7667 uniqueNodes[ iUnique + 3 ] = curNodes[ind2[ iCurOpp ]];
7676 } // if ( nbUniqueNodes == 6 && nbRepl == 4 )
7679 MESSAGE("MergeNodes() removes hexahedron "<< elem);
7686 } // switch ( nbNodes )
7688 } // if ( nbNodes != nbUniqueNodes ) // some nodes stick
7690 if ( isOk ) // the non-poly elem remains valid after sticking nodes
7692 elemType.Init( elem ).SetID( elem->GetID() );
7694 SMESHDS_SubMesh * sm = aShapeId > 0 ? aMesh->MeshElements(aShapeId) : 0;
7695 aMesh->RemoveFreeElement(elem, sm, /*fromGroups=*/false);
7697 uniqueNodes.resize(nbUniqueNodes);
7698 SMDS_MeshElement* newElem = this->AddElement( uniqueNodes, elemType );
7699 if ( sm && newElem )
7700 sm->AddElement( newElem );
7701 if ( elem != newElem )
7702 ReplaceElemInGroups( elem, newElem, aMesh );
7705 // Remove invalid regular element or invalid polygon
7706 rmElemIds.push_back( elem->GetID() );
7709 } // loop on elements
7711 // Remove bad elements, then equal nodes (order important)
7713 Remove( rmElemIds, false );
7714 Remove( rmNodeIds, true );
7720 // ========================================================
7721 // class : SortableElement
7722 // purpose : allow sorting elements basing on their nodes
7723 // ========================================================
7724 class SortableElement : public set <const SMDS_MeshElement*>
7728 SortableElement( const SMDS_MeshElement* theElem )
7731 SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator();
7732 while ( nodeIt->more() )
7733 this->insert( nodeIt->next() );
7736 const SMDS_MeshElement* Get() const
7739 void Set(const SMDS_MeshElement* e) const
7744 mutable const SMDS_MeshElement* myElem;
7747 //=======================================================================
7748 //function : FindEqualElements
7749 //purpose : Return list of group of elements built on the same nodes.
7750 // Search among theElements or in the whole mesh if theElements is empty
7751 //=======================================================================
7753 void SMESH_MeshEditor::FindEqualElements(TIDSortedElemSet & theElements,
7754 TListOfListOfElementsID & theGroupsOfElementsID)
7756 myLastCreatedElems.Clear();
7757 myLastCreatedNodes.Clear();
7759 typedef map< SortableElement, int > TMapOfNodeSet;
7760 typedef list<int> TGroupOfElems;
7762 if ( theElements.empty() )
7763 { // get all elements in the mesh
7764 SMDS_ElemIteratorPtr eIt = GetMeshDS()->elementsIterator();
7765 while ( eIt->more() )
7766 theElements.insert( theElements.end(), eIt->next());
7769 vector< TGroupOfElems > arrayOfGroups;
7770 TGroupOfElems groupOfElems;
7771 TMapOfNodeSet mapOfNodeSet;
7773 TIDSortedElemSet::iterator elemIt = theElements.begin();
7774 for ( int i = 0, j=0; elemIt != theElements.end(); ++elemIt, ++j ) {
7775 const SMDS_MeshElement* curElem = *elemIt;
7776 SortableElement SE(curElem);
7779 pair< TMapOfNodeSet::iterator, bool> pp = mapOfNodeSet.insert(make_pair(SE, i));
7780 if( !(pp.second) ) {
7781 TMapOfNodeSet::iterator& itSE = pp.first;
7782 ind = (*itSE).second;
7783 arrayOfGroups[ind].push_back(curElem->GetID());
7786 groupOfElems.clear();
7787 groupOfElems.push_back(curElem->GetID());
7788 arrayOfGroups.push_back(groupOfElems);
7793 vector< TGroupOfElems >::iterator groupIt = arrayOfGroups.begin();
7794 for ( ; groupIt != arrayOfGroups.end(); ++groupIt ) {
7795 groupOfElems = *groupIt;
7796 if ( groupOfElems.size() > 1 ) {
7797 groupOfElems.sort();
7798 theGroupsOfElementsID.push_back(groupOfElems);
7803 //=======================================================================
7804 //function : MergeElements
7805 //purpose : In each given group, substitute all elements by the first one.
7806 //=======================================================================
7808 void SMESH_MeshEditor::MergeElements(TListOfListOfElementsID & theGroupsOfElementsID)
7810 myLastCreatedElems.Clear();
7811 myLastCreatedNodes.Clear();
7813 typedef list<int> TListOfIDs;
7814 TListOfIDs rmElemIds; // IDs of elems to remove
7816 SMESHDS_Mesh* aMesh = GetMeshDS();
7818 TListOfListOfElementsID::iterator groupsIt = theGroupsOfElementsID.begin();
7819 while ( groupsIt != theGroupsOfElementsID.end() ) {
7820 TListOfIDs& aGroupOfElemID = *groupsIt;
7821 aGroupOfElemID.sort();
7822 int elemIDToKeep = aGroupOfElemID.front();
7823 const SMDS_MeshElement* elemToKeep = aMesh->FindElement(elemIDToKeep);
7824 aGroupOfElemID.pop_front();
7825 TListOfIDs::iterator idIt = aGroupOfElemID.begin();
7826 while ( idIt != aGroupOfElemID.end() ) {
7827 int elemIDToRemove = *idIt;
7828 const SMDS_MeshElement* elemToRemove = aMesh->FindElement(elemIDToRemove);
7829 // add the kept element in groups of removed one (PAL15188)
7830 AddToSameGroups( elemToKeep, elemToRemove, aMesh );
7831 rmElemIds.push_back( elemIDToRemove );
7837 Remove( rmElemIds, false );
7840 //=======================================================================
7841 //function : MergeEqualElements
7842 //purpose : Remove all but one of elements built on the same nodes.
7843 //=======================================================================
7845 void SMESH_MeshEditor::MergeEqualElements()
7847 TIDSortedElemSet aMeshElements; /* empty input ==
7848 to merge equal elements in the whole mesh */
7849 TListOfListOfElementsID aGroupsOfElementsID;
7850 FindEqualElements(aMeshElements, aGroupsOfElementsID);
7851 MergeElements(aGroupsOfElementsID);
7854 //=======================================================================
7855 //function : findAdjacentFace
7857 //=======================================================================
7859 static const SMDS_MeshElement* findAdjacentFace(const SMDS_MeshNode* n1,
7860 const SMDS_MeshNode* n2,
7861 const SMDS_MeshElement* elem)
7863 TIDSortedElemSet elemSet, avoidSet;
7865 avoidSet.insert ( elem );
7866 return SMESH_MeshAlgos::FindFaceInSet( n1, n2, elemSet, avoidSet );
7869 //=======================================================================
7870 //function : FindFreeBorder
7872 //=======================================================================
7874 #define ControlFreeBorder SMESH::Controls::FreeEdges::IsFreeEdge
7876 bool SMESH_MeshEditor::FindFreeBorder (const SMDS_MeshNode* theFirstNode,
7877 const SMDS_MeshNode* theSecondNode,
7878 const SMDS_MeshNode* theLastNode,
7879 list< const SMDS_MeshNode* > & theNodes,
7880 list< const SMDS_MeshElement* >& theFaces)
7882 if ( !theFirstNode || !theSecondNode )
7884 // find border face between theFirstNode and theSecondNode
7885 const SMDS_MeshElement* curElem = findAdjacentFace( theFirstNode, theSecondNode, 0 );
7889 theFaces.push_back( curElem );
7890 theNodes.push_back( theFirstNode );
7891 theNodes.push_back( theSecondNode );
7893 //vector<const SMDS_MeshNode*> nodes;
7894 const SMDS_MeshNode *nIgnore = theFirstNode, *nStart = theSecondNode;
7895 TIDSortedElemSet foundElems;
7896 bool needTheLast = ( theLastNode != 0 );
7898 while ( nStart != theLastNode ) {
7899 if ( nStart == theFirstNode )
7900 return !needTheLast;
7902 // find all free border faces sharing form nStart
7904 list< const SMDS_MeshElement* > curElemList;
7905 list< const SMDS_MeshNode* > nStartList;
7906 SMDS_ElemIteratorPtr invElemIt = nStart->GetInverseElementIterator(SMDSAbs_Face);
7907 while ( invElemIt->more() ) {
7908 const SMDS_MeshElement* e = invElemIt->next();
7909 if ( e == curElem || foundElems.insert( e ).second ) {
7911 int iNode = 0, nbNodes = e->NbNodes();
7912 //const SMDS_MeshNode* nodes[nbNodes+1];
7913 vector<const SMDS_MeshNode*> nodes(nbNodes+1);
7915 if(e->IsQuadratic()) {
7916 const SMDS_VtkFace* F =
7917 dynamic_cast<const SMDS_VtkFace*>(e);
7918 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
7919 // use special nodes iterator
7920 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
7921 while( anIter->more() ) {
7922 nodes[ iNode++ ] = cast2Node(anIter->next());
7926 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
7927 while ( nIt->more() )
7928 nodes[ iNode++ ] = static_cast<const SMDS_MeshNode*>( nIt->next() );
7930 nodes[ iNode ] = nodes[ 0 ];
7932 for ( iNode = 0; iNode < nbNodes; iNode++ )
7933 if (((nodes[ iNode ] == nStart && nodes[ iNode + 1] != nIgnore ) ||
7934 (nodes[ iNode + 1] == nStart && nodes[ iNode ] != nIgnore )) &&
7935 ControlFreeBorder( &nodes[ iNode ], e->GetID() ))
7937 nStartList.push_back( nodes[ iNode + ( nodes[ iNode ] == nStart ? 1 : 0 )]);
7938 curElemList.push_back( e );
7942 // analyse the found
7944 int nbNewBorders = curElemList.size();
7945 if ( nbNewBorders == 0 ) {
7946 // no free border furthermore
7947 return !needTheLast;
7949 else if ( nbNewBorders == 1 ) {
7950 // one more element found
7952 nStart = nStartList.front();
7953 curElem = curElemList.front();
7954 theFaces.push_back( curElem );
7955 theNodes.push_back( nStart );
7958 // several continuations found
7959 list< const SMDS_MeshElement* >::iterator curElemIt;
7960 list< const SMDS_MeshNode* >::iterator nStartIt;
7961 // check if one of them reached the last node
7962 if ( needTheLast ) {
7963 for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
7964 curElemIt!= curElemList.end();
7965 curElemIt++, nStartIt++ )
7966 if ( *nStartIt == theLastNode ) {
7967 theFaces.push_back( *curElemIt );
7968 theNodes.push_back( *nStartIt );
7972 // find the best free border by the continuations
7973 list<const SMDS_MeshNode*> contNodes[ 2 ], *cNL;
7974 list<const SMDS_MeshElement*> contFaces[ 2 ], *cFL;
7975 for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
7976 curElemIt!= curElemList.end();
7977 curElemIt++, nStartIt++ )
7979 cNL = & contNodes[ contNodes[0].empty() ? 0 : 1 ];
7980 cFL = & contFaces[ contFaces[0].empty() ? 0 : 1 ];
7981 // find one more free border
7982 if ( ! SMESH_MeshEditor::FindFreeBorder( nStart, *nStartIt, theLastNode, *cNL, *cFL )) {
7986 else if ( !contNodes[0].empty() && !contNodes[1].empty() ) {
7987 // choice: clear a worse one
7988 int iLongest = ( contNodes[0].size() < contNodes[1].size() ? 1 : 0 );
7989 int iWorse = ( needTheLast ? 1 - iLongest : iLongest );
7990 contNodes[ iWorse ].clear();
7991 contFaces[ iWorse ].clear();
7994 if ( contNodes[0].empty() && contNodes[1].empty() )
7997 // append the best free border
7998 cNL = & contNodes[ contNodes[0].empty() ? 1 : 0 ];
7999 cFL = & contFaces[ contFaces[0].empty() ? 1 : 0 ];
8000 theNodes.pop_back(); // remove nIgnore
8001 theNodes.pop_back(); // remove nStart
8002 theFaces.pop_back(); // remove curElem
8003 list< const SMDS_MeshNode* >::iterator nIt = cNL->begin();
8004 list< const SMDS_MeshElement* >::iterator fIt = cFL->begin();
8005 for ( ; nIt != cNL->end(); nIt++ ) theNodes.push_back( *nIt );
8006 for ( ; fIt != cFL->end(); fIt++ ) theFaces.push_back( *fIt );
8009 } // several continuations found
8010 } // while ( nStart != theLastNode )
8015 //=======================================================================
8016 //function : CheckFreeBorderNodes
8017 //purpose : Return true if the tree nodes are on a free border
8018 //=======================================================================
8020 bool SMESH_MeshEditor::CheckFreeBorderNodes(const SMDS_MeshNode* theNode1,
8021 const SMDS_MeshNode* theNode2,
8022 const SMDS_MeshNode* theNode3)
8024 list< const SMDS_MeshNode* > nodes;
8025 list< const SMDS_MeshElement* > faces;
8026 return FindFreeBorder( theNode1, theNode2, theNode3, nodes, faces);
8029 //=======================================================================
8030 //function : SewFreeBorder
8032 //=======================================================================
8034 SMESH_MeshEditor::Sew_Error
8035 SMESH_MeshEditor::SewFreeBorder (const SMDS_MeshNode* theBordFirstNode,
8036 const SMDS_MeshNode* theBordSecondNode,
8037 const SMDS_MeshNode* theBordLastNode,
8038 const SMDS_MeshNode* theSideFirstNode,
8039 const SMDS_MeshNode* theSideSecondNode,
8040 const SMDS_MeshNode* theSideThirdNode,
8041 const bool theSideIsFreeBorder,
8042 const bool toCreatePolygons,
8043 const bool toCreatePolyedrs)
8045 myLastCreatedElems.Clear();
8046 myLastCreatedNodes.Clear();
8048 MESSAGE("::SewFreeBorder()");
8049 Sew_Error aResult = SEW_OK;
8051 // ====================================
8052 // find side nodes and elements
8053 // ====================================
8055 list< const SMDS_MeshNode* > nSide[ 2 ];
8056 list< const SMDS_MeshElement* > eSide[ 2 ];
8057 list< const SMDS_MeshNode* >::iterator nIt[ 2 ];
8058 list< const SMDS_MeshElement* >::iterator eIt[ 2 ];
8062 if (!FindFreeBorder(theBordFirstNode,theBordSecondNode,theBordLastNode,
8063 nSide[0], eSide[0])) {
8064 MESSAGE(" Free Border 1 not found " );
8065 aResult = SEW_BORDER1_NOT_FOUND;
8067 if (theSideIsFreeBorder) {
8070 if (!FindFreeBorder(theSideFirstNode, theSideSecondNode, theSideThirdNode,
8071 nSide[1], eSide[1])) {
8072 MESSAGE(" Free Border 2 not found " );
8073 aResult = ( aResult != SEW_OK ? SEW_BOTH_BORDERS_NOT_FOUND : SEW_BORDER2_NOT_FOUND );
8076 if ( aResult != SEW_OK )
8079 if (!theSideIsFreeBorder) {
8083 // -------------------------------------------------------------------------
8085 // 1. If nodes to merge are not coincident, move nodes of the free border
8086 // from the coord sys defined by the direction from the first to last
8087 // nodes of the border to the correspondent sys of the side 2
8088 // 2. On the side 2, find the links most co-directed with the correspondent
8089 // links of the free border
8090 // -------------------------------------------------------------------------
8092 // 1. Since sewing may break if there are volumes to split on the side 2,
8093 // we wont move nodes but just compute new coordinates for them
8094 typedef map<const SMDS_MeshNode*, gp_XYZ> TNodeXYZMap;
8095 TNodeXYZMap nBordXYZ;
8096 list< const SMDS_MeshNode* >& bordNodes = nSide[ 0 ];
8097 list< const SMDS_MeshNode* >::iterator nBordIt;
8099 gp_XYZ Pb1( theBordFirstNode->X(), theBordFirstNode->Y(), theBordFirstNode->Z() );
8100 gp_XYZ Pb2( theBordLastNode->X(), theBordLastNode->Y(), theBordLastNode->Z() );
8101 gp_XYZ Ps1( theSideFirstNode->X(), theSideFirstNode->Y(), theSideFirstNode->Z() );
8102 gp_XYZ Ps2( theSideSecondNode->X(), theSideSecondNode->Y(), theSideSecondNode->Z() );
8103 double tol2 = 1.e-8;
8104 gp_Vec Vbs1( Pb1 - Ps1 ),Vbs2( Pb2 - Ps2 );
8105 if ( Vbs1.SquareMagnitude() > tol2 || Vbs2.SquareMagnitude() > tol2 ) {
8106 // Need node movement.
8108 // find X and Z axes to create trsf
8109 gp_Vec Zb( Pb1 - Pb2 ), Zs( Ps1 - Ps2 );
8111 if ( X.SquareMagnitude() <= gp::Resolution() * gp::Resolution() )
8113 X = gp_Ax2( gp::Origin(), Zb ).XDirection();
8116 gp_Ax3 toBordAx( Pb1, Zb, X );
8117 gp_Ax3 fromSideAx( Ps1, Zs, X );
8118 gp_Ax3 toGlobalAx( gp::Origin(), gp::DZ(), gp::DX() );
8120 gp_Trsf toBordSys, fromSide2Sys;
8121 toBordSys.SetTransformation( toBordAx );
8122 fromSide2Sys.SetTransformation( fromSideAx, toGlobalAx );
8123 fromSide2Sys.SetScaleFactor( Zs.Magnitude() / Zb.Magnitude() );
8126 for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
8127 const SMDS_MeshNode* n = *nBordIt;
8128 gp_XYZ xyz( n->X(),n->Y(),n->Z() );
8129 toBordSys.Transforms( xyz );
8130 fromSide2Sys.Transforms( xyz );
8131 nBordXYZ.insert( TNodeXYZMap::value_type( n, xyz ));
8135 // just insert nodes XYZ in the nBordXYZ map
8136 for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
8137 const SMDS_MeshNode* n = *nBordIt;
8138 nBordXYZ.insert( TNodeXYZMap::value_type( n, gp_XYZ( n->X(),n->Y(),n->Z() )));
8142 // 2. On the side 2, find the links most co-directed with the correspondent
8143 // links of the free border
8145 list< const SMDS_MeshElement* >& sideElems = eSide[ 1 ];
8146 list< const SMDS_MeshNode* >& sideNodes = nSide[ 1 ];
8147 sideNodes.push_back( theSideFirstNode );
8149 bool hasVolumes = false;
8150 LinkID_Gen aLinkID_Gen( GetMeshDS() );
8151 set<long> foundSideLinkIDs, checkedLinkIDs;
8152 SMDS_VolumeTool volume;
8153 //const SMDS_MeshNode* faceNodes[ 4 ];
8155 const SMDS_MeshNode* sideNode;
8156 const SMDS_MeshElement* sideElem;
8157 const SMDS_MeshNode* prevSideNode = theSideFirstNode;
8158 const SMDS_MeshNode* prevBordNode = theBordFirstNode;
8159 nBordIt = bordNodes.begin();
8161 // border node position and border link direction to compare with
8162 gp_XYZ bordPos = nBordXYZ[ *nBordIt ];
8163 gp_XYZ bordDir = bordPos - nBordXYZ[ prevBordNode ];
8164 // choose next side node by link direction or by closeness to
8165 // the current border node:
8166 bool searchByDir = ( *nBordIt != theBordLastNode );
8168 // find the next node on the Side 2
8170 double maxDot = -DBL_MAX, minDist = DBL_MAX;
8172 checkedLinkIDs.clear();
8173 gp_XYZ prevXYZ( prevSideNode->X(), prevSideNode->Y(), prevSideNode->Z() );
8175 // loop on inverse elements of current node (prevSideNode) on the Side 2
8176 SMDS_ElemIteratorPtr invElemIt = prevSideNode->GetInverseElementIterator();
8177 while ( invElemIt->more() )
8179 const SMDS_MeshElement* elem = invElemIt->next();
8180 // prepare data for a loop on links coming to prevSideNode, of a face or a volume
8181 int iPrevNode, iNode = 0, nbNodes = elem->NbNodes();
8182 vector< const SMDS_MeshNode* > faceNodes( nbNodes, (const SMDS_MeshNode*)0 );
8183 bool isVolume = volume.Set( elem );
8184 const SMDS_MeshNode** nodes = isVolume ? volume.GetNodes() : & faceNodes[0];
8185 if ( isVolume ) // --volume
8187 else if ( elem->GetType()==SMDSAbs_Face ) { // --face
8188 // retrieve all face nodes and find iPrevNode - an index of the prevSideNode
8189 if(elem->IsQuadratic()) {
8190 const SMDS_VtkFace* F =
8191 dynamic_cast<const SMDS_VtkFace*>(elem);
8192 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8193 // use special nodes iterator
8194 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8195 while( anIter->more() ) {
8196 nodes[ iNode ] = cast2Node(anIter->next());
8197 if ( nodes[ iNode++ ] == prevSideNode )
8198 iPrevNode = iNode - 1;
8202 SMDS_ElemIteratorPtr nIt = elem->nodesIterator();
8203 while ( nIt->more() ) {
8204 nodes[ iNode ] = cast2Node( nIt->next() );
8205 if ( nodes[ iNode++ ] == prevSideNode )
8206 iPrevNode = iNode - 1;
8209 // there are 2 links to check
8214 // loop on links, to be precise, on the second node of links
8215 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
8216 const SMDS_MeshNode* n = nodes[ iNode ];
8218 if ( !volume.IsLinked( n, prevSideNode ))
8222 if ( iNode ) // a node before prevSideNode
8223 n = nodes[ iPrevNode == 0 ? elem->NbNodes() - 1 : iPrevNode - 1 ];
8224 else // a node after prevSideNode
8225 n = nodes[ iPrevNode + 1 == elem->NbNodes() ? 0 : iPrevNode + 1 ];
8227 // check if this link was already used
8228 long iLink = aLinkID_Gen.GetLinkID( prevSideNode, n );
8229 bool isJustChecked = !checkedLinkIDs.insert( iLink ).second;
8230 if (!isJustChecked &&
8231 foundSideLinkIDs.find( iLink ) == foundSideLinkIDs.end() )
8233 // test a link geometrically
8234 gp_XYZ nextXYZ ( n->X(), n->Y(), n->Z() );
8235 bool linkIsBetter = false;
8236 double dot = 0.0, dist = 0.0;
8237 if ( searchByDir ) { // choose most co-directed link
8238 dot = bordDir * ( nextXYZ - prevXYZ ).Normalized();
8239 linkIsBetter = ( dot > maxDot );
8241 else { // choose link with the node closest to bordPos
8242 dist = ( nextXYZ - bordPos ).SquareModulus();
8243 linkIsBetter = ( dist < minDist );
8245 if ( linkIsBetter ) {
8254 } // loop on inverse elements of prevSideNode
8257 MESSAGE(" Cant find path by links of the Side 2 ");
8258 return SEW_BAD_SIDE_NODES;
8260 sideNodes.push_back( sideNode );
8261 sideElems.push_back( sideElem );
8262 foundSideLinkIDs.insert ( linkID );
8263 prevSideNode = sideNode;
8265 if ( *nBordIt == theBordLastNode )
8266 searchByDir = false;
8268 // find the next border link to compare with
8269 gp_XYZ sidePos( sideNode->X(), sideNode->Y(), sideNode->Z() );
8270 searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8271 // move to next border node if sideNode is before forward border node (bordPos)
8272 while ( *nBordIt != theBordLastNode && !searchByDir ) {
8273 prevBordNode = *nBordIt;
8275 bordPos = nBordXYZ[ *nBordIt ];
8276 bordDir = bordPos - nBordXYZ[ prevBordNode ];
8277 searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
8281 while ( sideNode != theSideSecondNode );
8283 if ( hasVolumes && sideNodes.size () != bordNodes.size() && !toCreatePolyedrs) {
8284 MESSAGE("VOLUME SPLITTING IS FORBIDDEN");
8285 return SEW_VOLUMES_TO_SPLIT; // volume splitting is forbidden
8287 } // end nodes search on the side 2
8289 // ============================
8290 // sew the border to the side 2
8291 // ============================
8293 int nbNodes[] = { nSide[0].size(), nSide[1].size() };
8294 int maxNbNodes = Max( nbNodes[0], nbNodes[1] );
8296 TListOfListOfNodes nodeGroupsToMerge;
8297 if ( nbNodes[0] == nbNodes[1] ||
8298 ( theSideIsFreeBorder && !theSideThirdNode)) {
8300 // all nodes are to be merged
8302 for (nIt[0] = nSide[0].begin(), nIt[1] = nSide[1].begin();
8303 nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end();
8304 nIt[0]++, nIt[1]++ )
8306 nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8307 nodeGroupsToMerge.back().push_back( *nIt[1] ); // to keep
8308 nodeGroupsToMerge.back().push_back( *nIt[0] ); // to remove
8313 // insert new nodes into the border and the side to get equal nb of segments
8315 // get normalized parameters of nodes on the borders
8316 //double param[ 2 ][ maxNbNodes ];
8318 param[0] = new double [ maxNbNodes ];
8319 param[1] = new double [ maxNbNodes ];
8321 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8322 list< const SMDS_MeshNode* >& nodes = nSide[ iBord ];
8323 list< const SMDS_MeshNode* >::iterator nIt = nodes.begin();
8324 const SMDS_MeshNode* nPrev = *nIt;
8325 double bordLength = 0;
8326 for ( iNode = 0; nIt != nodes.end(); nIt++, iNode++ ) { // loop on border nodes
8327 const SMDS_MeshNode* nCur = *nIt;
8328 gp_XYZ segment (nCur->X() - nPrev->X(),
8329 nCur->Y() - nPrev->Y(),
8330 nCur->Z() - nPrev->Z());
8331 double segmentLen = segment.Modulus();
8332 bordLength += segmentLen;
8333 param[ iBord ][ iNode ] = bordLength;
8336 // normalize within [0,1]
8337 for ( iNode = 0; iNode < nbNodes[ iBord ]; iNode++ ) {
8338 param[ iBord ][ iNode ] /= bordLength;
8342 // loop on border segments
8343 const SMDS_MeshNode *nPrev[ 2 ] = { 0, 0 };
8344 int i[ 2 ] = { 0, 0 };
8345 nIt[0] = nSide[0].begin(); eIt[0] = eSide[0].begin();
8346 nIt[1] = nSide[1].begin(); eIt[1] = eSide[1].begin();
8348 TElemOfNodeListMap insertMap;
8349 TElemOfNodeListMap::iterator insertMapIt;
8351 // key: elem to insert nodes into
8352 // value: 2 nodes to insert between + nodes to be inserted
8354 bool next[ 2 ] = { false, false };
8356 // find min adjacent segment length after sewing
8357 double nextParam = 10., prevParam = 0;
8358 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8359 if ( i[ iBord ] + 1 < nbNodes[ iBord ])
8360 nextParam = Min( nextParam, param[iBord][ i[iBord] + 1 ]);
8361 if ( i[ iBord ] > 0 )
8362 prevParam = Max( prevParam, param[iBord][ i[iBord] - 1 ]);
8364 double minParam = Min( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8365 double maxParam = Max( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8366 double minSegLen = Min( nextParam - minParam, maxParam - prevParam );
8368 // choose to insert or to merge nodes
8369 double du = param[ 1 ][ i[1] ] - param[ 0 ][ i[0] ];
8370 if ( Abs( du ) <= minSegLen * 0.2 ) {
8373 nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8374 const SMDS_MeshNode* n0 = *nIt[0];
8375 const SMDS_MeshNode* n1 = *nIt[1];
8376 nodeGroupsToMerge.back().push_back( n1 );
8377 nodeGroupsToMerge.back().push_back( n0 );
8378 // position of node of the border changes due to merge
8379 param[ 0 ][ i[0] ] += du;
8380 // move n1 for the sake of elem shape evaluation during insertion.
8381 // n1 will be removed by MergeNodes() anyway
8382 const_cast<SMDS_MeshNode*>( n0 )->setXYZ( n1->X(), n1->Y(), n1->Z() );
8383 next[0] = next[1] = true;
8388 int intoBord = ( du < 0 ) ? 0 : 1;
8389 const SMDS_MeshElement* elem = *eIt[ intoBord ];
8390 const SMDS_MeshNode* n1 = nPrev[ intoBord ];
8391 const SMDS_MeshNode* n2 = *nIt[ intoBord ];
8392 const SMDS_MeshNode* nIns = *nIt[ 1 - intoBord ];
8393 if ( intoBord == 1 ) {
8394 // move node of the border to be on a link of elem of the side
8395 gp_XYZ p1 (n1->X(), n1->Y(), n1->Z());
8396 gp_XYZ p2 (n2->X(), n2->Y(), n2->Z());
8397 double ratio = du / ( param[ 1 ][ i[1] ] - param[ 1 ][ i[1]-1 ]);
8398 gp_XYZ p = p2 * ( 1 - ratio ) + p1 * ratio;
8399 GetMeshDS()->MoveNode( nIns, p.X(), p.Y(), p.Z() );
8401 insertMapIt = insertMap.find( elem );
8402 bool notFound = ( insertMapIt == insertMap.end() );
8403 bool otherLink = ( !notFound && (*insertMapIt).second.front() != n1 );
8405 // insert into another link of the same element:
8406 // 1. perform insertion into the other link of the elem
8407 list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8408 const SMDS_MeshNode* n12 = nodeList.front(); nodeList.pop_front();
8409 const SMDS_MeshNode* n22 = nodeList.front(); nodeList.pop_front();
8410 InsertNodesIntoLink( elem, n12, n22, nodeList, toCreatePolygons );
8411 // 2. perform insertion into the link of adjacent faces
8413 const SMDS_MeshElement* adjElem = findAdjacentFace( n12, n22, elem );
8415 InsertNodesIntoLink( adjElem, n12, n22, nodeList, toCreatePolygons );
8419 if (toCreatePolyedrs) {
8420 // perform insertion into the links of adjacent volumes
8421 UpdateVolumes(n12, n22, nodeList);
8423 // 3. find an element appeared on n1 and n2 after the insertion
8424 insertMap.erase( elem );
8425 elem = findAdjacentFace( n1, n2, 0 );
8427 if ( notFound || otherLink ) {
8428 // add element and nodes of the side into the insertMap
8429 insertMapIt = insertMap.insert
8430 ( TElemOfNodeListMap::value_type( elem, list<const SMDS_MeshNode*>() )).first;
8431 (*insertMapIt).second.push_back( n1 );
8432 (*insertMapIt).second.push_back( n2 );
8434 // add node to be inserted into elem
8435 (*insertMapIt).second.push_back( nIns );
8436 next[ 1 - intoBord ] = true;
8439 // go to the next segment
8440 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8441 if ( next[ iBord ] ) {
8442 if ( i[ iBord ] != 0 && eIt[ iBord ] != eSide[ iBord ].end())
8444 nPrev[ iBord ] = *nIt[ iBord ];
8445 nIt[ iBord ]++; i[ iBord ]++;
8449 while ( nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end());
8451 // perform insertion of nodes into elements
8453 for (insertMapIt = insertMap.begin();
8454 insertMapIt != insertMap.end();
8457 const SMDS_MeshElement* elem = (*insertMapIt).first;
8458 list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8459 const SMDS_MeshNode* n1 = nodeList.front(); nodeList.pop_front();
8460 const SMDS_MeshNode* n2 = nodeList.front(); nodeList.pop_front();
8462 InsertNodesIntoLink( elem, n1, n2, nodeList, toCreatePolygons );
8464 if ( !theSideIsFreeBorder ) {
8465 // look for and insert nodes into the faces adjacent to elem
8467 const SMDS_MeshElement* adjElem = findAdjacentFace( n1, n2, elem );
8469 InsertNodesIntoLink( adjElem, n1, n2, nodeList, toCreatePolygons );
8474 if (toCreatePolyedrs) {
8475 // perform insertion into the links of adjacent volumes
8476 UpdateVolumes(n1, n2, nodeList);
8482 } // end: insert new nodes
8484 MergeNodes ( nodeGroupsToMerge );
8489 //=======================================================================
8490 //function : InsertNodesIntoLink
8491 //purpose : insert theNodesToInsert into theFace between theBetweenNode1
8492 // and theBetweenNode2 and split theElement
8493 //=======================================================================
8495 void SMESH_MeshEditor::InsertNodesIntoLink(const SMDS_MeshElement* theFace,
8496 const SMDS_MeshNode* theBetweenNode1,
8497 const SMDS_MeshNode* theBetweenNode2,
8498 list<const SMDS_MeshNode*>& theNodesToInsert,
8499 const bool toCreatePoly)
8501 if ( theFace->GetType() != SMDSAbs_Face ) return;
8503 // find indices of 2 link nodes and of the rest nodes
8504 int iNode = 0, il1, il2, i3, i4;
8505 il1 = il2 = i3 = i4 = -1;
8506 //const SMDS_MeshNode* nodes[ theFace->NbNodes() ];
8507 vector<const SMDS_MeshNode*> nodes( theFace->NbNodes() );
8509 if(theFace->IsQuadratic()) {
8510 const SMDS_VtkFace* F =
8511 dynamic_cast<const SMDS_VtkFace*>(theFace);
8512 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8513 // use special nodes iterator
8514 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8515 while( anIter->more() ) {
8516 const SMDS_MeshNode* n = cast2Node(anIter->next());
8517 if ( n == theBetweenNode1 )
8519 else if ( n == theBetweenNode2 )
8525 nodes[ iNode++ ] = n;
8529 SMDS_ElemIteratorPtr nodeIt = theFace->nodesIterator();
8530 while ( nodeIt->more() ) {
8531 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8532 if ( n == theBetweenNode1 )
8534 else if ( n == theBetweenNode2 )
8540 nodes[ iNode++ ] = n;
8543 if ( il1 < 0 || il2 < 0 || i3 < 0 )
8546 // arrange link nodes to go one after another regarding the face orientation
8547 bool reverse = ( Abs( il2 - il1 ) == 1 ? il2 < il1 : il1 < il2 );
8548 list<const SMDS_MeshNode *> aNodesToInsert = theNodesToInsert;
8553 aNodesToInsert.reverse();
8555 // check that not link nodes of a quadrangles are in good order
8556 int nbFaceNodes = theFace->NbNodes();
8557 if ( nbFaceNodes == 4 && i4 - i3 != 1 ) {
8563 if (toCreatePoly || theFace->IsPoly()) {
8566 vector<const SMDS_MeshNode *> poly_nodes (nbFaceNodes + aNodesToInsert.size());
8568 // add nodes of face up to first node of link
8571 if(theFace->IsQuadratic()) {
8572 const SMDS_VtkFace* F =
8573 dynamic_cast<const SMDS_VtkFace*>(theFace);
8574 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8575 // use special nodes iterator
8576 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8577 while( anIter->more() && !isFLN ) {
8578 const SMDS_MeshNode* n = cast2Node(anIter->next());
8579 poly_nodes[iNode++] = n;
8580 if (n == nodes[il1]) {
8584 // add nodes to insert
8585 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8586 for (; nIt != aNodesToInsert.end(); nIt++) {
8587 poly_nodes[iNode++] = *nIt;
8589 // add nodes of face starting from last node of link
8590 while ( anIter->more() ) {
8591 poly_nodes[iNode++] = cast2Node(anIter->next());
8595 SMDS_ElemIteratorPtr nodeIt = theFace->nodesIterator();
8596 while ( nodeIt->more() && !isFLN ) {
8597 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8598 poly_nodes[iNode++] = n;
8599 if (n == nodes[il1]) {
8603 // add nodes to insert
8604 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8605 for (; nIt != aNodesToInsert.end(); nIt++) {
8606 poly_nodes[iNode++] = *nIt;
8608 // add nodes of face starting from last node of link
8609 while ( nodeIt->more() ) {
8610 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8611 poly_nodes[iNode++] = n;
8615 // edit or replace the face
8616 SMESHDS_Mesh *aMesh = GetMeshDS();
8618 if (theFace->IsPoly()) {
8619 aMesh->ChangePolygonNodes(theFace, poly_nodes);
8622 int aShapeId = FindShape( theFace );
8624 SMDS_MeshElement* newElem = aMesh->AddPolygonalFace(poly_nodes);
8625 myLastCreatedElems.Append(newElem);
8626 if ( aShapeId && newElem )
8627 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8629 aMesh->RemoveElement(theFace);
8634 SMESHDS_Mesh *aMesh = GetMeshDS();
8635 if( !theFace->IsQuadratic() ) {
8637 // put aNodesToInsert between theBetweenNode1 and theBetweenNode2
8638 int nbLinkNodes = 2 + aNodesToInsert.size();
8639 //const SMDS_MeshNode* linkNodes[ nbLinkNodes ];
8640 vector<const SMDS_MeshNode*> linkNodes( nbLinkNodes );
8641 linkNodes[ 0 ] = nodes[ il1 ];
8642 linkNodes[ nbLinkNodes - 1 ] = nodes[ il2 ];
8643 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8644 for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
8645 linkNodes[ iNode++ ] = *nIt;
8647 // decide how to split a quadrangle: compare possible variants
8648 // and choose which of splits to be a quadrangle
8649 int i1, i2, iSplit, nbSplits = nbLinkNodes - 1, iBestQuad;
8650 if ( nbFaceNodes == 3 ) {
8651 iBestQuad = nbSplits;
8654 else if ( nbFaceNodes == 4 ) {
8655 SMESH::Controls::NumericalFunctorPtr aCrit( new SMESH::Controls::AspectRatio);
8656 double aBestRate = DBL_MAX;
8657 for ( int iQuad = 0; iQuad < nbSplits; iQuad++ ) {
8659 double aBadRate = 0;
8660 // evaluate elements quality
8661 for ( iSplit = 0; iSplit < nbSplits; iSplit++ ) {
8662 if ( iSplit == iQuad ) {
8663 SMDS_FaceOfNodes quad (linkNodes[ i1++ ],
8667 aBadRate += getBadRate( &quad, aCrit );
8670 SMDS_FaceOfNodes tria (linkNodes[ i1++ ],
8672 nodes[ iSplit < iQuad ? i4 : i3 ]);
8673 aBadRate += getBadRate( &tria, aCrit );
8677 if ( aBadRate < aBestRate ) {
8679 aBestRate = aBadRate;
8684 // create new elements
8685 int aShapeId = FindShape( theFace );
8688 for ( iSplit = 0; iSplit < nbSplits - 1; iSplit++ ) {
8689 SMDS_MeshElement* newElem = 0;
8690 if ( iSplit == iBestQuad )
8691 newElem = aMesh->AddFace (linkNodes[ i1++ ],
8696 newElem = aMesh->AddFace (linkNodes[ i1++ ],
8698 nodes[ iSplit < iBestQuad ? i4 : i3 ]);
8699 myLastCreatedElems.Append(newElem);
8700 if ( aShapeId && newElem )
8701 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8704 // change nodes of theFace
8705 const SMDS_MeshNode* newNodes[ 4 ];
8706 newNodes[ 0 ] = linkNodes[ i1 ];
8707 newNodes[ 1 ] = linkNodes[ i2 ];
8708 newNodes[ 2 ] = nodes[ iSplit >= iBestQuad ? i3 : i4 ];
8709 newNodes[ 3 ] = nodes[ i4 ];
8710 //aMesh->ChangeElementNodes( theFace, newNodes, iSplit == iBestQuad ? 4 : 3 );
8711 const SMDS_MeshElement* newElem = 0;
8712 if (iSplit == iBestQuad)
8713 newElem = aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2], newNodes[3] );
8715 newElem = aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2] );
8716 myLastCreatedElems.Append(newElem);
8717 if ( aShapeId && newElem )
8718 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8719 } // end if(!theFace->IsQuadratic())
8720 else { // theFace is quadratic
8721 // we have to split theFace on simple triangles and one simple quadrangle
8723 int nbshift = tmp*2;
8724 // shift nodes in nodes[] by nbshift
8726 for(i=0; i<nbshift; i++) {
8727 const SMDS_MeshNode* n = nodes[0];
8728 for(j=0; j<nbFaceNodes-1; j++) {
8729 nodes[j] = nodes[j+1];
8731 nodes[nbFaceNodes-1] = n;
8733 il1 = il1 - nbshift;
8734 // now have to insert nodes between n0 and n1 or n1 and n2 (see below)
8735 // n0 n1 n2 n0 n1 n2
8736 // +-----+-----+ +-----+-----+
8745 // create new elements
8746 int aShapeId = FindShape( theFace );
8749 if(nbFaceNodes==6) { // quadratic triangle
8750 SMDS_MeshElement* newElem =
8751 aMesh->AddFace(nodes[3],nodes[4],nodes[5]);
8752 myLastCreatedElems.Append(newElem);
8753 if ( aShapeId && newElem )
8754 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8755 if(theFace->IsMediumNode(nodes[il1])) {
8756 // create quadrangle
8757 newElem = aMesh->AddFace(nodes[0],nodes[1],nodes[3],nodes[5]);
8758 myLastCreatedElems.Append(newElem);
8759 if ( aShapeId && newElem )
8760 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8766 // create quadrangle
8767 newElem = aMesh->AddFace(nodes[1],nodes[2],nodes[3],nodes[5]);
8768 myLastCreatedElems.Append(newElem);
8769 if ( aShapeId && newElem )
8770 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8776 else { // nbFaceNodes==8 - quadratic quadrangle
8777 SMDS_MeshElement* newElem =
8778 aMesh->AddFace(nodes[3],nodes[4],nodes[5]);
8779 myLastCreatedElems.Append(newElem);
8780 if ( aShapeId && newElem )
8781 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8782 newElem = aMesh->AddFace(nodes[5],nodes[6],nodes[7]);
8783 myLastCreatedElems.Append(newElem);
8784 if ( aShapeId && newElem )
8785 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8786 newElem = aMesh->AddFace(nodes[5],nodes[7],nodes[3]);
8787 myLastCreatedElems.Append(newElem);
8788 if ( aShapeId && newElem )
8789 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8790 if(theFace->IsMediumNode(nodes[il1])) {
8791 // create quadrangle
8792 newElem = aMesh->AddFace(nodes[0],nodes[1],nodes[3],nodes[7]);
8793 myLastCreatedElems.Append(newElem);
8794 if ( aShapeId && newElem )
8795 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8801 // create quadrangle
8802 newElem = aMesh->AddFace(nodes[1],nodes[2],nodes[3],nodes[7]);
8803 myLastCreatedElems.Append(newElem);
8804 if ( aShapeId && newElem )
8805 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8811 // create needed triangles using n1,n2,n3 and inserted nodes
8812 int nbn = 2 + aNodesToInsert.size();
8813 //const SMDS_MeshNode* aNodes[nbn];
8814 vector<const SMDS_MeshNode*> aNodes(nbn);
8815 aNodes[0] = nodes[n1];
8816 aNodes[nbn-1] = nodes[n2];
8817 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8818 for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
8819 aNodes[iNode++] = *nIt;
8821 for(i=1; i<nbn; i++) {
8822 SMDS_MeshElement* newElem =
8823 aMesh->AddFace(aNodes[i-1],aNodes[i],nodes[n3]);
8824 myLastCreatedElems.Append(newElem);
8825 if ( aShapeId && newElem )
8826 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8830 aMesh->RemoveElement(theFace);
8833 //=======================================================================
8834 //function : UpdateVolumes
8836 //=======================================================================
8837 void SMESH_MeshEditor::UpdateVolumes (const SMDS_MeshNode* theBetweenNode1,
8838 const SMDS_MeshNode* theBetweenNode2,
8839 list<const SMDS_MeshNode*>& theNodesToInsert)
8841 myLastCreatedElems.Clear();
8842 myLastCreatedNodes.Clear();
8844 SMDS_ElemIteratorPtr invElemIt = theBetweenNode1->GetInverseElementIterator(SMDSAbs_Volume);
8845 while (invElemIt->more()) { // loop on inverse elements of theBetweenNode1
8846 const SMDS_MeshElement* elem = invElemIt->next();
8848 // check, if current volume has link theBetweenNode1 - theBetweenNode2
8849 SMDS_VolumeTool aVolume (elem);
8850 if (!aVolume.IsLinked(theBetweenNode1, theBetweenNode2))
8853 // insert new nodes in all faces of the volume, sharing link theBetweenNode1 - theBetweenNode2
8854 int iface, nbFaces = aVolume.NbFaces();
8855 vector<const SMDS_MeshNode *> poly_nodes;
8856 vector<int> quantities (nbFaces);
8858 for (iface = 0; iface < nbFaces; iface++) {
8859 int nbFaceNodes = aVolume.NbFaceNodes(iface), nbInserted = 0;
8860 // faceNodes will contain (nbFaceNodes + 1) nodes, last = first
8861 const SMDS_MeshNode** faceNodes = aVolume.GetFaceNodes(iface);
8863 for (int inode = 0; inode < nbFaceNodes; inode++) {
8864 poly_nodes.push_back(faceNodes[inode]);
8866 if (nbInserted == 0) {
8867 if (faceNodes[inode] == theBetweenNode1) {
8868 if (faceNodes[inode + 1] == theBetweenNode2) {
8869 nbInserted = theNodesToInsert.size();
8871 // add nodes to insert
8872 list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.begin();
8873 for (; nIt != theNodesToInsert.end(); nIt++) {
8874 poly_nodes.push_back(*nIt);
8878 else if (faceNodes[inode] == theBetweenNode2) {
8879 if (faceNodes[inode + 1] == theBetweenNode1) {
8880 nbInserted = theNodesToInsert.size();
8882 // add nodes to insert in reversed order
8883 list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.end();
8885 for (; nIt != theNodesToInsert.begin(); nIt--) {
8886 poly_nodes.push_back(*nIt);
8888 poly_nodes.push_back(*nIt);
8895 quantities[iface] = nbFaceNodes + nbInserted;
8898 // Replace or update the volume
8899 SMESHDS_Mesh *aMesh = GetMeshDS();
8901 if (elem->IsPoly()) {
8902 aMesh->ChangePolyhedronNodes(elem, poly_nodes, quantities);
8906 int aShapeId = FindShape( elem );
8908 SMDS_MeshElement* newElem =
8909 aMesh->AddPolyhedralVolume(poly_nodes, quantities);
8910 myLastCreatedElems.Append(newElem);
8911 if (aShapeId && newElem)
8912 aMesh->SetMeshElementOnShape(newElem, aShapeId);
8914 aMesh->RemoveElement(elem);
8921 //================================================================================
8923 * \brief Transform any volume into data of SMDSEntity_Polyhedra
8925 //================================================================================
8927 void volumeToPolyhedron( const SMDS_MeshElement* elem,
8928 vector<const SMDS_MeshNode *> & nodes,
8929 vector<int> & nbNodeInFaces )
8932 nbNodeInFaces.clear();
8933 SMDS_VolumeTool vTool ( elem );
8934 for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
8936 const SMDS_MeshNode** fNodes = vTool.GetFaceNodes( iF );
8937 nodes.insert( nodes.end(), fNodes, fNodes + vTool.NbFaceNodes( iF ));
8938 nbNodeInFaces.push_back( vTool.NbFaceNodes( iF ));
8943 //=======================================================================
8945 * \brief Convert elements contained in a sub-mesh to quadratic
8946 * \return int - nb of checked elements
8948 //=======================================================================
8950 int SMESH_MeshEditor::convertElemToQuadratic(SMESHDS_SubMesh * theSm,
8951 SMESH_MesherHelper& theHelper,
8952 const bool theForce3d)
8955 if( !theSm ) return nbElem;
8957 vector<int> nbNodeInFaces;
8958 vector<const SMDS_MeshNode *> nodes;
8959 SMDS_ElemIteratorPtr ElemItr = theSm->GetElements();
8960 while(ElemItr->more())
8963 const SMDS_MeshElement* elem = ElemItr->next();
8964 if( !elem ) continue;
8966 // analyse a necessity of conversion
8967 const SMDSAbs_ElementType aType = elem->GetType();
8968 if ( aType < SMDSAbs_Edge || aType > SMDSAbs_Volume )
8970 const SMDSAbs_EntityType aGeomType = elem->GetEntityType();
8971 bool hasCentralNodes = false;
8972 if ( elem->IsQuadratic() )
8975 switch ( aGeomType ) {
8976 case SMDSEntity_Quad_Triangle:
8977 case SMDSEntity_Quad_Quadrangle:
8978 case SMDSEntity_Quad_Hexa:
8979 alreadyOK = !theHelper.GetIsBiQuadratic(); break;
8981 case SMDSEntity_BiQuad_Triangle:
8982 case SMDSEntity_BiQuad_Quadrangle:
8983 case SMDSEntity_TriQuad_Hexa:
8984 alreadyOK = theHelper.GetIsBiQuadratic();
8985 hasCentralNodes = true;
8990 // take into account already present modium nodes
8992 case SMDSAbs_Volume:
8993 theHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( elem )); break;
8995 theHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( elem )); break;
8997 theHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( elem )); break;
9003 // get elem data needed to re-create it
9005 const int id = elem->GetID();
9006 const int nbNodes = elem->NbCornerNodes();
9007 nodes.assign(elem->begin_nodes(), elem->end_nodes());
9008 if ( aGeomType == SMDSEntity_Polyhedra )
9009 nbNodeInFaces = static_cast<const SMDS_VtkVolume* >( elem )->GetQuantities();
9010 else if ( aGeomType == SMDSEntity_Hexagonal_Prism )
9011 volumeToPolyhedron( elem, nodes, nbNodeInFaces );
9013 // remove a linear element
9014 GetMeshDS()->RemoveFreeElement(elem, theSm, /*fromGroups=*/false);
9016 // remove central nodes of biquadratic elements (biquad->quad convertion)
9017 if ( hasCentralNodes )
9018 for ( size_t i = nbNodes * 2; i < nodes.size(); ++i )
9019 if ( nodes[i]->NbInverseElements() == 0 )
9020 GetMeshDS()->RemoveFreeNode( nodes[i], theSm, /*fromGroups=*/true );
9022 const SMDS_MeshElement* NewElem = 0;
9028 NewElem = theHelper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9036 NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
9039 NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9042 NewElem = theHelper.AddPolygonalFace(nodes, id, theForce3d);
9046 case SMDSAbs_Volume :
9050 case SMDSEntity_Tetra:
9051 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9053 case SMDSEntity_Pyramid:
9054 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], id, theForce3d);
9056 case SMDSEntity_Penta:
9057 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], nodes[5], id, theForce3d);
9059 case SMDSEntity_Hexa:
9060 case SMDSEntity_Quad_Hexa:
9061 case SMDSEntity_TriQuad_Hexa:
9062 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9063 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9065 case SMDSEntity_Hexagonal_Prism:
9067 NewElem = theHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9074 ReplaceElemInGroups( elem, NewElem, GetMeshDS());
9075 if( NewElem && NewElem->getshapeId() < 1 )
9076 theSm->AddElement( NewElem );
9080 //=======================================================================
9081 //function : ConvertToQuadratic
9083 //=======================================================================
9085 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d, const bool theToBiQuad)
9087 SMESHDS_Mesh* meshDS = GetMeshDS();
9089 SMESH_MesherHelper aHelper(*myMesh);
9091 aHelper.SetIsQuadratic( true );
9092 aHelper.SetIsBiQuadratic( theToBiQuad );
9093 aHelper.SetElementsOnShape(true);
9094 aHelper.ToFixNodeParameters( true );
9096 // convert elements assigned to sub-meshes
9097 int nbCheckedElems = 0;
9098 if ( myMesh->HasShapeToMesh() )
9100 if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9102 SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9103 while ( smIt->more() ) {
9104 SMESH_subMesh* sm = smIt->next();
9105 if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() ) {
9106 aHelper.SetSubShape( sm->GetSubShape() );
9107 nbCheckedElems += convertElemToQuadratic(smDS, aHelper, theForce3d);
9113 // convert elements NOT assigned to sub-meshes
9114 int totalNbElems = meshDS->NbEdges() + meshDS->NbFaces() + meshDS->NbVolumes();
9115 if ( nbCheckedElems < totalNbElems ) // not all elements are in sub-meshes
9117 aHelper.SetElementsOnShape(false);
9118 SMESHDS_SubMesh *smDS = 0;
9121 SMDS_EdgeIteratorPtr aEdgeItr = meshDS->edgesIterator();
9122 while( aEdgeItr->more() )
9124 const SMDS_MeshEdge* edge = aEdgeItr->next();
9125 if ( !edge->IsQuadratic() )
9127 int id = edge->GetID();
9128 const SMDS_MeshNode* n1 = edge->GetNode(0);
9129 const SMDS_MeshNode* n2 = edge->GetNode(1);
9131 meshDS->RemoveFreeElement(edge, smDS, /*fromGroups=*/false);
9133 const SMDS_MeshEdge* NewEdge = aHelper.AddEdge(n1, n2, id, theForce3d);
9134 ReplaceElemInGroups( edge, NewEdge, GetMeshDS());
9138 aHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( edge ));
9143 SMDS_FaceIteratorPtr aFaceItr = meshDS->facesIterator();
9144 while( aFaceItr->more() )
9146 const SMDS_MeshFace* face = aFaceItr->next();
9147 if ( !face ) continue;
9149 const SMDSAbs_EntityType type = face->GetEntityType();
9153 case SMDSEntity_Quad_Triangle:
9154 case SMDSEntity_Quad_Quadrangle:
9155 alreadyOK = !theToBiQuad;
9156 aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
9158 case SMDSEntity_BiQuad_Triangle:
9159 case SMDSEntity_BiQuad_Quadrangle:
9160 alreadyOK = theToBiQuad;
9161 aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
9163 default: alreadyOK = false;
9168 const int id = face->GetID();
9169 vector<const SMDS_MeshNode *> nodes ( face->begin_nodes(), face->end_nodes());
9171 meshDS->RemoveFreeElement(face, smDS, /*fromGroups=*/false);
9173 SMDS_MeshFace * NewFace = 0;
9176 case SMDSEntity_Triangle:
9177 case SMDSEntity_Quad_Triangle:
9178 case SMDSEntity_BiQuad_Triangle:
9179 NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
9180 if ( nodes.size() == 7 && nodes[6]->NbInverseElements() == 0 ) // rm a central node
9181 GetMeshDS()->RemoveFreeNode( nodes[6], /*sm=*/0, /*fromGroups=*/true );
9184 case SMDSEntity_Quadrangle:
9185 case SMDSEntity_Quad_Quadrangle:
9186 case SMDSEntity_BiQuad_Quadrangle:
9187 NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9188 if ( nodes.size() == 9 && nodes[8]->NbInverseElements() == 0 ) // rm a central node
9189 GetMeshDS()->RemoveFreeNode( nodes[8], /*sm=*/0, /*fromGroups=*/true );
9193 NewFace = aHelper.AddPolygonalFace(nodes, id, theForce3d);
9195 ReplaceElemInGroups( face, NewFace, GetMeshDS());
9199 vector<int> nbNodeInFaces;
9200 SMDS_VolumeIteratorPtr aVolumeItr = meshDS->volumesIterator();
9201 while(aVolumeItr->more())
9203 const SMDS_MeshVolume* volume = aVolumeItr->next();
9204 if ( !volume ) continue;
9206 const SMDSAbs_EntityType type = volume->GetEntityType();
9207 if ( volume->IsQuadratic() )
9212 case SMDSEntity_Quad_Hexa: alreadyOK = !theToBiQuad; break;
9213 case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; break;
9214 default: alreadyOK = true;
9218 aHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( volume ));
9222 const int id = volume->GetID();
9223 vector<const SMDS_MeshNode *> nodes (volume->begin_nodes(), volume->end_nodes());
9224 if ( type == SMDSEntity_Polyhedra )
9225 nbNodeInFaces = static_cast<const SMDS_VtkVolume* >(volume)->GetQuantities();
9226 else if ( type == SMDSEntity_Hexagonal_Prism )
9227 volumeToPolyhedron( volume, nodes, nbNodeInFaces );
9229 meshDS->RemoveFreeElement(volume, smDS, /*fromGroups=*/false);
9231 SMDS_MeshVolume * NewVolume = 0;
9234 case SMDSEntity_Tetra:
9235 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d );
9237 case SMDSEntity_Hexa:
9238 case SMDSEntity_Quad_Hexa:
9239 case SMDSEntity_TriQuad_Hexa:
9240 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9241 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9242 for ( size_t i = 20; i < nodes.size(); ++i ) // rm central nodes
9243 if ( nodes[i]->NbInverseElements() == 0 )
9244 GetMeshDS()->RemoveFreeNode( nodes[i], /*sm=*/0, /*fromGroups=*/true );
9246 case SMDSEntity_Pyramid:
9247 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9248 nodes[3], nodes[4], id, theForce3d);
9250 case SMDSEntity_Penta:
9251 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
9252 nodes[3], nodes[4], nodes[5], id, theForce3d);
9254 case SMDSEntity_Hexagonal_Prism:
9256 NewVolume = aHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
9258 ReplaceElemInGroups(volume, NewVolume, meshDS);
9263 { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9264 // aHelper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9265 // aHelper.FixQuadraticElements(myError);
9266 SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
9270 //================================================================================
9272 * \brief Makes given elements quadratic
9273 * \param theForce3d - if true, the medium nodes will be placed in the middle of link
9274 * \param theElements - elements to make quadratic
9276 //================================================================================
9278 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d,
9279 TIDSortedElemSet& theElements,
9280 const bool theToBiQuad)
9282 if ( theElements.empty() ) return;
9284 // we believe that all theElements are of the same type
9285 const SMDSAbs_ElementType elemType = (*theElements.begin())->GetType();
9287 // get all nodes shared by theElements
9288 TIDSortedNodeSet allNodes;
9289 TIDSortedElemSet::iterator eIt = theElements.begin();
9290 for ( ; eIt != theElements.end(); ++eIt )
9291 allNodes.insert( (*eIt)->begin_nodes(), (*eIt)->end_nodes() );
9293 // complete theElements with elements of lower dim whose all nodes are in allNodes
9295 TIDSortedElemSet quadAdjacentElems [ SMDSAbs_NbElementTypes ]; // quadratic adjacent elements
9296 TIDSortedElemSet checkedAdjacentElems [ SMDSAbs_NbElementTypes ];
9297 TIDSortedNodeSet::iterator nIt = allNodes.begin();
9298 for ( ; nIt != allNodes.end(); ++nIt )
9300 const SMDS_MeshNode* n = *nIt;
9301 SMDS_ElemIteratorPtr invIt = n->GetInverseElementIterator();
9302 while ( invIt->more() )
9304 const SMDS_MeshElement* e = invIt->next();
9305 const SMDSAbs_ElementType type = e->GetType();
9306 if ( e->IsQuadratic() )
9308 quadAdjacentElems[ type ].insert( e );
9311 switch ( e->GetEntityType() ) {
9312 case SMDSEntity_Quad_Triangle:
9313 case SMDSEntity_Quad_Quadrangle:
9314 case SMDSEntity_Quad_Hexa: alreadyOK = !theToBiQuad; break;
9315 case SMDSEntity_BiQuad_Triangle:
9316 case SMDSEntity_BiQuad_Quadrangle:
9317 case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; break;
9318 default: alreadyOK = true;
9323 if ( type >= elemType )
9324 continue; // same type or more complex linear element
9326 if ( !checkedAdjacentElems[ type ].insert( e ).second )
9327 continue; // e is already checked
9331 SMDS_NodeIteratorPtr nodeIt = e->nodeIterator();
9332 while ( nodeIt->more() && allIn )
9333 allIn = allNodes.count( nodeIt->next() );
9335 theElements.insert(e );
9339 SMESH_MesherHelper helper(*myMesh);
9340 helper.SetIsQuadratic( true );
9341 helper.SetIsBiQuadratic( theToBiQuad );
9343 // add links of quadratic adjacent elements to the helper
9345 if ( !quadAdjacentElems[SMDSAbs_Edge].empty() )
9346 for ( eIt = quadAdjacentElems[SMDSAbs_Edge].begin();
9347 eIt != quadAdjacentElems[SMDSAbs_Edge].end(); ++eIt )
9349 helper.AddTLinks( static_cast< const SMDS_MeshEdge*> (*eIt) );
9351 if ( !quadAdjacentElems[SMDSAbs_Face].empty() )
9352 for ( eIt = quadAdjacentElems[SMDSAbs_Face].begin();
9353 eIt != quadAdjacentElems[SMDSAbs_Face].end(); ++eIt )
9355 helper.AddTLinks( static_cast< const SMDS_MeshFace*> (*eIt) );
9357 if ( !quadAdjacentElems[SMDSAbs_Volume].empty() )
9358 for ( eIt = quadAdjacentElems[SMDSAbs_Volume].begin();
9359 eIt != quadAdjacentElems[SMDSAbs_Volume].end(); ++eIt )
9361 helper.AddTLinks( static_cast< const SMDS_MeshVolume*> (*eIt) );
9364 // make quadratic (or bi-tri-quadratic) elements instead of linear ones
9366 SMESHDS_Mesh* meshDS = GetMeshDS();
9367 SMESHDS_SubMesh* smDS = 0;
9368 for ( eIt = theElements.begin(); eIt != theElements.end(); ++eIt )
9370 const SMDS_MeshElement* elem = *eIt;
9373 int nbCentralNodes = 0;
9374 switch ( elem->GetEntityType() ) {
9375 // linear convertible
9376 case SMDSEntity_Edge:
9377 case SMDSEntity_Triangle:
9378 case SMDSEntity_Quadrangle:
9379 case SMDSEntity_Tetra:
9380 case SMDSEntity_Pyramid:
9381 case SMDSEntity_Hexa:
9382 case SMDSEntity_Penta: alreadyOK = false; nbCentralNodes = 0; break;
9383 // quadratic that can become bi-quadratic
9384 case SMDSEntity_Quad_Triangle:
9385 case SMDSEntity_Quad_Quadrangle:
9386 case SMDSEntity_Quad_Hexa: alreadyOK =!theToBiQuad; nbCentralNodes = 0; break;
9388 case SMDSEntity_BiQuad_Triangle:
9389 case SMDSEntity_BiQuad_Quadrangle: alreadyOK = theToBiQuad; nbCentralNodes = 1; break;
9390 case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; nbCentralNodes = 7; break;
9392 default: alreadyOK = true;
9394 if ( alreadyOK ) continue;
9396 const SMDSAbs_ElementType type = elem->GetType();
9397 const int id = elem->GetID();
9398 const int nbNodes = elem->NbCornerNodes();
9399 vector<const SMDS_MeshNode *> nodes ( elem->begin_nodes(), elem->end_nodes());
9401 helper.SetSubShape( elem->getshapeId() );
9403 if ( !smDS || !smDS->Contains( elem ))
9404 smDS = meshDS->MeshElements( elem->getshapeId() );
9405 meshDS->RemoveFreeElement(elem, smDS, /*fromGroups=*/false);
9407 SMDS_MeshElement * newElem = 0;
9410 case 4: // cases for most frequently used element types go first (for optimization)
9411 if ( type == SMDSAbs_Volume )
9412 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9414 newElem = helper.AddFace (nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9417 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9418 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9421 newElem = helper.AddFace (nodes[0], nodes[1], nodes[2], id, theForce3d);
9424 newElem = helper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9427 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9428 nodes[4], id, theForce3d);
9431 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9432 nodes[4], nodes[5], id, theForce3d);
9436 ReplaceElemInGroups( elem, newElem, meshDS);
9437 if( newElem && smDS )
9438 smDS->AddElement( newElem );
9440 // remove central nodes
9441 for ( size_t i = nodes.size() - nbCentralNodes; i < nodes.size(); ++i )
9442 if ( nodes[i]->NbInverseElements() == 0 )
9443 meshDS->RemoveFreeNode( nodes[i], smDS, /*fromGroups=*/true );
9445 } // loop on theElements
9448 { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9449 // helper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9450 // helper.FixQuadraticElements( myError );
9451 SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
9455 //=======================================================================
9457 * \brief Convert quadratic elements to linear ones and remove quadratic nodes
9458 * \return int - nb of checked elements
9460 //=======================================================================
9462 int SMESH_MeshEditor::removeQuadElem(SMESHDS_SubMesh * theSm,
9463 SMDS_ElemIteratorPtr theItr,
9464 const int theShapeID)
9467 SMESHDS_Mesh* meshDS = GetMeshDS();
9468 ElemFeatures elemType;
9469 vector<const SMDS_MeshNode *> nodes;
9471 while( theItr->more() )
9473 const SMDS_MeshElement* elem = theItr->next();
9475 if( elem && elem->IsQuadratic())
9478 int nbCornerNodes = elem->NbCornerNodes();
9479 nodes.assign( elem->begin_nodes(), elem->end_nodes() );
9481 elemType.Init( elem, /*basicOnly=*/false ).SetID( elem->GetID() ).SetQuad( false );
9483 //remove a quadratic element
9484 if ( !theSm || !theSm->Contains( elem ))
9485 theSm = meshDS->MeshElements( elem->getshapeId() );
9486 meshDS->RemoveFreeElement( elem, theSm, /*fromGroups=*/false );
9488 // remove medium nodes
9489 for ( size_t i = nbCornerNodes; i < nodes.size(); ++i )
9490 if ( nodes[i]->NbInverseElements() == 0 )
9491 meshDS->RemoveFreeNode( nodes[i], theSm );
9493 // add a linear element
9494 nodes.resize( nbCornerNodes );
9495 SMDS_MeshElement * newElem = AddElement( nodes, elemType );
9496 ReplaceElemInGroups(elem, newElem, meshDS);
9497 if( theSm && newElem )
9498 theSm->AddElement( newElem );
9504 //=======================================================================
9505 //function : ConvertFromQuadratic
9507 //=======================================================================
9509 bool SMESH_MeshEditor::ConvertFromQuadratic()
9511 int nbCheckedElems = 0;
9512 if ( myMesh->HasShapeToMesh() )
9514 if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9516 SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9517 while ( smIt->more() ) {
9518 SMESH_subMesh* sm = smIt->next();
9519 if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() )
9520 nbCheckedElems += removeQuadElem( smDS, smDS->GetElements(), sm->GetId() );
9526 GetMeshDS()->NbEdges() + GetMeshDS()->NbFaces() + GetMeshDS()->NbVolumes();
9527 if ( nbCheckedElems < totalNbElems ) // not all elements are in submeshes
9529 SMESHDS_SubMesh *aSM = 0;
9530 removeQuadElem( aSM, GetMeshDS()->elementsIterator(), 0 );
9538 //================================================================================
9540 * \brief Return true if all medium nodes of the element are in the node set
9542 //================================================================================
9544 bool allMediumNodesIn(const SMDS_MeshElement* elem, TIDSortedNodeSet& nodeSet )
9546 for ( int i = elem->NbCornerNodes(); i < elem->NbNodes(); ++i )
9547 if ( !nodeSet.count( elem->GetNode(i) ))
9553 //================================================================================
9555 * \brief Makes given elements linear
9557 //================================================================================
9559 void SMESH_MeshEditor::ConvertFromQuadratic(TIDSortedElemSet& theElements)
9561 if ( theElements.empty() ) return;
9563 // collect IDs of medium nodes of theElements; some of these nodes will be removed
9564 set<int> mediumNodeIDs;
9565 TIDSortedElemSet::iterator eIt = theElements.begin();
9566 for ( ; eIt != theElements.end(); ++eIt )
9568 const SMDS_MeshElement* e = *eIt;
9569 for ( int i = e->NbCornerNodes(); i < e->NbNodes(); ++i )
9570 mediumNodeIDs.insert( e->GetNode(i)->GetID() );
9573 // replace given elements by linear ones
9574 SMDS_ElemIteratorPtr elemIt = elemSetIterator( theElements );
9575 removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9577 // we need to convert remaining elements whose all medium nodes are in mediumNodeIDs
9578 // except those elements sharing medium nodes of quadratic element whose medium nodes
9579 // are not all in mediumNodeIDs
9581 // get remaining medium nodes
9582 TIDSortedNodeSet mediumNodes;
9583 set<int>::iterator nIdsIt = mediumNodeIDs.begin();
9584 for ( ; nIdsIt != mediumNodeIDs.end(); ++nIdsIt )
9585 if ( const SMDS_MeshNode* n = GetMeshDS()->FindNode( *nIdsIt ))
9586 mediumNodes.insert( mediumNodes.end(), n );
9588 // find more quadratic elements to convert
9589 TIDSortedElemSet moreElemsToConvert;
9590 TIDSortedNodeSet::iterator nIt = mediumNodes.begin();
9591 for ( ; nIt != mediumNodes.end(); ++nIt )
9593 SMDS_ElemIteratorPtr invIt = (*nIt)->GetInverseElementIterator();
9594 while ( invIt->more() )
9596 const SMDS_MeshElement* e = invIt->next();
9597 if ( e->IsQuadratic() && allMediumNodesIn( e, mediumNodes ))
9599 // find a more complex element including e and
9600 // whose medium nodes are not in mediumNodes
9601 bool complexFound = false;
9602 for ( int type = e->GetType() + 1; type < SMDSAbs_0DElement; ++type )
9604 SMDS_ElemIteratorPtr invIt2 =
9605 (*nIt)->GetInverseElementIterator( SMDSAbs_ElementType( type ));
9606 while ( invIt2->more() )
9608 const SMDS_MeshElement* eComplex = invIt2->next();
9609 if ( eComplex->IsQuadratic() && !allMediumNodesIn( eComplex, mediumNodes))
9611 int nbCommonNodes = SMESH_MeshAlgos::GetCommonNodes( e, eComplex ).size();
9612 if ( nbCommonNodes == e->NbNodes())
9614 complexFound = true;
9615 type = SMDSAbs_NbElementTypes; // to quit from the outer loop
9621 if ( !complexFound )
9622 moreElemsToConvert.insert( e );
9626 elemIt = elemSetIterator( moreElemsToConvert );
9627 removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9630 //=======================================================================
9631 //function : SewSideElements
9633 //=======================================================================
9635 SMESH_MeshEditor::Sew_Error
9636 SMESH_MeshEditor::SewSideElements (TIDSortedElemSet& theSide1,
9637 TIDSortedElemSet& theSide2,
9638 const SMDS_MeshNode* theFirstNode1,
9639 const SMDS_MeshNode* theFirstNode2,
9640 const SMDS_MeshNode* theSecondNode1,
9641 const SMDS_MeshNode* theSecondNode2)
9643 myLastCreatedElems.Clear();
9644 myLastCreatedNodes.Clear();
9646 MESSAGE ("::::SewSideElements()");
9647 if ( theSide1.size() != theSide2.size() )
9648 return SEW_DIFF_NB_OF_ELEMENTS;
9650 Sew_Error aResult = SEW_OK;
9652 // 1. Build set of faces representing each side
9653 // 2. Find which nodes of the side 1 to merge with ones on the side 2
9654 // 3. Replace nodes in elements of the side 1 and remove replaced nodes
9656 // =======================================================================
9657 // 1. Build set of faces representing each side:
9658 // =======================================================================
9659 // a. build set of nodes belonging to faces
9660 // b. complete set of faces: find missing faces whose nodes are in set of nodes
9661 // c. create temporary faces representing side of volumes if correspondent
9662 // face does not exist
9664 SMESHDS_Mesh* aMesh = GetMeshDS();
9665 // TODO algoritm not OK with vtkUnstructuredGrid: 2 meshes can't share nodes
9666 //SMDS_Mesh aTmpFacesMesh; // try to use the same mesh
9667 TIDSortedElemSet faceSet1, faceSet2;
9668 set<const SMDS_MeshElement*> volSet1, volSet2;
9669 set<const SMDS_MeshNode*> nodeSet1, nodeSet2;
9670 TIDSortedElemSet * faceSetPtr[] = { &faceSet1, &faceSet2 };
9671 set<const SMDS_MeshElement*> * volSetPtr[] = { &volSet1, &volSet2 };
9672 set<const SMDS_MeshNode*> * nodeSetPtr[] = { &nodeSet1, &nodeSet2 };
9673 TIDSortedElemSet * elemSetPtr[] = { &theSide1, &theSide2 };
9674 int iSide, iFace, iNode;
9676 list<const SMDS_MeshElement* > tempFaceList;
9677 for ( iSide = 0; iSide < 2; iSide++ ) {
9678 set<const SMDS_MeshNode*> * nodeSet = nodeSetPtr[ iSide ];
9679 TIDSortedElemSet * elemSet = elemSetPtr[ iSide ];
9680 TIDSortedElemSet * faceSet = faceSetPtr[ iSide ];
9681 set<const SMDS_MeshElement*> * volSet = volSetPtr [ iSide ];
9682 set<const SMDS_MeshElement*>::iterator vIt;
9683 TIDSortedElemSet::iterator eIt;
9684 set<const SMDS_MeshNode*>::iterator nIt;
9686 // check that given nodes belong to given elements
9687 const SMDS_MeshNode* n1 = ( iSide == 0 ) ? theFirstNode1 : theFirstNode2;
9688 const SMDS_MeshNode* n2 = ( iSide == 0 ) ? theSecondNode1 : theSecondNode2;
9689 int firstIndex = -1, secondIndex = -1;
9690 for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9691 const SMDS_MeshElement* elem = *eIt;
9692 if ( firstIndex < 0 ) firstIndex = elem->GetNodeIndex( n1 );
9693 if ( secondIndex < 0 ) secondIndex = elem->GetNodeIndex( n2 );
9694 if ( firstIndex > -1 && secondIndex > -1 ) break;
9696 if ( firstIndex < 0 || secondIndex < 0 ) {
9697 // we can simply return until temporary faces created
9698 return (iSide == 0 ) ? SEW_BAD_SIDE1_NODES : SEW_BAD_SIDE2_NODES;
9701 // -----------------------------------------------------------
9702 // 1a. Collect nodes of existing faces
9703 // and build set of face nodes in order to detect missing
9704 // faces corresponding to sides of volumes
9705 // -----------------------------------------------------------
9707 set< set <const SMDS_MeshNode*> > setOfFaceNodeSet;
9709 // loop on the given element of a side
9710 for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9711 //const SMDS_MeshElement* elem = *eIt;
9712 const SMDS_MeshElement* elem = *eIt;
9713 if ( elem->GetType() == SMDSAbs_Face ) {
9714 faceSet->insert( elem );
9715 set <const SMDS_MeshNode*> faceNodeSet;
9716 SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
9717 while ( nodeIt->more() ) {
9718 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9719 nodeSet->insert( n );
9720 faceNodeSet.insert( n );
9722 setOfFaceNodeSet.insert( faceNodeSet );
9724 else if ( elem->GetType() == SMDSAbs_Volume )
9725 volSet->insert( elem );
9727 // ------------------------------------------------------------------------------
9728 // 1b. Complete set of faces: find missing faces whose nodes are in set of nodes
9729 // ------------------------------------------------------------------------------
9731 for ( nIt = nodeSet->begin(); nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
9732 SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
9733 while ( fIt->more() ) { // loop on faces sharing a node
9734 const SMDS_MeshElement* f = fIt->next();
9735 if ( faceSet->find( f ) == faceSet->end() ) {
9736 // check if all nodes are in nodeSet and
9737 // complete setOfFaceNodeSet if they are
9738 set <const SMDS_MeshNode*> faceNodeSet;
9739 SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
9740 bool allInSet = true;
9741 while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
9742 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9743 if ( nodeSet->find( n ) == nodeSet->end() )
9746 faceNodeSet.insert( n );
9749 faceSet->insert( f );
9750 setOfFaceNodeSet.insert( faceNodeSet );
9756 // -------------------------------------------------------------------------
9757 // 1c. Create temporary faces representing sides of volumes if correspondent
9758 // face does not exist
9759 // -------------------------------------------------------------------------
9761 if ( !volSet->empty() ) {
9762 //int nodeSetSize = nodeSet->size();
9764 // loop on given volumes
9765 for ( vIt = volSet->begin(); vIt != volSet->end(); vIt++ ) {
9766 SMDS_VolumeTool vol (*vIt);
9767 // loop on volume faces: find free faces
9768 // --------------------------------------
9769 list<const SMDS_MeshElement* > freeFaceList;
9770 for ( iFace = 0; iFace < vol.NbFaces(); iFace++ ) {
9771 if ( !vol.IsFreeFace( iFace ))
9773 // check if there is already a face with same nodes in a face set
9774 const SMDS_MeshElement* aFreeFace = 0;
9775 const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iFace );
9776 int nbNodes = vol.NbFaceNodes( iFace );
9777 set <const SMDS_MeshNode*> faceNodeSet;
9778 vol.GetFaceNodes( iFace, faceNodeSet );
9779 bool isNewFace = setOfFaceNodeSet.insert( faceNodeSet ).second;
9781 // no such a face is given but it still can exist, check it
9782 vector<const SMDS_MeshNode *> nodes ( fNodes, fNodes + nbNodes);
9783 aFreeFace = aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false );
9786 // create a temporary face
9787 if ( nbNodes == 3 ) {
9788 //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2] );
9789 aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2] );
9791 else if ( nbNodes == 4 ) {
9792 //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
9793 aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
9796 vector<const SMDS_MeshNode *> poly_nodes ( fNodes, & fNodes[nbNodes]);
9797 //aFreeFace = aTmpFacesMesh.AddPolygonalFace(poly_nodes);
9798 aFreeFace = aMesh->AddPolygonalFace(poly_nodes);
9801 tempFaceList.push_back( aFreeFace );
9805 freeFaceList.push_back( aFreeFace );
9807 } // loop on faces of a volume
9809 // choose one of several free faces of a volume
9810 // --------------------------------------------
9811 if ( freeFaceList.size() > 1 ) {
9812 // choose a face having max nb of nodes shared by other elems of a side
9813 int maxNbNodes = -1;
9814 list<const SMDS_MeshElement* >::iterator fIt = freeFaceList.begin();
9815 while ( fIt != freeFaceList.end() ) { // loop on free faces
9816 int nbSharedNodes = 0;
9817 SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
9818 while ( nodeIt->more() ) { // loop on free face nodes
9819 const SMDS_MeshNode* n =
9820 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9821 SMDS_ElemIteratorPtr invElemIt = n->GetInverseElementIterator();
9822 while ( invElemIt->more() ) {
9823 const SMDS_MeshElement* e = invElemIt->next();
9824 nbSharedNodes += faceSet->count( e );
9825 nbSharedNodes += elemSet->count( e );
9828 if ( nbSharedNodes > maxNbNodes ) {
9829 maxNbNodes = nbSharedNodes;
9830 freeFaceList.erase( freeFaceList.begin(), fIt++ );
9832 else if ( nbSharedNodes == maxNbNodes ) {
9836 freeFaceList.erase( fIt++ ); // here fIt++ occurs before erase
9839 if ( freeFaceList.size() > 1 )
9841 // could not choose one face, use another way
9842 // choose a face most close to the bary center of the opposite side
9843 gp_XYZ aBC( 0., 0., 0. );
9844 set <const SMDS_MeshNode*> addedNodes;
9845 TIDSortedElemSet * elemSet2 = elemSetPtr[ 1 - iSide ];
9846 eIt = elemSet2->begin();
9847 for ( eIt = elemSet2->begin(); eIt != elemSet2->end(); eIt++ ) {
9848 SMDS_ElemIteratorPtr nodeIt = (*eIt)->nodesIterator();
9849 while ( nodeIt->more() ) { // loop on free face nodes
9850 const SMDS_MeshNode* n =
9851 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9852 if ( addedNodes.insert( n ).second )
9853 aBC += gp_XYZ( n->X(),n->Y(),n->Z() );
9856 aBC /= addedNodes.size();
9857 double minDist = DBL_MAX;
9858 fIt = freeFaceList.begin();
9859 while ( fIt != freeFaceList.end() ) { // loop on free faces
9861 SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
9862 while ( nodeIt->more() ) { // loop on free face nodes
9863 const SMDS_MeshNode* n =
9864 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9865 gp_XYZ p( n->X(),n->Y(),n->Z() );
9866 dist += ( aBC - p ).SquareModulus();
9868 if ( dist < minDist ) {
9870 freeFaceList.erase( freeFaceList.begin(), fIt++ );
9873 fIt = freeFaceList.erase( fIt++ );
9876 } // choose one of several free faces of a volume
9878 if ( freeFaceList.size() == 1 ) {
9879 const SMDS_MeshElement* aFreeFace = freeFaceList.front();
9880 faceSet->insert( aFreeFace );
9881 // complete a node set with nodes of a found free face
9882 // for ( iNode = 0; iNode < ; iNode++ )
9883 // nodeSet->insert( fNodes[ iNode ] );
9886 } // loop on volumes of a side
9888 // // complete a set of faces if new nodes in a nodeSet appeared
9889 // // ----------------------------------------------------------
9890 // if ( nodeSetSize != nodeSet->size() ) {
9891 // for ( ; nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
9892 // SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
9893 // while ( fIt->more() ) { // loop on faces sharing a node
9894 // const SMDS_MeshElement* f = fIt->next();
9895 // if ( faceSet->find( f ) == faceSet->end() ) {
9896 // // check if all nodes are in nodeSet and
9897 // // complete setOfFaceNodeSet if they are
9898 // set <const SMDS_MeshNode*> faceNodeSet;
9899 // SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
9900 // bool allInSet = true;
9901 // while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
9902 // const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9903 // if ( nodeSet->find( n ) == nodeSet->end() )
9904 // allInSet = false;
9906 // faceNodeSet.insert( n );
9908 // if ( allInSet ) {
9909 // faceSet->insert( f );
9910 // setOfFaceNodeSet.insert( faceNodeSet );
9916 } // Create temporary faces, if there are volumes given
9919 if ( faceSet1.size() != faceSet2.size() ) {
9920 // delete temporary faces: they are in reverseElements of actual nodes
9921 // SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
9922 // while ( tmpFaceIt->more() )
9923 // aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
9924 // list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
9925 // for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
9926 // aMesh->RemoveElement(*tmpFaceIt);
9927 MESSAGE("Diff nb of faces");
9928 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9931 // ============================================================
9932 // 2. Find nodes to merge:
9933 // bind a node to remove to a node to put instead
9934 // ============================================================
9936 TNodeNodeMap nReplaceMap; // bind a node to remove to a node to put instead
9937 if ( theFirstNode1 != theFirstNode2 )
9938 nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
9939 if ( theSecondNode1 != theSecondNode2 )
9940 nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
9942 LinkID_Gen aLinkID_Gen( GetMeshDS() );
9943 set< long > linkIdSet; // links to process
9944 linkIdSet.insert( aLinkID_Gen.GetLinkID( theFirstNode1, theSecondNode1 ));
9946 typedef pair< const SMDS_MeshNode*, const SMDS_MeshNode* > NLink;
9947 list< NLink > linkList[2];
9948 linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
9949 linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
9950 // loop on links in linkList; find faces by links and append links
9951 // of the found faces to linkList
9952 list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
9953 for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ )
9955 NLink link[] = { *linkIt[0], *linkIt[1] };
9956 long linkID = aLinkID_Gen.GetLinkID( link[0].first, link[0].second );
9957 if ( !linkIdSet.count( linkID ) )
9960 // by links, find faces in the face sets,
9961 // and find indices of link nodes in the found faces;
9962 // in a face set, there is only one or no face sharing a link
9963 // ---------------------------------------------------------------
9965 const SMDS_MeshElement* face[] = { 0, 0 };
9966 vector<const SMDS_MeshNode*> fnodes[2];
9967 int iLinkNode[2][2];
9968 TIDSortedElemSet avoidSet;
9969 for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
9970 const SMDS_MeshNode* n1 = link[iSide].first;
9971 const SMDS_MeshNode* n2 = link[iSide].second;
9972 //cout << "Side " << iSide << " ";
9973 //cout << "L( " << n1->GetID() << ", " << n2->GetID() << " ) " << endl;
9974 // find a face by two link nodes
9975 face[ iSide ] = SMESH_MeshAlgos::FindFaceInSet( n1, n2,
9976 *faceSetPtr[ iSide ], avoidSet,
9977 &iLinkNode[iSide][0],
9978 &iLinkNode[iSide][1] );
9981 //cout << " F " << face[ iSide]->GetID() <<endl;
9982 faceSetPtr[ iSide ]->erase( face[ iSide ]);
9983 // put face nodes to fnodes
9984 if ( face[ iSide ]->IsQuadratic() )
9986 // use interlaced nodes iterator
9987 const SMDS_VtkFace* F = dynamic_cast<const SMDS_VtkFace*>( face[ iSide ]);
9988 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
9989 SMDS_ElemIteratorPtr nIter = F->interlacedNodesElemIterator();
9990 while ( nIter->more() )
9991 fnodes[ iSide ].push_back( cast2Node( nIter->next() ));
9995 fnodes[ iSide ].assign( face[ iSide ]->begin_nodes(),
9996 face[ iSide ]->end_nodes() );
9998 fnodes[ iSide ].push_back( fnodes[ iSide ].front());
10002 // check similarity of elements of the sides
10003 if (aResult == SEW_OK && (( face[0] && !face[1] ) || ( !face[0] && face[1] ))) {
10004 MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
10005 if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
10006 aResult = ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10009 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10011 break; // do not return because it's necessary to remove tmp faces
10014 // set nodes to merge
10015 // -------------------
10017 if ( face[0] && face[1] ) {
10018 const int nbNodes = face[0]->NbNodes();
10019 if ( nbNodes != face[1]->NbNodes() ) {
10020 MESSAGE("Diff nb of face nodes");
10021 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10022 break; // do not return because it s necessary to remove tmp faces
10024 bool reverse[] = { false, false }; // order of nodes in the link
10025 for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
10026 // analyse link orientation in faces
10027 int i1 = iLinkNode[ iSide ][ 0 ];
10028 int i2 = iLinkNode[ iSide ][ 1 ];
10029 reverse[ iSide ] = Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1;
10031 int di1 = reverse[0] ? -1 : +1, i1 = iLinkNode[0][1] + di1;
10032 int di2 = reverse[1] ? -1 : +1, i2 = iLinkNode[1][1] + di2;
10033 for ( int i = nbNodes - 2; i > 0; --i, i1 += di1, i2 += di2 )
10035 nReplaceMap.insert ( make_pair ( fnodes[0][ ( i1 + nbNodes ) % nbNodes ],
10036 fnodes[1][ ( i2 + nbNodes ) % nbNodes ]));
10039 // add other links of the faces to linkList
10040 // -----------------------------------------
10042 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
10043 linkID = aLinkID_Gen.GetLinkID( fnodes[0][iNode], fnodes[0][iNode+1] );
10044 pair< set<long>::iterator, bool > iter_isnew = linkIdSet.insert( linkID );
10045 if ( !iter_isnew.second ) { // already in a set: no need to process
10046 linkIdSet.erase( iter_isnew.first );
10048 else // new in set == encountered for the first time: add
10050 const SMDS_MeshNode* n1 = fnodes[0][ iNode ];
10051 const SMDS_MeshNode* n2 = fnodes[0][ iNode + 1];
10052 linkList[0].push_back ( NLink( n1, n2 ));
10053 linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10058 if ( faceSetPtr[0]->empty() || faceSetPtr[1]->empty() )
10061 } // loop on link lists
10063 if ( aResult == SEW_OK &&
10064 ( //linkIt[0] != linkList[0].end() ||
10065 !faceSetPtr[0]->empty() || !faceSetPtr[1]->empty() )) {
10066 MESSAGE( (linkIt[0] != linkList[0].end()) <<" "<< (faceSetPtr[0]->empty()) <<
10067 " " << (faceSetPtr[1]->empty()));
10068 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10071 // ====================================================================
10072 // 3. Replace nodes in elements of the side 1 and remove replaced nodes
10073 // ====================================================================
10075 // delete temporary faces
10076 // SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
10077 // while ( tmpFaceIt->more() )
10078 // aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
10079 list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
10080 for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
10081 aMesh->RemoveElement(*tmpFaceIt);
10083 if ( aResult != SEW_OK)
10086 list< int > nodeIDsToRemove;
10087 vector< const SMDS_MeshNode*> nodes;
10088 ElemFeatures elemType;
10090 // loop on nodes replacement map
10091 TNodeNodeMap::iterator nReplaceMapIt = nReplaceMap.begin(), nnIt;
10092 for ( ; nReplaceMapIt != nReplaceMap.end(); nReplaceMapIt++ )
10093 if ( (*nReplaceMapIt).first != (*nReplaceMapIt).second )
10095 const SMDS_MeshNode* nToRemove = (*nReplaceMapIt).first;
10096 nodeIDsToRemove.push_back( nToRemove->GetID() );
10097 // loop on elements sharing nToRemove
10098 SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
10099 while ( invElemIt->more() ) {
10100 const SMDS_MeshElement* e = invElemIt->next();
10101 // get a new suite of nodes: make replacement
10102 int nbReplaced = 0, i = 0, nbNodes = e->NbNodes();
10103 nodes.resize( nbNodes );
10104 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
10105 while ( nIt->more() ) {
10106 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nIt->next() );
10107 nnIt = nReplaceMap.find( n );
10108 if ( nnIt != nReplaceMap.end() ) {
10110 n = (*nnIt).second;
10114 // if ( nbReplaced == nbNodes && e->GetType() == SMDSAbs_Face )
10115 // elemIDsToRemove.push_back( e->GetID() );
10119 elemType.Init( e, /*basicOnly=*/false ).SetID( e->GetID() );
10120 aMesh->RemoveElement( e );
10122 if ( SMDS_MeshElement* newElem = this->AddElement( nodes, elemType ))
10124 AddToSameGroups( newElem, e, aMesh );
10125 if ( int aShapeId = e->getshapeId() )
10126 aMesh->SetMeshElementOnShape( newElem, aShapeId );
10132 Remove( nodeIDsToRemove, true );
10137 //================================================================================
10139 * \brief Find corresponding nodes in two sets of faces
10140 * \param theSide1 - first face set
10141 * \param theSide2 - second first face
10142 * \param theFirstNode1 - a boundary node of set 1
10143 * \param theFirstNode2 - a node of set 2 corresponding to theFirstNode1
10144 * \param theSecondNode1 - a boundary node of set 1 linked with theFirstNode1
10145 * \param theSecondNode2 - a node of set 2 corresponding to theSecondNode1
10146 * \param nReplaceMap - output map of corresponding nodes
10147 * \return bool - is a success or not
10149 //================================================================================
10152 //#define DEBUG_MATCHING_NODES
10155 SMESH_MeshEditor::Sew_Error
10156 SMESH_MeshEditor::FindMatchingNodes(set<const SMDS_MeshElement*>& theSide1,
10157 set<const SMDS_MeshElement*>& theSide2,
10158 const SMDS_MeshNode* theFirstNode1,
10159 const SMDS_MeshNode* theFirstNode2,
10160 const SMDS_MeshNode* theSecondNode1,
10161 const SMDS_MeshNode* theSecondNode2,
10162 TNodeNodeMap & nReplaceMap)
10164 set<const SMDS_MeshElement*> * faceSetPtr[] = { &theSide1, &theSide2 };
10166 nReplaceMap.clear();
10167 if ( theFirstNode1 != theFirstNode2 )
10168 nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
10169 if ( theSecondNode1 != theSecondNode2 )
10170 nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
10172 set< SMESH_TLink > linkSet; // set of nodes where order of nodes is ignored
10173 linkSet.insert( SMESH_TLink( theFirstNode1, theSecondNode1 ));
10175 list< NLink > linkList[2];
10176 linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
10177 linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
10179 // loop on links in linkList; find faces by links and append links
10180 // of the found faces to linkList
10181 list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
10182 for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ ) {
10183 NLink link[] = { *linkIt[0], *linkIt[1] };
10184 if ( linkSet.find( link[0] ) == linkSet.end() )
10187 // by links, find faces in the face sets,
10188 // and find indices of link nodes in the found faces;
10189 // in a face set, there is only one or no face sharing a link
10190 // ---------------------------------------------------------------
10192 const SMDS_MeshElement* face[] = { 0, 0 };
10193 list<const SMDS_MeshNode*> notLinkNodes[2];
10194 //bool reverse[] = { false, false }; // order of notLinkNodes
10196 for ( int iSide = 0; iSide < 2; iSide++ ) // loop on 2 sides
10198 const SMDS_MeshNode* n1 = link[iSide].first;
10199 const SMDS_MeshNode* n2 = link[iSide].second;
10200 set<const SMDS_MeshElement*> * faceSet = faceSetPtr[ iSide ];
10201 set< const SMDS_MeshElement* > facesOfNode1;
10202 for ( int iNode = 0; iNode < 2; iNode++ ) // loop on 2 nodes of a link
10204 // during a loop of the first node, we find all faces around n1,
10205 // during a loop of the second node, we find one face sharing both n1 and n2
10206 const SMDS_MeshNode* n = iNode ? n1 : n2; // a node of a link
10207 SMDS_ElemIteratorPtr fIt = n->GetInverseElementIterator(SMDSAbs_Face);
10208 while ( fIt->more() ) { // loop on faces sharing a node
10209 const SMDS_MeshElement* f = fIt->next();
10210 if (faceSet->find( f ) != faceSet->end() && // f is in face set
10211 ! facesOfNode1.insert( f ).second ) // f encounters twice
10213 if ( face[ iSide ] ) {
10214 MESSAGE( "2 faces per link " );
10215 return ( iSide ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10218 faceSet->erase( f );
10220 // get not link nodes
10221 int nbN = f->NbNodes();
10222 if ( f->IsQuadratic() )
10224 nbNodes[ iSide ] = nbN;
10225 list< const SMDS_MeshNode* > & nodes = notLinkNodes[ iSide ];
10226 int i1 = f->GetNodeIndex( n1 );
10227 int i2 = f->GetNodeIndex( n2 );
10228 int iEnd = nbN, iBeg = -1, iDelta = 1;
10229 bool reverse = ( Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1 );
10231 std::swap( iEnd, iBeg ); iDelta = -1;
10236 if ( i == iEnd ) i = iBeg + iDelta;
10237 if ( i == i1 ) break;
10238 nodes.push_back ( f->GetNode( i ) );
10244 // check similarity of elements of the sides
10245 if (( face[0] && !face[1] ) || ( !face[0] && face[1] )) {
10246 MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
10247 if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
10248 return ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
10251 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10255 // set nodes to merge
10256 // -------------------
10258 if ( face[0] && face[1] ) {
10259 if ( nbNodes[0] != nbNodes[1] ) {
10260 MESSAGE("Diff nb of face nodes");
10261 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
10263 #ifdef DEBUG_MATCHING_NODES
10264 MESSAGE ( " Link 1: " << link[0].first->GetID() <<" "<< link[0].second->GetID()
10265 << " F 1: " << face[0] << "| Link 2: " << link[1].first->GetID() <<" "
10266 << link[1].second->GetID() << " F 2: " << face[1] << " | Bind: " ) ;
10268 int nbN = nbNodes[0];
10270 list<const SMDS_MeshNode*>::iterator n1 = notLinkNodes[0].begin();
10271 list<const SMDS_MeshNode*>::iterator n2 = notLinkNodes[1].begin();
10272 for ( int i = 0 ; i < nbN - 2; ++i ) {
10273 #ifdef DEBUG_MATCHING_NODES
10274 MESSAGE ( (*n1)->GetID() << " to " << (*n2)->GetID() );
10276 nReplaceMap.insert( make_pair( *(n1++), *(n2++) ));
10280 // add other links of the face 1 to linkList
10281 // -----------------------------------------
10283 const SMDS_MeshElement* f0 = face[0];
10284 const SMDS_MeshNode* n1 = f0->GetNode( nbN - 1 );
10285 for ( int i = 0; i < nbN; i++ )
10287 const SMDS_MeshNode* n2 = f0->GetNode( i );
10288 pair< set< SMESH_TLink >::iterator, bool > iter_isnew =
10289 linkSet.insert( SMESH_TLink( n1, n2 ));
10290 if ( !iter_isnew.second ) { // already in a set: no need to process
10291 linkSet.erase( iter_isnew.first );
10293 else // new in set == encountered for the first time: add
10295 #ifdef DEBUG_MATCHING_NODES
10296 MESSAGE ( "Add link 1: " << n1->GetID() << " " << n2->GetID() << " "
10297 << " | link 2: " << nReplaceMap[n1]->GetID() << " " << nReplaceMap[n2]->GetID() << " " );
10299 linkList[0].push_back ( NLink( n1, n2 ));
10300 linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
10305 } // loop on link lists
10310 //================================================================================
10312 * \brief Create elements equal (on same nodes) to given ones
10313 * \param [in] theElements - a set of elems to duplicate. If it is empty, all
10314 * elements of the uppest dimension are duplicated.
10316 //================================================================================
10318 void SMESH_MeshEditor::DoubleElements( const TIDSortedElemSet& theElements )
10320 ClearLastCreated();
10321 SMESHDS_Mesh* mesh = GetMeshDS();
10323 // get an element type and an iterator over elements
10325 SMDSAbs_ElementType type;
10326 SMDS_ElemIteratorPtr elemIt;
10327 vector< const SMDS_MeshElement* > allElems;
10328 if ( theElements.empty() )
10330 if ( mesh->NbNodes() == 0 )
10332 // get most complex type
10333 SMDSAbs_ElementType types[SMDSAbs_NbElementTypes] = {
10334 SMDSAbs_Volume, SMDSAbs_Face, SMDSAbs_Edge,
10335 SMDSAbs_0DElement, SMDSAbs_Ball, SMDSAbs_Node
10337 for ( int i = 0; i < SMDSAbs_NbElementTypes; ++i )
10338 if ( mesh->GetMeshInfo().NbElements( types[i] ))
10343 // put all elements in the vector <allElems>
10344 allElems.reserve( mesh->GetMeshInfo().NbElements( type ));
10345 elemIt = mesh->elementsIterator( type );
10346 while ( elemIt->more() )
10347 allElems.push_back( elemIt->next());
10348 elemIt = elemSetIterator( allElems );
10352 type = (*theElements.begin())->GetType();
10353 elemIt = elemSetIterator( theElements );
10356 // duplicate elements
10358 ElemFeatures elemType;
10360 vector< const SMDS_MeshNode* > nodes;
10361 while ( elemIt->more() )
10363 const SMDS_MeshElement* elem = elemIt->next();
10364 if ( elem->GetType() != type )
10367 elemType.Init( elem, /*basicOnly=*/false );
10368 nodes.assign( elem->begin_nodes(), elem->end_nodes() );
10370 AddElement( nodes, elemType );
10374 //================================================================================
10376 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10377 \param theElems - the list of elements (edges or faces) to be replicated
10378 The nodes for duplication could be found from these elements
10379 \param theNodesNot - list of nodes to NOT replicate
10380 \param theAffectedElems - the list of elements (cells and edges) to which the
10381 replicated nodes should be associated to.
10382 \return TRUE if operation has been completed successfully, FALSE otherwise
10384 //================================================================================
10386 bool SMESH_MeshEditor::DoubleNodes( const TIDSortedElemSet& theElems,
10387 const TIDSortedElemSet& theNodesNot,
10388 const TIDSortedElemSet& theAffectedElems )
10390 myLastCreatedElems.Clear();
10391 myLastCreatedNodes.Clear();
10393 if ( theElems.size() == 0 )
10396 SMESHDS_Mesh* aMeshDS = GetMeshDS();
10401 TNodeNodeMap anOldNodeToNewNode;
10402 // duplicate elements and nodes
10403 res = doubleNodes( aMeshDS, theElems, theNodesNot, anOldNodeToNewNode, true );
10404 // replce nodes by duplications
10405 res = doubleNodes( aMeshDS, theAffectedElems, theNodesNot, anOldNodeToNewNode, false );
10409 //================================================================================
10411 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10412 \param theMeshDS - mesh instance
10413 \param theElems - the elements replicated or modified (nodes should be changed)
10414 \param theNodesNot - nodes to NOT replicate
10415 \param theNodeNodeMap - relation of old node to new created node
10416 \param theIsDoubleElem - flag os to replicate element or modify
10417 \return TRUE if operation has been completed successfully, FALSE otherwise
10419 //================================================================================
10421 bool SMESH_MeshEditor::doubleNodes(SMESHDS_Mesh* theMeshDS,
10422 const TIDSortedElemSet& theElems,
10423 const TIDSortedElemSet& theNodesNot,
10424 TNodeNodeMap& theNodeNodeMap,
10425 const bool theIsDoubleElem )
10427 MESSAGE("doubleNodes");
10428 // iterate through element and duplicate them (by nodes duplication)
10430 std::vector<const SMDS_MeshNode*> newNodes;
10431 ElemFeatures elemType;
10433 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10434 for ( ; elemItr != theElems.end(); ++elemItr )
10436 const SMDS_MeshElement* anElem = *elemItr;
10440 // duplicate nodes to duplicate element
10441 bool isDuplicate = false;
10442 newNodes.resize( anElem->NbNodes() );
10443 SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
10445 while ( anIter->more() )
10447 const SMDS_MeshNode* aCurrNode = static_cast<const SMDS_MeshNode*>( anIter->next() );
10448 const SMDS_MeshNode* aNewNode = aCurrNode;
10449 TNodeNodeMap::iterator n2n = theNodeNodeMap.find( aCurrNode );
10450 if ( n2n != theNodeNodeMap.end() )
10452 aNewNode = n2n->second;
10454 else if ( theIsDoubleElem && !theNodesNot.count( aCurrNode ))
10457 aNewNode = theMeshDS->AddNode( aCurrNode->X(), aCurrNode->Y(), aCurrNode->Z() );
10458 copyPosition( aCurrNode, aNewNode );
10459 theNodeNodeMap[ aCurrNode ] = aNewNode;
10460 myLastCreatedNodes.Append( aNewNode );
10462 isDuplicate |= (aCurrNode != aNewNode);
10463 newNodes[ ind++ ] = aNewNode;
10465 if ( !isDuplicate )
10468 if ( theIsDoubleElem )
10469 AddElement( newNodes, elemType.Init( anElem, /*basicOnly=*/false ));
10471 theMeshDS->ChangeElementNodes( anElem, &newNodes[ 0 ], newNodes.size() );
10478 //================================================================================
10480 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10481 \param theNodes - identifiers of nodes to be doubled
10482 \param theModifiedElems - identifiers of elements to be updated by the new (doubled)
10483 nodes. If list of element identifiers is empty then nodes are doubled but
10484 they not assigned to elements
10485 \return TRUE if operation has been completed successfully, FALSE otherwise
10487 //================================================================================
10489 bool SMESH_MeshEditor::DoubleNodes( const std::list< int >& theListOfNodes,
10490 const std::list< int >& theListOfModifiedElems )
10492 MESSAGE("DoubleNodes");
10493 myLastCreatedElems.Clear();
10494 myLastCreatedNodes.Clear();
10496 if ( theListOfNodes.size() == 0 )
10499 SMESHDS_Mesh* aMeshDS = GetMeshDS();
10503 // iterate through nodes and duplicate them
10505 std::map< const SMDS_MeshNode*, const SMDS_MeshNode* > anOldNodeToNewNode;
10507 std::list< int >::const_iterator aNodeIter;
10508 for ( aNodeIter = theListOfNodes.begin(); aNodeIter != theListOfNodes.end(); ++aNodeIter )
10510 int aCurr = *aNodeIter;
10511 SMDS_MeshNode* aNode = (SMDS_MeshNode*)aMeshDS->FindNode( aCurr );
10517 const SMDS_MeshNode* aNewNode = aMeshDS->AddNode( aNode->X(), aNode->Y(), aNode->Z() );
10520 copyPosition( aNode, aNewNode );
10521 anOldNodeToNewNode[ aNode ] = aNewNode;
10522 myLastCreatedNodes.Append( aNewNode );
10526 // Create map of new nodes for modified elements
10528 std::map< SMDS_MeshElement*, vector<const SMDS_MeshNode*> > anElemToNodes;
10530 std::list< int >::const_iterator anElemIter;
10531 for ( anElemIter = theListOfModifiedElems.begin();
10532 anElemIter != theListOfModifiedElems.end(); ++anElemIter )
10534 int aCurr = *anElemIter;
10535 SMDS_MeshElement* anElem = (SMDS_MeshElement*)aMeshDS->FindElement( aCurr );
10539 vector<const SMDS_MeshNode*> aNodeArr( anElem->NbNodes() );
10541 SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
10543 while ( anIter->more() )
10545 SMDS_MeshNode* aCurrNode = (SMDS_MeshNode*)anIter->next();
10546 if ( aCurr && anOldNodeToNewNode.find( aCurrNode ) != anOldNodeToNewNode.end() )
10548 const SMDS_MeshNode* aNewNode = anOldNodeToNewNode[ aCurrNode ];
10549 aNodeArr[ ind++ ] = aNewNode;
10552 aNodeArr[ ind++ ] = aCurrNode;
10554 anElemToNodes[ anElem ] = aNodeArr;
10557 // Change nodes of elements
10559 std::map< SMDS_MeshElement*, vector<const SMDS_MeshNode*> >::iterator
10560 anElemToNodesIter = anElemToNodes.begin();
10561 for ( ; anElemToNodesIter != anElemToNodes.end(); ++anElemToNodesIter )
10563 const SMDS_MeshElement* anElem = anElemToNodesIter->first;
10564 vector<const SMDS_MeshNode*> aNodeArr = anElemToNodesIter->second;
10567 MESSAGE("ChangeElementNodes");
10568 aMeshDS->ChangeElementNodes( anElem, &aNodeArr[ 0 ], anElem->NbNodes() );
10577 //================================================================================
10579 \brief Check if element located inside shape
10580 \return TRUE if IN or ON shape, FALSE otherwise
10582 //================================================================================
10584 template<class Classifier>
10585 bool isInside(const SMDS_MeshElement* theElem,
10586 Classifier& theClassifier,
10587 const double theTol)
10589 gp_XYZ centerXYZ (0, 0, 0);
10590 SMDS_ElemIteratorPtr aNodeItr = theElem->nodesIterator();
10591 while (aNodeItr->more())
10592 centerXYZ += SMESH_TNodeXYZ(cast2Node( aNodeItr->next()));
10594 gp_Pnt aPnt = centerXYZ / theElem->NbNodes();
10595 theClassifier.Perform(aPnt, theTol);
10596 TopAbs_State aState = theClassifier.State();
10597 return (aState == TopAbs_IN || aState == TopAbs_ON );
10600 //================================================================================
10602 * \brief Classifier of the 3D point on the TopoDS_Face
10603 * with interaface suitable for isInside()
10605 //================================================================================
10607 struct _FaceClassifier
10609 Extrema_ExtPS _extremum;
10610 BRepAdaptor_Surface _surface;
10611 TopAbs_State _state;
10613 _FaceClassifier(const TopoDS_Face& face):_extremum(),_surface(face),_state(TopAbs_OUT)
10615 _extremum.Initialize( _surface,
10616 _surface.FirstUParameter(), _surface.LastUParameter(),
10617 _surface.FirstVParameter(), _surface.LastVParameter(),
10618 _surface.Tolerance(), _surface.Tolerance() );
10620 void Perform(const gp_Pnt& aPnt, double theTol)
10623 _state = TopAbs_OUT;
10624 _extremum.Perform(aPnt);
10625 if ( _extremum.IsDone() )
10626 for ( int iSol = 1; iSol <= _extremum.NbExt() && _state == TopAbs_OUT; ++iSol)
10627 _state = ( _extremum.SquareDistance(iSol) <= theTol ? TopAbs_IN : TopAbs_OUT );
10629 TopAbs_State State() const
10636 //================================================================================
10638 \brief Identify the elements that will be affected by node duplication (actual duplication is not performed).
10639 This method is the first step of DoubleNodeElemGroupsInRegion.
10640 \param theElems - list of groups of elements (edges or faces) to be replicated
10641 \param theNodesNot - list of groups of nodes not to replicated
10642 \param theShape - shape to detect affected elements (element which geometric center
10643 located on or inside shape). If the shape is null, detection is done on faces orientations
10644 (select elements with a gravity center on the side given by faces normals).
10645 This mode (null shape) is faster, but works only when theElems are faces, with coherents orientations.
10646 The replicated nodes should be associated to affected elements.
10647 \return groups of affected elements
10648 \sa DoubleNodeElemGroupsInRegion()
10650 //================================================================================
10652 bool SMESH_MeshEditor::AffectedElemGroupsInRegion( const TIDSortedElemSet& theElems,
10653 const TIDSortedElemSet& theNodesNot,
10654 const TopoDS_Shape& theShape,
10655 TIDSortedElemSet& theAffectedElems)
10657 if ( theShape.IsNull() )
10659 std::set<const SMDS_MeshNode*> alreadyCheckedNodes;
10660 std::set<const SMDS_MeshElement*> alreadyCheckedElems;
10661 std::set<const SMDS_MeshElement*> edgesToCheck;
10662 alreadyCheckedNodes.clear();
10663 alreadyCheckedElems.clear();
10664 edgesToCheck.clear();
10666 // --- iterates on elements to be replicated and get elements by back references from their nodes
10668 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10670 for ( ielem=1; elemItr != theElems.end(); ++elemItr )
10672 SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
10673 if (!anElem || (anElem->GetType() != SMDSAbs_Face))
10676 SMESH_MeshAlgos::FaceNormal( anElem, normal, /*normalized=*/true );
10677 MESSAGE("element " << ielem++ << " normal " << normal.X() << " " << normal.Y() << " " << normal.Z());
10678 std::set<const SMDS_MeshNode*> nodesElem;
10680 SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
10681 while ( nodeItr->more() )
10683 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10684 nodesElem.insert(aNode);
10686 std::set<const SMDS_MeshNode*>::iterator nodit = nodesElem.begin();
10687 for (; nodit != nodesElem.end(); nodit++)
10689 MESSAGE(" noeud ");
10690 const SMDS_MeshNode* aNode = *nodit;
10691 if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
10693 if (alreadyCheckedNodes.find(aNode) != alreadyCheckedNodes.end())
10695 alreadyCheckedNodes.insert(aNode);
10696 SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
10697 while ( backElemItr->more() )
10699 MESSAGE(" backelem ");
10700 const SMDS_MeshElement* curElem = backElemItr->next();
10701 if (alreadyCheckedElems.find(curElem) != alreadyCheckedElems.end())
10703 if (theElems.find(curElem) != theElems.end())
10705 alreadyCheckedElems.insert(curElem);
10706 double x=0, y=0, z=0;
10708 SMDS_ElemIteratorPtr nodeItr2 = curElem->nodesIterator();
10709 while ( nodeItr2->more() )
10711 const SMDS_MeshNode* anotherNode = cast2Node(nodeItr2->next());
10712 x += anotherNode->X();
10713 y += anotherNode->Y();
10714 z += anotherNode->Z();
10718 p.SetCoord( x/nb -aNode->X(),
10720 z/nb -aNode->Z() );
10721 MESSAGE(" check " << p.X() << " " << p.Y() << " " << p.Z());
10724 MESSAGE(" --- inserted")
10725 theAffectedElems.insert( curElem );
10727 else if (curElem->GetType() == SMDSAbs_Edge)
10728 edgesToCheck.insert(curElem);
10732 // --- add also edges lying on the set of faces (all nodes in alreadyCheckedNodes)
10733 std::set<const SMDS_MeshElement*>::iterator eit = edgesToCheck.begin();
10734 for( ; eit != edgesToCheck.end(); eit++)
10736 bool onside = true;
10737 const SMDS_MeshElement* anEdge = *eit;
10738 SMDS_ElemIteratorPtr nodeItr = anEdge->nodesIterator();
10739 while ( nodeItr->more() )
10741 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10742 if (alreadyCheckedNodes.find(aNode) == alreadyCheckedNodes.end())
10750 MESSAGE(" --- edge onside inserted")
10751 theAffectedElems.insert(anEdge);
10757 const double aTol = Precision::Confusion();
10758 auto_ptr< BRepClass3d_SolidClassifier> bsc3d;
10759 auto_ptr<_FaceClassifier> aFaceClassifier;
10760 if ( theShape.ShapeType() == TopAbs_SOLID )
10762 bsc3d.reset( new BRepClass3d_SolidClassifier(theShape));;
10763 bsc3d->PerformInfinitePoint(aTol);
10765 else if (theShape.ShapeType() == TopAbs_FACE )
10767 aFaceClassifier.reset( new _FaceClassifier(TopoDS::Face(theShape)));
10770 // iterates on indicated elements and get elements by back references from their nodes
10771 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10773 for ( ielem = 1; elemItr != theElems.end(); ++elemItr )
10775 MESSAGE("element " << ielem++);
10776 SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
10779 SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
10780 while ( nodeItr->more() )
10782 MESSAGE(" noeud ");
10783 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10784 if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
10786 SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
10787 while ( backElemItr->more() )
10789 MESSAGE(" backelem ");
10790 const SMDS_MeshElement* curElem = backElemItr->next();
10791 if ( curElem && theElems.find(curElem) == theElems.end() &&
10793 isInside( curElem, *bsc3d, aTol ) :
10794 isInside( curElem, *aFaceClassifier, aTol )))
10795 theAffectedElems.insert( curElem );
10803 //================================================================================
10805 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10806 \param theElems - group of of elements (edges or faces) to be replicated
10807 \param theNodesNot - group of nodes not to replicate
10808 \param theShape - shape to detect affected elements (element which geometric center
10809 located on or inside shape).
10810 The replicated nodes should be associated to affected elements.
10811 \return TRUE if operation has been completed successfully, FALSE otherwise
10813 //================================================================================
10815 bool SMESH_MeshEditor::DoubleNodesInRegion( const TIDSortedElemSet& theElems,
10816 const TIDSortedElemSet& theNodesNot,
10817 const TopoDS_Shape& theShape )
10819 if ( theShape.IsNull() )
10822 const double aTol = Precision::Confusion();
10823 auto_ptr< BRepClass3d_SolidClassifier> bsc3d;
10824 auto_ptr<_FaceClassifier> aFaceClassifier;
10825 if ( theShape.ShapeType() == TopAbs_SOLID )
10827 bsc3d.reset( new BRepClass3d_SolidClassifier(theShape));;
10828 bsc3d->PerformInfinitePoint(aTol);
10830 else if (theShape.ShapeType() == TopAbs_FACE )
10832 aFaceClassifier.reset( new _FaceClassifier(TopoDS::Face(theShape)));
10835 // iterates on indicated elements and get elements by back references from their nodes
10836 TIDSortedElemSet anAffected;
10837 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10838 for ( ; elemItr != theElems.end(); ++elemItr )
10840 SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
10844 SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
10845 while ( nodeItr->more() )
10847 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10848 if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
10850 SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
10851 while ( backElemItr->more() )
10853 const SMDS_MeshElement* curElem = backElemItr->next();
10854 if ( curElem && theElems.find(curElem) == theElems.end() &&
10856 isInside( curElem, *bsc3d, aTol ) :
10857 isInside( curElem, *aFaceClassifier, aTol )))
10858 anAffected.insert( curElem );
10862 return DoubleNodes( theElems, theNodesNot, anAffected );
10866 * \brief compute an oriented angle between two planes defined by four points.
10867 * The vector (p0,p1) defines the intersection of the 2 planes (p0,p1,g1) and (p0,p1,g2)
10868 * @param p0 base of the rotation axe
10869 * @param p1 extremity of the rotation axe
10870 * @param g1 belongs to the first plane
10871 * @param g2 belongs to the second plane
10873 double SMESH_MeshEditor::OrientedAngle(const gp_Pnt& p0, const gp_Pnt& p1, const gp_Pnt& g1, const gp_Pnt& g2)
10875 // MESSAGE(" p0: " << p0.X() << " " << p0.Y() << " " << p0.Z());
10876 // MESSAGE(" p1: " << p1.X() << " " << p1.Y() << " " << p1.Z());
10877 // MESSAGE(" g1: " << g1.X() << " " << g1.Y() << " " << g1.Z());
10878 // MESSAGE(" g2: " << g2.X() << " " << g2.Y() << " " << g2.Z());
10879 gp_Vec vref(p0, p1);
10882 gp_Vec n1 = vref.Crossed(v1);
10883 gp_Vec n2 = vref.Crossed(v2);
10885 return n2.AngleWithRef(n1, vref);
10887 catch ( Standard_Failure ) {
10889 return Max( v1.Magnitude(), v2.Magnitude() );
10893 * \brief Double nodes on shared faces between groups of volumes and create flat elements on demand.
10894 * The list of groups must contain at least two groups. The groups have to be disjoint: no common element into two different groups.
10895 * The nodes of the internal faces at the boundaries of the groups are doubled. Optionally, the internal faces are replaced by flat elements.
10896 * Triangles are transformed into prisms, and quadrangles into hexahedrons.
10897 * The flat elements are stored in groups of volumes. These groups are named according to the position of the group in the list:
10898 * 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.
10899 * 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.
10900 * All the flat elements are gathered into the group named "joints3D" (or "joints2D" in 2D situation).
10901 * The flat element of the multiple junctions between the simple junction are stored in a group named "jointsMultiples".
10902 * \param theElems - list of groups of volumes, where a group of volume is a set of
10903 * SMDS_MeshElements sorted by Id.
10904 * \param createJointElems - if TRUE, create the elements
10905 * \param onAllBoundaries - if TRUE, the nodes and elements are also created on
10906 * the boundary between \a theDomains and the rest mesh
10907 * \return TRUE if operation has been completed successfully, FALSE otherwise
10909 bool SMESH_MeshEditor::DoubleNodesOnGroupBoundaries( const std::vector<TIDSortedElemSet>& theElems,
10910 bool createJointElems,
10911 bool onAllBoundaries)
10913 MESSAGE("----------------------------------------------");
10914 MESSAGE("SMESH_MeshEditor::doubleNodesOnGroupBoundaries");
10915 MESSAGE("----------------------------------------------");
10917 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
10918 meshDS->BuildDownWardConnectivity(true);
10920 SMDS_UnstructuredGrid *grid = meshDS->getGrid();
10922 // --- build the list of faces shared by 2 domains (group of elements), with their domain and volume indexes
10923 // build the list of cells with only a node or an edge on the border, with their domain and volume indexes
10924 // build the list of nodes shared by 2 or more domains, with their domain indexes
10926 std::map<DownIdType, std::map<int,int>, DownIdCompare> faceDomains; // face --> (id domain --> id volume)
10927 std::map<int,int>celldom; // cell vtkId --> domain
10928 std::map<DownIdType, std::map<int,int>, DownIdCompare> cellDomains; // oldNode --> (id domain --> id cell)
10929 std::map<int, std::map<int,int> > nodeDomains; // oldId --> (domainId --> newId)
10930 faceDomains.clear();
10932 cellDomains.clear();
10933 nodeDomains.clear();
10934 std::map<int,int> emptyMap;
10935 std::set<int> emptySet;
10938 MESSAGE(".. Number of domains :"<<theElems.size());
10940 TIDSortedElemSet theRestDomElems;
10941 const int iRestDom = -1;
10942 const int idom0 = onAllBoundaries ? iRestDom : 0;
10943 const int nbDomains = theElems.size();
10945 // Check if the domains do not share an element
10946 for (int idom = 0; idom < nbDomains-1; idom++)
10948 // MESSAGE("... Check of domain #" << idom);
10949 const TIDSortedElemSet& domain = theElems[idom];
10950 TIDSortedElemSet::const_iterator elemItr = domain.begin();
10951 for (; elemItr != domain.end(); ++elemItr)
10953 const SMDS_MeshElement* anElem = *elemItr;
10954 int idombisdeb = idom + 1 ;
10955 for (int idombis = idombisdeb; idombis < theElems.size(); idombis++) // check if the element belongs to a domain further in the list
10957 const TIDSortedElemSet& domainbis = theElems[idombis];
10958 if ( domainbis.count(anElem) )
10960 MESSAGE(".... Domain #" << idom);
10961 MESSAGE(".... Domain #" << idombis);
10962 throw SALOME_Exception("The domains are not disjoint.");
10969 for (int idom = 0; idom < nbDomains; idom++)
10972 // --- build a map (face to duplicate --> volume to modify)
10973 // with all the faces shared by 2 domains (group of elements)
10974 // and corresponding volume of this domain, for each shared face.
10975 // a volume has a face shared by 2 domains if it has a neighbor which is not in his domain.
10977 MESSAGE("... Neighbors of domain #" << idom);
10978 const TIDSortedElemSet& domain = theElems[idom];
10979 TIDSortedElemSet::const_iterator elemItr = domain.begin();
10980 for (; elemItr != domain.end(); ++elemItr)
10982 const SMDS_MeshElement* anElem = *elemItr;
10985 int vtkId = anElem->getVtkId();
10986 //MESSAGE(" vtkId " << vtkId << " smdsId " << anElem->GetID());
10987 int neighborsVtkIds[NBMAXNEIGHBORS];
10988 int downIds[NBMAXNEIGHBORS];
10989 unsigned char downTypes[NBMAXNEIGHBORS];
10990 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
10991 for (int n = 0; n < nbNeighbors; n++)
10993 int smdsId = meshDS->fromVtkToSmds(neighborsVtkIds[n]);
10994 const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
10995 if (elem && ! domain.count(elem)) // neighbor is in another domain : face is shared
10998 for (int idombis = 0; idombis < theElems.size() && !ok; idombis++) // check if the neighbor belongs to another domain of the list
11000 // MESSAGE("Domain " << idombis);
11001 const TIDSortedElemSet& domainbis = theElems[idombis];
11002 if ( domainbis.count(elem)) ok = true ; // neighbor is in a correct domain : face is kept
11004 if ( ok || onAllBoundaries ) // the characteristics of the face is stored
11006 DownIdType face(downIds[n], downTypes[n]);
11007 if (!faceDomains[face].count(idom))
11009 faceDomains[face][idom] = vtkId; // volume associated to face in this domain
11010 celldom[vtkId] = idom;
11011 //MESSAGE(" cell with a border " << vtkId << " domain " << idom);
11015 theRestDomElems.insert( elem );
11016 faceDomains[face][iRestDom] = neighborsVtkIds[n];
11017 celldom[neighborsVtkIds[n]] = iRestDom;
11025 //MESSAGE("Number of shared faces " << faceDomains.size());
11026 std::map<DownIdType, std::map<int, int>, DownIdCompare>::iterator itface;
11028 // --- explore the shared faces domain by domain,
11029 // explore the nodes of the face and see if they belong to a cell in the domain,
11030 // which has only a node or an edge on the border (not a shared face)
11032 for (int idomain = idom0; idomain < nbDomains; idomain++)
11034 //MESSAGE("Domain " << idomain);
11035 const TIDSortedElemSet& domain = (idomain == iRestDom) ? theRestDomElems : theElems[idomain];
11036 itface = faceDomains.begin();
11037 for (; itface != faceDomains.end(); ++itface)
11039 const std::map<int, int>& domvol = itface->second;
11040 if (!domvol.count(idomain))
11042 DownIdType face = itface->first;
11043 //MESSAGE(" --- face " << face.cellId);
11044 std::set<int> oldNodes;
11046 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11047 std::set<int>::iterator itn = oldNodes.begin();
11048 for (; itn != oldNodes.end(); ++itn)
11051 //MESSAGE(" node " << oldId);
11052 vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
11053 for (int i=0; i<l.ncells; i++)
11055 int vtkId = l.cells[i];
11056 const SMDS_MeshElement* anElem = GetMeshDS()->FindElement(GetMeshDS()->fromVtkToSmds(vtkId));
11057 if (!domain.count(anElem))
11059 int vtkType = grid->GetCellType(vtkId);
11060 int downId = grid->CellIdToDownId(vtkId);
11063 MESSAGE("doubleNodesOnGroupBoundaries: internal algorithm problem");
11064 continue; // not OK at this stage of the algorithm:
11065 //no cells created after BuildDownWardConnectivity
11067 DownIdType aCell(downId, vtkType);
11068 cellDomains[aCell][idomain] = vtkId;
11069 celldom[vtkId] = idomain;
11070 //MESSAGE(" cell " << vtkId << " domain " << idomain);
11076 // --- explore the shared faces domain by domain, to duplicate the nodes in a coherent way
11077 // for each shared face, get the nodes
11078 // for each node, for each domain of the face, create a clone of the node
11080 // --- edges at the intersection of 3 or 4 domains, with the order of domains to build
11081 // junction elements of type prism or hexa. the key is the pair of nodesId (lower first)
11082 // the value is the ordered domain ids. (more than 4 domains not taken into account)
11084 std::map<std::vector<int>, std::vector<int> > edgesMultiDomains; // nodes of edge --> ordered domains
11085 std::map<int, std::vector<int> > mutipleNodes; // nodes multi domains with domain order
11086 std::map<int, std::vector<int> > mutipleNodesToFace; // nodes multi domains with domain order to transform in Face (junction between 3 or more 2D domains)
11088 MESSAGE(".. Duplication of the nodes");
11089 for (int idomain = idom0; idomain < nbDomains; idomain++)
11091 itface = faceDomains.begin();
11092 for (; itface != faceDomains.end(); ++itface)
11094 const std::map<int, int>& domvol = itface->second;
11095 if (!domvol.count(idomain))
11097 DownIdType face = itface->first;
11098 //MESSAGE(" --- face " << face.cellId);
11099 std::set<int> oldNodes;
11101 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11102 std::set<int>::iterator itn = oldNodes.begin();
11103 for (; itn != oldNodes.end(); ++itn)
11106 if (nodeDomains[oldId].empty())
11108 nodeDomains[oldId][idomain] = oldId; // keep the old node in the first domain
11109 //MESSAGE("-+-+-b oldNode " << oldId << " domain " << idomain);
11111 std::map<int, int>::const_iterator itdom = domvol.begin();
11112 for (; itdom != domvol.end(); ++itdom)
11114 int idom = itdom->first;
11115 //MESSAGE(" domain " << idom);
11116 if (!nodeDomains[oldId].count(idom)) // --- node to clone
11118 if (nodeDomains[oldId].size() >= 2) // a multiple node
11120 vector<int> orderedDoms;
11121 //MESSAGE("multiple node " << oldId);
11122 if (mutipleNodes.count(oldId))
11123 orderedDoms = mutipleNodes[oldId];
11126 map<int,int>::iterator it = nodeDomains[oldId].begin();
11127 for (; it != nodeDomains[oldId].end(); ++it)
11128 orderedDoms.push_back(it->first);
11130 orderedDoms.push_back(idom); // TODO order ==> push_front or back
11131 //stringstream txt;
11132 //for (int i=0; i<orderedDoms.size(); i++)
11133 // txt << orderedDoms[i] << " ";
11134 //MESSAGE("orderedDoms " << txt.str());
11135 mutipleNodes[oldId] = orderedDoms;
11137 double *coords = grid->GetPoint(oldId);
11138 SMDS_MeshNode *newNode = meshDS->AddNode(coords[0], coords[1], coords[2]);
11139 copyPosition( meshDS->FindNodeVtk( oldId ), newNode );
11140 int newId = newNode->getVtkId();
11141 nodeDomains[oldId][idom] = newId; // cloned node for other domains
11142 //MESSAGE("-+-+-c oldNode " << oldId << " domain " << idomain << " newNode " << newId << " domain " << idom << " size=" <<nodeDomains[oldId].size());
11149 MESSAGE(".. Creation of elements");
11150 for (int idomain = idom0; idomain < nbDomains; idomain++)
11152 itface = faceDomains.begin();
11153 for (; itface != faceDomains.end(); ++itface)
11155 std::map<int, int> domvol = itface->second;
11156 if (!domvol.count(idomain))
11158 DownIdType face = itface->first;
11159 //MESSAGE(" --- face " << face.cellId);
11160 std::set<int> oldNodes;
11162 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11163 int nbMultipleNodes = 0;
11164 std::set<int>::iterator itn = oldNodes.begin();
11165 for (; itn != oldNodes.end(); ++itn)
11168 if (mutipleNodes.count(oldId))
11171 if (nbMultipleNodes > 1) // check if an edge of the face is shared between 3 or more domains
11173 //MESSAGE("multiple Nodes detected on a shared face");
11174 int downId = itface->first.cellId;
11175 unsigned char cellType = itface->first.cellType;
11176 // --- shared edge or shared face ?
11177 if ((cellType == VTK_LINE) || (cellType == VTK_QUADRATIC_EDGE)) // shared edge (between two faces)
11180 int nbNodes = grid->getDownArray(cellType)->getNodes(downId, nodes);
11181 for (int i=0; i< nbNodes; i=i+nbNodes-1) // i=0 , i=nbNodes-1
11182 if (mutipleNodes.count(nodes[i]))
11183 if (!mutipleNodesToFace.count(nodes[i]))
11184 mutipleNodesToFace[nodes[i]] = mutipleNodes[nodes[i]];
11186 else // shared face (between two volumes)
11188 int nbEdges = grid->getDownArray(cellType)->getNumberOfDownCells(downId);
11189 const int* downEdgeIds = grid->getDownArray(cellType)->getDownCells(downId);
11190 const unsigned char* edgeType = grid->getDownArray(cellType)->getDownTypes(downId);
11191 for (int ie =0; ie < nbEdges; ie++)
11194 int nbNodes = grid->getDownArray(edgeType[ie])->getNodes(downEdgeIds[ie], nodes);
11195 if (mutipleNodes.count(nodes[0]) && mutipleNodes.count(nodes[nbNodes-1]))
11197 vector<int> vn0 = mutipleNodes[nodes[0]];
11198 vector<int> vn1 = mutipleNodes[nodes[nbNodes - 1]];
11200 for (int i0 = 0; i0 < vn0.size(); i0++)
11201 for (int i1 = 0; i1 < vn1.size(); i1++)
11202 if (vn0[i0] == vn1[i1])
11203 doms.push_back(vn0[i0]);
11204 if (doms.size() >2)
11206 //MESSAGE(" detect edgesMultiDomains " << nodes[0] << " " << nodes[nbNodes - 1]);
11207 double *coords = grid->GetPoint(nodes[0]);
11208 gp_Pnt p0(coords[0], coords[1], coords[2]);
11209 coords = grid->GetPoint(nodes[nbNodes - 1]);
11210 gp_Pnt p1(coords[0], coords[1], coords[2]);
11212 int vtkVolIds[1000]; // an edge can belong to a lot of volumes
11213 map<int, SMDS_VtkVolume*> domvol; // domain --> a volume with the edge
11214 map<int, double> angleDom; // oriented angles between planes defined by edge and volume centers
11215 int nbvol = grid->GetParentVolumes(vtkVolIds, downEdgeIds[ie], edgeType[ie]);
11216 for (int id=0; id < doms.size(); id++)
11218 int idom = doms[id];
11219 const TIDSortedElemSet& domain = (idom == iRestDom) ? theRestDomElems : theElems[idom];
11220 for (int ivol=0; ivol<nbvol; ivol++)
11222 int smdsId = meshDS->fromVtkToSmds(vtkVolIds[ivol]);
11223 SMDS_MeshElement* elem = (SMDS_MeshElement*)meshDS->FindElement(smdsId);
11224 if (domain.count(elem))
11226 SMDS_VtkVolume* svol = dynamic_cast<SMDS_VtkVolume*>(elem);
11227 domvol[idom] = svol;
11228 //MESSAGE(" domain " << idom << " volume " << elem->GetID());
11230 vtkIdType npts = 0;
11231 vtkIdType* pts = 0;
11232 grid->GetCellPoints(vtkVolIds[ivol], npts, pts);
11233 SMDS_VtkVolume::gravityCenter(grid, pts, npts, values);
11236 gref.SetXYZ(gp_XYZ(values[0], values[1], values[2]));
11237 angleDom[idom] = 0;
11241 gp_Pnt g(values[0], values[1], values[2]);
11242 angleDom[idom] = OrientedAngle(p0, p1, gref, g); // -pi<angle<+pi
11243 //MESSAGE(" angle=" << angleDom[idom]);
11249 map<double, int> sortedDom; // sort domains by angle
11250 for (map<int, double>::iterator ia = angleDom.begin(); ia != angleDom.end(); ++ia)
11251 sortedDom[ia->second] = ia->first;
11252 vector<int> vnodes;
11254 for (map<double, int>::iterator ib = sortedDom.begin(); ib != sortedDom.end(); ++ib)
11256 vdom.push_back(ib->second);
11257 //MESSAGE(" ordered domain " << ib->second << " angle " << ib->first);
11259 for (int ino = 0; ino < nbNodes; ino++)
11260 vnodes.push_back(nodes[ino]);
11261 edgesMultiDomains[vnodes] = vdom; // nodes vector --> ordered domains
11270 // --- iterate on shared faces (volumes to modify, face to extrude)
11271 // get node id's of the face (id SMDS = id VTK)
11272 // create flat element with old and new nodes if requested
11274 // --- new quad nodes on flat quad elements: oldId --> ((domain1 X domain2) --> newId)
11275 // (domain1 X domain2) = domain1 + MAXINT*domain2
11277 std::map<int, std::map<long,int> > nodeQuadDomains;
11278 std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11280 MESSAGE(".. Creation of elements: simple junction");
11281 if (createJointElems)
11284 string joints2DName = "joints2D";
11285 mapOfJunctionGroups[joints2DName] = this->myMesh->AddGroup(SMDSAbs_Face, joints2DName.c_str(), idg);
11286 SMESHDS_Group *joints2DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints2DName]->GetGroupDS());
11287 string joints3DName = "joints3D";
11288 mapOfJunctionGroups[joints3DName] = this->myMesh->AddGroup(SMDSAbs_Volume, joints3DName.c_str(), idg);
11289 SMESHDS_Group *joints3DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints3DName]->GetGroupDS());
11291 itface = faceDomains.begin();
11292 for (; itface != faceDomains.end(); ++itface)
11294 DownIdType face = itface->first;
11295 std::set<int> oldNodes;
11296 std::set<int>::iterator itn;
11298 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11300 std::map<int, int> domvol = itface->second;
11301 std::map<int, int>::iterator itdom = domvol.begin();
11302 int dom1 = itdom->first;
11303 int vtkVolId = itdom->second;
11305 int dom2 = itdom->first;
11306 SMDS_MeshCell *vol = grid->extrudeVolumeFromFace(vtkVolId, dom1, dom2, oldNodes, nodeDomains,
11308 stringstream grpname;
11311 grpname << dom1 << "_" << dom2;
11313 grpname << dom2 << "_" << dom1;
11314 string namegrp = grpname.str();
11315 if (!mapOfJunctionGroups.count(namegrp))
11316 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(vol->GetType(), namegrp.c_str(), idg);
11317 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11319 sgrp->Add(vol->GetID());
11320 if (vol->GetType() == SMDSAbs_Volume)
11321 joints3DGrp->Add(vol->GetID());
11322 else if (vol->GetType() == SMDSAbs_Face)
11323 joints2DGrp->Add(vol->GetID());
11327 // --- create volumes on multiple domain intersection if requested
11328 // iterate on mutipleNodesToFace
11329 // iterate on edgesMultiDomains
11331 MESSAGE(".. Creation of elements: multiple junction");
11332 if (createJointElems)
11334 // --- iterate on mutipleNodesToFace
11336 std::map<int, std::vector<int> >::iterator itn = mutipleNodesToFace.begin();
11337 for (; itn != mutipleNodesToFace.end(); ++itn)
11339 int node = itn->first;
11340 vector<int> orderDom = itn->second;
11341 vector<vtkIdType> orderedNodes;
11342 for (int idom = 0; idom <orderDom.size(); idom++)
11343 orderedNodes.push_back( nodeDomains[node][orderDom[idom]] );
11344 SMDS_MeshFace* face = this->GetMeshDS()->AddFaceFromVtkIds(orderedNodes);
11346 stringstream grpname;
11348 grpname << 0 << "_" << 0;
11350 string namegrp = grpname.str();
11351 if (!mapOfJunctionGroups.count(namegrp))
11352 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Face, namegrp.c_str(), idg);
11353 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11355 sgrp->Add(face->GetID());
11358 // --- iterate on edgesMultiDomains
11360 std::map<std::vector<int>, std::vector<int> >::iterator ite = edgesMultiDomains.begin();
11361 for (; ite != edgesMultiDomains.end(); ++ite)
11363 vector<int> nodes = ite->first;
11364 vector<int> orderDom = ite->second;
11365 vector<vtkIdType> orderedNodes;
11366 if (nodes.size() == 2)
11368 //MESSAGE(" use edgesMultiDomains " << nodes[0] << " " << nodes[1]);
11369 for (int ino=0; ino < nodes.size(); ino++)
11370 if (orderDom.size() == 3)
11371 for (int idom = 0; idom <orderDom.size(); idom++)
11372 orderedNodes.push_back( nodeDomains[nodes[ino]][orderDom[idom]] );
11374 for (int idom = orderDom.size()-1; idom >=0; idom--)
11375 orderedNodes.push_back( nodeDomains[nodes[ino]][orderDom[idom]] );
11376 SMDS_MeshVolume* vol = this->GetMeshDS()->AddVolumeFromVtkIds(orderedNodes);
11379 string namegrp = "jointsMultiples";
11380 if (!mapOfJunctionGroups.count(namegrp))
11381 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
11382 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11384 sgrp->Add(vol->GetID());
11388 INFOS("Quadratic multiple joints not implemented");
11389 // TODO quadratic nodes
11394 // --- list the explicit faces and edges of the mesh that need to be modified,
11395 // i.e. faces and edges built with one or more duplicated nodes.
11396 // associate these faces or edges to their corresponding domain.
11397 // only the first domain found is kept when a face or edge is shared
11399 std::map<DownIdType, std::map<int,int>, DownIdCompare> faceOrEdgeDom; // cellToModify --> (id domain --> id cell)
11400 std::map<int,int> feDom; // vtk id of cell to modify --> id domain
11401 faceOrEdgeDom.clear();
11404 MESSAGE(".. Modification of elements");
11405 for (int idomain = idom0; idomain < nbDomains; idomain++)
11407 std::map<int, std::map<int, int> >::const_iterator itnod = nodeDomains.begin();
11408 for (; itnod != nodeDomains.end(); ++itnod)
11410 int oldId = itnod->first;
11411 //MESSAGE(" node " << oldId);
11412 vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
11413 for (int i = 0; i < l.ncells; i++)
11415 int vtkId = l.cells[i];
11416 int vtkType = grid->GetCellType(vtkId);
11417 int downId = grid->CellIdToDownId(vtkId);
11419 continue; // new cells: not to be modified
11420 DownIdType aCell(downId, vtkType);
11421 int volParents[1000];
11422 int nbvol = grid->GetParentVolumes(volParents, vtkId);
11423 for (int j = 0; j < nbvol; j++)
11424 if (celldom.count(volParents[j]) && (celldom[volParents[j]] == idomain))
11425 if (!feDom.count(vtkId))
11427 feDom[vtkId] = idomain;
11428 faceOrEdgeDom[aCell] = emptyMap;
11429 faceOrEdgeDom[aCell][idomain] = vtkId; // affect face or edge to the first domain only
11430 //MESSAGE("affect cell " << this->GetMeshDS()->fromVtkToSmds(vtkId) << " domain " << idomain
11431 // << " type " << vtkType << " downId " << downId);
11437 // --- iterate on shared faces (volumes to modify, face to extrude)
11438 // get node id's of the face
11439 // replace old nodes by new nodes in volumes, and update inverse connectivity
11441 std::map<DownIdType, std::map<int,int>, DownIdCompare>* maps[3] = {&faceDomains, &cellDomains, &faceOrEdgeDom};
11442 for (int m=0; m<3; m++)
11444 std::map<DownIdType, std::map<int,int>, DownIdCompare>* amap = maps[m];
11445 itface = (*amap).begin();
11446 for (; itface != (*amap).end(); ++itface)
11448 DownIdType face = itface->first;
11449 std::set<int> oldNodes;
11450 std::set<int>::iterator itn;
11452 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11453 //MESSAGE("examine cell, downId " << face.cellId << " type " << int(face.cellType));
11454 std::map<int, int> localClonedNodeIds;
11456 std::map<int, int> domvol = itface->second;
11457 std::map<int, int>::iterator itdom = domvol.begin();
11458 for (; itdom != domvol.end(); ++itdom)
11460 int idom = itdom->first;
11461 int vtkVolId = itdom->second;
11462 //MESSAGE("modify nodes of cell " << this->GetMeshDS()->fromVtkToSmds(vtkVolId) << " domain " << idom);
11463 localClonedNodeIds.clear();
11464 for (itn = oldNodes.begin(); itn != oldNodes.end(); ++itn)
11467 if (nodeDomains[oldId].count(idom))
11469 localClonedNodeIds[oldId] = nodeDomains[oldId][idom];
11470 //MESSAGE(" node " << oldId << " --> " << localClonedNodeIds[oldId]);
11473 meshDS->ModifyCellNodes(vtkVolId, localClonedNodeIds);
11478 // Remove empty groups (issue 0022812)
11479 std::map<std::string, SMESH_Group*>::iterator name_group = mapOfJunctionGroups.begin();
11480 for ( ; name_group != mapOfJunctionGroups.end(); ++name_group )
11482 if ( name_group->second && name_group->second->GetGroupDS()->IsEmpty() )
11483 myMesh->RemoveGroup( name_group->second->GetGroupDS()->GetID() );
11486 meshDS->CleanDownWardConnectivity(); // Mesh has been modified, downward connectivity is no more usable, free memory
11487 grid->BuildLinks();
11495 * \brief Double nodes on some external faces and create flat elements.
11496 * Flat elements are mainly used by some types of mechanic calculations.
11498 * Each group of the list must be constituted of faces.
11499 * Triangles are transformed in prisms, and quadrangles in hexahedrons.
11500 * @param theElems - list of groups of faces, where a group of faces is a set of
11501 * SMDS_MeshElements sorted by Id.
11502 * @return TRUE if operation has been completed successfully, FALSE otherwise
11504 bool SMESH_MeshEditor::CreateFlatElementsOnFacesGroups(const std::vector<TIDSortedElemSet>& theElems)
11506 MESSAGE("-------------------------------------------------");
11507 MESSAGE("SMESH_MeshEditor::CreateFlatElementsOnFacesGroups");
11508 MESSAGE("-------------------------------------------------");
11510 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11512 // --- For each group of faces
11513 // duplicate the nodes, create a flat element based on the face
11514 // replace the nodes of the faces by their clones
11516 std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> clonedNodes;
11517 std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> intermediateNodes;
11518 clonedNodes.clear();
11519 intermediateNodes.clear();
11520 std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11521 mapOfJunctionGroups.clear();
11523 for (int idom = 0; idom < theElems.size(); idom++)
11525 const TIDSortedElemSet& domain = theElems[idom];
11526 TIDSortedElemSet::const_iterator elemItr = domain.begin();
11527 for (; elemItr != domain.end(); ++elemItr)
11529 SMDS_MeshElement* anElem = (SMDS_MeshElement*) *elemItr;
11530 SMDS_MeshFace* aFace = dynamic_cast<SMDS_MeshFace*> (anElem);
11533 // MESSAGE("aFace=" << aFace->GetID());
11534 bool isQuad = aFace->IsQuadratic();
11535 vector<const SMDS_MeshNode*> ln0, ln1, ln2, ln3, ln4;
11537 // --- clone the nodes, create intermediate nodes for non medium nodes of a quad face
11539 SMDS_ElemIteratorPtr nodeIt = aFace->nodesIterator();
11540 while (nodeIt->more())
11542 const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*> (nodeIt->next());
11543 bool isMedium = isQuad && (aFace->IsMediumNode(node));
11545 ln2.push_back(node);
11547 ln0.push_back(node);
11549 const SMDS_MeshNode* clone = 0;
11550 if (!clonedNodes.count(node))
11552 clone = meshDS->AddNode(node->X(), node->Y(), node->Z());
11553 copyPosition( node, clone );
11554 clonedNodes[node] = clone;
11557 clone = clonedNodes[node];
11560 ln3.push_back(clone);
11562 ln1.push_back(clone);
11564 const SMDS_MeshNode* inter = 0;
11565 if (isQuad && (!isMedium))
11567 if (!intermediateNodes.count(node))
11569 inter = meshDS->AddNode(node->X(), node->Y(), node->Z());
11570 copyPosition( node, inter );
11571 intermediateNodes[node] = inter;
11574 inter = intermediateNodes[node];
11575 ln4.push_back(inter);
11579 // --- extrude the face
11581 vector<const SMDS_MeshNode*> ln;
11582 SMDS_MeshVolume* vol = 0;
11583 vtkIdType aType = aFace->GetVtkType();
11587 vol = meshDS->AddVolume(ln0[2], ln0[1], ln0[0], ln1[2], ln1[1], ln1[0]);
11588 // MESSAGE("vol prism " << vol->GetID());
11589 ln.push_back(ln1[0]);
11590 ln.push_back(ln1[1]);
11591 ln.push_back(ln1[2]);
11594 vol = meshDS->AddVolume(ln0[3], ln0[2], ln0[1], ln0[0], ln1[3], ln1[2], ln1[1], ln1[0]);
11595 // MESSAGE("vol hexa " << vol->GetID());
11596 ln.push_back(ln1[0]);
11597 ln.push_back(ln1[1]);
11598 ln.push_back(ln1[2]);
11599 ln.push_back(ln1[3]);
11601 case VTK_QUADRATIC_TRIANGLE:
11602 vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln0[0], ln0[1], ln0[2], ln3[0], ln3[1], ln3[2],
11603 ln2[0], ln2[1], ln2[2], ln4[0], ln4[1], ln4[2]);
11604 // MESSAGE("vol quad prism " << vol->GetID());
11605 ln.push_back(ln1[0]);
11606 ln.push_back(ln1[1]);
11607 ln.push_back(ln1[2]);
11608 ln.push_back(ln3[0]);
11609 ln.push_back(ln3[1]);
11610 ln.push_back(ln3[2]);
11612 case VTK_QUADRATIC_QUAD:
11613 // vol = meshDS->AddVolume(ln0[0], ln0[1], ln0[2], ln0[3], ln1[0], ln1[1], ln1[2], ln1[3],
11614 // ln2[0], ln2[1], ln2[2], ln2[3], ln3[0], ln3[1], ln3[2], ln3[3],
11615 // ln4[0], ln4[1], ln4[2], ln4[3]);
11616 vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln1[3], ln0[0], ln0[1], ln0[2], ln0[3],
11617 ln3[0], ln3[1], ln3[2], ln3[3], ln2[0], ln2[1], ln2[2], ln2[3],
11618 ln4[0], ln4[1], ln4[2], ln4[3]);
11619 // MESSAGE("vol quad hexa " << vol->GetID());
11620 ln.push_back(ln1[0]);
11621 ln.push_back(ln1[1]);
11622 ln.push_back(ln1[2]);
11623 ln.push_back(ln1[3]);
11624 ln.push_back(ln3[0]);
11625 ln.push_back(ln3[1]);
11626 ln.push_back(ln3[2]);
11627 ln.push_back(ln3[3]);
11637 stringstream grpname;
11641 string namegrp = grpname.str();
11642 if (!mapOfJunctionGroups.count(namegrp))
11643 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
11644 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11646 sgrp->Add(vol->GetID());
11649 // --- modify the face
11651 aFace->ChangeNodes(&ln[0], ln.size());
11658 * \brief identify all the elements around a geom shape, get the faces delimiting the hole
11659 * Build groups of volume to remove, groups of faces to replace on the skin of the object,
11660 * groups of faces to remove inside the object, (idem edges).
11661 * Build ordered list of nodes at the border of each group of faces to replace (to be used to build a geom subshape)
11663 void SMESH_MeshEditor::CreateHoleSkin(double radius,
11664 const TopoDS_Shape& theShape,
11665 SMESH_NodeSearcher* theNodeSearcher,
11666 const char* groupName,
11667 std::vector<double>& nodesCoords,
11668 std::vector<std::vector<int> >& listOfListOfNodes)
11670 MESSAGE("--------------------------------");
11671 MESSAGE("SMESH_MeshEditor::CreateHoleSkin");
11672 MESSAGE("--------------------------------");
11674 // --- zone of volumes to remove is given :
11675 // 1 either by a geom shape (one or more vertices) and a radius,
11676 // 2 either by a group of nodes (representative of the shape)to use with the radius,
11677 // 3 either by a group of nodes where all the elements build on one of this nodes are to remove,
11678 // In the case 2, the group of nodes is an external group of nodes from another mesh,
11679 // In the case 3, the group of nodes is an internal group of the mesh (obtained for instance by a filter),
11680 // defined by it's name.
11682 SMESHDS_GroupBase* groupDS = 0;
11683 SMESH_Mesh::GroupIteratorPtr groupIt = this->myMesh->GetGroups();
11684 while ( groupIt->more() )
11687 SMESH_Group * group = groupIt->next();
11688 if ( !group ) continue;
11689 groupDS = group->GetGroupDS();
11690 if ( !groupDS || groupDS->IsEmpty() ) continue;
11691 std::string grpName = group->GetName();
11692 //MESSAGE("grpName=" << grpName);
11693 if (grpName == groupName)
11699 bool isNodeGroup = false;
11700 bool isNodeCoords = false;
11703 if (groupDS->GetType() != SMDSAbs_Node)
11705 isNodeGroup = true; // a group of nodes exists and it is in this mesh
11708 if (nodesCoords.size() > 0)
11709 isNodeCoords = true; // a list o nodes given by their coordinates
11710 //MESSAGE("---" << isNodeGroup << " " << isNodeCoords);
11712 // --- define groups to build
11714 int idg; // --- group of SMDS volumes
11715 string grpvName = groupName;
11716 grpvName += "_vol";
11717 SMESH_Group *grp = this->myMesh->AddGroup(SMDSAbs_Volume, grpvName.c_str(), idg);
11720 MESSAGE("group not created " << grpvName);
11723 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(grp->GetGroupDS());
11725 int idgs; // --- group of SMDS faces on the skin
11726 string grpsName = groupName;
11727 grpsName += "_skin";
11728 SMESH_Group *grps = this->myMesh->AddGroup(SMDSAbs_Face, grpsName.c_str(), idgs);
11731 MESSAGE("group not created " << grpsName);
11734 SMESHDS_Group *sgrps = dynamic_cast<SMESHDS_Group*>(grps->GetGroupDS());
11736 int idgi; // --- group of SMDS faces internal (several shapes)
11737 string grpiName = groupName;
11738 grpiName += "_internalFaces";
11739 SMESH_Group *grpi = this->myMesh->AddGroup(SMDSAbs_Face, grpiName.c_str(), idgi);
11742 MESSAGE("group not created " << grpiName);
11745 SMESHDS_Group *sgrpi = dynamic_cast<SMESHDS_Group*>(grpi->GetGroupDS());
11747 int idgei; // --- group of SMDS faces internal (several shapes)
11748 string grpeiName = groupName;
11749 grpeiName += "_internalEdges";
11750 SMESH_Group *grpei = this->myMesh->AddGroup(SMDSAbs_Edge, grpeiName.c_str(), idgei);
11753 MESSAGE("group not created " << grpeiName);
11756 SMESHDS_Group *sgrpei = dynamic_cast<SMESHDS_Group*>(grpei->GetGroupDS());
11758 // --- build downward connectivity
11760 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11761 meshDS->BuildDownWardConnectivity(true);
11762 SMDS_UnstructuredGrid* grid = meshDS->getGrid();
11764 // --- set of volumes detected inside
11766 std::set<int> setOfInsideVol;
11767 std::set<int> setOfVolToCheck;
11769 std::vector<gp_Pnt> gpnts;
11772 if (isNodeGroup) // --- a group of nodes is provided : find all the volumes using one or more of this nodes
11774 MESSAGE("group of nodes provided");
11775 SMDS_ElemIteratorPtr elemIt = groupDS->GetElements();
11776 while ( elemIt->more() )
11778 const SMDS_MeshElement* elem = elemIt->next();
11781 const SMDS_MeshNode* node = dynamic_cast<const SMDS_MeshNode*>(elem);
11784 SMDS_MeshElement* vol = 0;
11785 SMDS_ElemIteratorPtr volItr = node->GetInverseElementIterator(SMDSAbs_Volume);
11786 while (volItr->more())
11788 vol = (SMDS_MeshElement*)volItr->next();
11789 setOfInsideVol.insert(vol->getVtkId());
11790 sgrp->Add(vol->GetID());
11794 else if (isNodeCoords)
11796 MESSAGE("list of nodes coordinates provided");
11799 while (i < nodesCoords.size()-2)
11801 double x = nodesCoords[i++];
11802 double y = nodesCoords[i++];
11803 double z = nodesCoords[i++];
11804 gp_Pnt p = gp_Pnt(x, y ,z);
11805 gpnts.push_back(p);
11806 MESSAGE("TopoDS_Vertex " << k << " " << p.X() << " " << p.Y() << " " << p.Z());
11810 else // --- no group, no coordinates : use the vertices of the geom shape provided, and radius
11812 MESSAGE("no group of nodes provided, using vertices from geom shape, and radius");
11813 TopTools_IndexedMapOfShape vertexMap;
11814 TopExp::MapShapes( theShape, TopAbs_VERTEX, vertexMap );
11815 gp_Pnt p = gp_Pnt(0,0,0);
11816 if (vertexMap.Extent() < 1)
11819 for ( int i = 1; i <= vertexMap.Extent(); ++i )
11821 const TopoDS_Vertex& vertex = TopoDS::Vertex( vertexMap( i ));
11822 p = BRep_Tool::Pnt(vertex);
11823 gpnts.push_back(p);
11824 MESSAGE("TopoDS_Vertex " << i << " " << p.X() << " " << p.Y() << " " << p.Z());
11828 if (gpnts.size() > 0)
11831 const SMDS_MeshNode* startNode = theNodeSearcher->FindClosestTo(gpnts[0]);
11833 nodeId = startNode->GetID();
11834 MESSAGE("nodeId " << nodeId);
11836 double radius2 = radius*radius;
11837 MESSAGE("radius2 " << radius2);
11839 // --- volumes on start node
11841 setOfVolToCheck.clear();
11842 SMDS_MeshElement* startVol = 0;
11843 SMDS_ElemIteratorPtr volItr = startNode->GetInverseElementIterator(SMDSAbs_Volume);
11844 while (volItr->more())
11846 startVol = (SMDS_MeshElement*)volItr->next();
11847 setOfVolToCheck.insert(startVol->getVtkId());
11849 if (setOfVolToCheck.empty())
11851 MESSAGE("No volumes found");
11855 // --- starting with central volumes then their neighbors, check if they are inside
11856 // or outside the domain, until no more new neighbor volume is inside.
11857 // Fill the group of inside volumes
11859 std::map<int, double> mapOfNodeDistance2;
11860 mapOfNodeDistance2.clear();
11861 std::set<int> setOfOutsideVol;
11862 while (!setOfVolToCheck.empty())
11864 std::set<int>::iterator it = setOfVolToCheck.begin();
11866 MESSAGE("volume to check, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
11867 bool volInside = false;
11868 vtkIdType npts = 0;
11869 vtkIdType* pts = 0;
11870 grid->GetCellPoints(vtkId, npts, pts);
11871 for (int i=0; i<npts; i++)
11873 double distance2 = 0;
11874 if (mapOfNodeDistance2.count(pts[i]))
11876 distance2 = mapOfNodeDistance2[pts[i]];
11877 MESSAGE("point " << pts[i] << " distance2 " << distance2);
11881 double *coords = grid->GetPoint(pts[i]);
11882 gp_Pnt aPoint = gp_Pnt(coords[0], coords[1], coords[2]);
11884 for (int j=0; j<gpnts.size(); j++)
11886 double d2 = aPoint.SquareDistance(gpnts[j]);
11887 if (d2 < distance2)
11890 if (distance2 < radius2)
11894 mapOfNodeDistance2[pts[i]] = distance2;
11895 MESSAGE(" point " << pts[i] << " distance2 " << distance2 << " coords " << coords[0] << " " << coords[1] << " " << coords[2]);
11897 if (distance2 < radius2)
11899 volInside = true; // one or more nodes inside the domain
11900 sgrp->Add(meshDS->fromVtkToSmds(vtkId));
11906 setOfInsideVol.insert(vtkId);
11907 MESSAGE(" volume inside, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
11908 int neighborsVtkIds[NBMAXNEIGHBORS];
11909 int downIds[NBMAXNEIGHBORS];
11910 unsigned char downTypes[NBMAXNEIGHBORS];
11911 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
11912 for (int n = 0; n < nbNeighbors; n++)
11913 if (!setOfInsideVol.count(neighborsVtkIds[n]) ||setOfOutsideVol.count(neighborsVtkIds[n]))
11914 setOfVolToCheck.insert(neighborsVtkIds[n]);
11918 setOfOutsideVol.insert(vtkId);
11919 MESSAGE(" volume outside, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
11921 setOfVolToCheck.erase(vtkId);
11925 // --- for outside hexahedrons, check if they have more than one neighbor volume inside
11926 // If yes, add the volume to the inside set
11928 bool addedInside = true;
11929 std::set<int> setOfVolToReCheck;
11930 while (addedInside)
11932 MESSAGE(" --------------------------- re check");
11933 addedInside = false;
11934 std::set<int>::iterator itv = setOfInsideVol.begin();
11935 for (; itv != setOfInsideVol.end(); ++itv)
11938 int neighborsVtkIds[NBMAXNEIGHBORS];
11939 int downIds[NBMAXNEIGHBORS];
11940 unsigned char downTypes[NBMAXNEIGHBORS];
11941 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
11942 for (int n = 0; n < nbNeighbors; n++)
11943 if (!setOfInsideVol.count(neighborsVtkIds[n]))
11944 setOfVolToReCheck.insert(neighborsVtkIds[n]);
11946 setOfVolToCheck = setOfVolToReCheck;
11947 setOfVolToReCheck.clear();
11948 while (!setOfVolToCheck.empty())
11950 std::set<int>::iterator it = setOfVolToCheck.begin();
11952 if (grid->GetCellType(vtkId) == VTK_HEXAHEDRON)
11954 MESSAGE("volume to recheck, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
11955 int countInside = 0;
11956 int neighborsVtkIds[NBMAXNEIGHBORS];
11957 int downIds[NBMAXNEIGHBORS];
11958 unsigned char downTypes[NBMAXNEIGHBORS];
11959 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
11960 for (int n = 0; n < nbNeighbors; n++)
11961 if (setOfInsideVol.count(neighborsVtkIds[n]))
11963 MESSAGE("countInside " << countInside);
11964 if (countInside > 1)
11966 MESSAGE(" volume inside, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
11967 setOfInsideVol.insert(vtkId);
11968 sgrp->Add(meshDS->fromVtkToSmds(vtkId));
11969 addedInside = true;
11972 setOfVolToReCheck.insert(vtkId);
11974 setOfVolToCheck.erase(vtkId);
11978 // --- map of Downward faces at the boundary, inside the global volume
11979 // map of Downward faces on the skin of the global volume (equivalent to SMDS faces on the skin)
11980 // fill group of SMDS faces inside the volume (when several volume shapes)
11981 // fill group of SMDS faces on the skin of the global volume (if skin)
11983 std::map<DownIdType, int, DownIdCompare> boundaryFaces; // boundary faces inside the volume --> corresponding cell
11984 std::map<DownIdType, int, DownIdCompare> skinFaces; // faces on the skin of the global volume --> corresponding cell
11985 std::set<int>::iterator it = setOfInsideVol.begin();
11986 for (; it != setOfInsideVol.end(); ++it)
11989 //MESSAGE(" vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
11990 int neighborsVtkIds[NBMAXNEIGHBORS];
11991 int downIds[NBMAXNEIGHBORS];
11992 unsigned char downTypes[NBMAXNEIGHBORS];
11993 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId, true);
11994 for (int n = 0; n < nbNeighbors; n++)
11996 int neighborDim = SMDS_Downward::getCellDimension(grid->GetCellType(neighborsVtkIds[n]));
11997 if (neighborDim == 3)
11999 if (! setOfInsideVol.count(neighborsVtkIds[n])) // neighbor volume is not inside : face is boundary
12001 DownIdType face(downIds[n], downTypes[n]);
12002 boundaryFaces[face] = vtkId;
12004 // if the face between to volumes is in the mesh, get it (internal face between shapes)
12005 int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12006 if (vtkFaceId >= 0)
12008 sgrpi->Add(meshDS->fromVtkToSmds(vtkFaceId));
12009 // find also the smds edges on this face
12010 int nbEdges = grid->getDownArray(downTypes[n])->getNumberOfDownCells(downIds[n]);
12011 const int* dEdges = grid->getDownArray(downTypes[n])->getDownCells(downIds[n]);
12012 const unsigned char* dTypes = grid->getDownArray(downTypes[n])->getDownTypes(downIds[n]);
12013 for (int i = 0; i < nbEdges; i++)
12015 int vtkEdgeId = grid->getDownArray(dTypes[i])->getVtkCellId(dEdges[i]);
12016 if (vtkEdgeId >= 0)
12017 sgrpei->Add(meshDS->fromVtkToSmds(vtkEdgeId));
12021 else if (neighborDim == 2) // skin of the volume
12023 DownIdType face(downIds[n], downTypes[n]);
12024 skinFaces[face] = vtkId;
12025 int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
12026 if (vtkFaceId >= 0)
12027 sgrps->Add(meshDS->fromVtkToSmds(vtkFaceId));
12032 // --- identify the edges constituting the wire of each subshape on the skin
12033 // define polylines with the nodes of edges, equivalent to wires
12034 // project polylines on subshapes, and partition, to get geom faces
12036 std::map<int, std::set<int> > shapeIdToVtkIdSet; // shapeId --> set of vtkId on skin
12037 std::set<int> emptySet;
12039 std::set<int> shapeIds;
12041 SMDS_ElemIteratorPtr itelem = sgrps->GetElements();
12042 while (itelem->more())
12044 const SMDS_MeshElement *elem = itelem->next();
12045 int shapeId = elem->getshapeId();
12046 int vtkId = elem->getVtkId();
12047 if (!shapeIdToVtkIdSet.count(shapeId))
12049 shapeIdToVtkIdSet[shapeId] = emptySet;
12050 shapeIds.insert(shapeId);
12052 shapeIdToVtkIdSet[shapeId].insert(vtkId);
12055 std::map<int, std::set<DownIdType, DownIdCompare> > shapeIdToEdges; // shapeId --> set of downward edges
12056 std::set<DownIdType, DownIdCompare> emptyEdges;
12057 emptyEdges.clear();
12059 std::map<int, std::set<int> >::iterator itShape = shapeIdToVtkIdSet.begin();
12060 for (; itShape != shapeIdToVtkIdSet.end(); ++itShape)
12062 int shapeId = itShape->first;
12063 MESSAGE(" --- Shape ID --- "<< shapeId);
12064 shapeIdToEdges[shapeId] = emptyEdges;
12066 std::vector<int> nodesEdges;
12068 std::set<int>::iterator its = itShape->second.begin();
12069 for (; its != itShape->second.end(); ++its)
12072 MESSAGE(" " << vtkId);
12073 int neighborsVtkIds[NBMAXNEIGHBORS];
12074 int downIds[NBMAXNEIGHBORS];
12075 unsigned char downTypes[NBMAXNEIGHBORS];
12076 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
12077 for (int n = 0; n < nbNeighbors; n++)
12079 if (neighborsVtkIds[n]<0) // only smds faces are considered as neighbors here
12081 int smdsId = meshDS->fromVtkToSmds(neighborsVtkIds[n]);
12082 const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
12083 if ( shapeIds.count(elem->getshapeId()) && !sgrps->Contains(elem)) // edge : neighbor in the set of shape, not in the group
12085 DownIdType edge(downIds[n], downTypes[n]);
12086 if (!shapeIdToEdges[shapeId].count(edge))
12088 shapeIdToEdges[shapeId].insert(edge);
12090 int nbNodes = grid->getDownArray(downTypes[n])->getNodes(downIds[n],vtkNodeId);
12091 nodesEdges.push_back(vtkNodeId[0]);
12092 nodesEdges.push_back(vtkNodeId[nbNodes-1]);
12093 MESSAGE(" --- nodes " << vtkNodeId[0]+1 << " " << vtkNodeId[nbNodes-1]+1);
12099 std::list<int> order;
12101 if (nodesEdges.size() > 0)
12103 order.push_back(nodesEdges[0]); MESSAGE(" --- back " << order.back()+1); // SMDS id = VTK id + 1;
12104 nodesEdges[0] = -1;
12105 order.push_back(nodesEdges[1]); MESSAGE(" --- back " << order.back()+1);
12106 nodesEdges[1] = -1; // do not reuse this edge
12110 int nodeTofind = order.back(); // try first to push back
12112 for (i = 0; i<nodesEdges.size(); i++)
12113 if (nodesEdges[i] == nodeTofind)
12115 if (i == nodesEdges.size())
12116 found = false; // no follower found on back
12119 if (i%2) // odd ==> use the previous one
12120 if (nodesEdges[i-1] < 0)
12124 order.push_back(nodesEdges[i-1]); MESSAGE(" --- back " << order.back()+1);
12125 nodesEdges[i-1] = -1;
12127 else // even ==> use the next one
12128 if (nodesEdges[i+1] < 0)
12132 order.push_back(nodesEdges[i+1]); MESSAGE(" --- back " << order.back()+1);
12133 nodesEdges[i+1] = -1;
12138 // try to push front
12140 nodeTofind = order.front(); // try to push front
12141 for (i = 0; i<nodesEdges.size(); i++)
12142 if (nodesEdges[i] == nodeTofind)
12144 if (i == nodesEdges.size())
12146 found = false; // no predecessor found on front
12149 if (i%2) // odd ==> use the previous one
12150 if (nodesEdges[i-1] < 0)
12154 order.push_front(nodesEdges[i-1]); MESSAGE(" --- front " << order.front()+1);
12155 nodesEdges[i-1] = -1;
12157 else // even ==> use the next one
12158 if (nodesEdges[i+1] < 0)
12162 order.push_front(nodesEdges[i+1]); MESSAGE(" --- front " << order.front()+1);
12163 nodesEdges[i+1] = -1;
12169 std::vector<int> nodes;
12170 nodes.push_back(shapeId);
12171 std::list<int>::iterator itl = order.begin();
12172 for (; itl != order.end(); itl++)
12174 nodes.push_back((*itl) + 1); // SMDS id = VTK id + 1;
12175 MESSAGE(" ordered node " << nodes[nodes.size()-1]);
12177 listOfListOfNodes.push_back(nodes);
12180 // partition geom faces with blocFissure
12181 // mesh blocFissure and geom faces of the skin (external wires given, triangle algo to choose)
12182 // mesh volume around blocFissure (skin triangles and quadrangle given, tetra algo to choose)
12188 //================================================================================
12190 * \brief Generates skin mesh (containing 2D cells) from 3D mesh
12191 * The created 2D mesh elements based on nodes of free faces of boundary volumes
12192 * \return TRUE if operation has been completed successfully, FALSE otherwise
12194 //================================================================================
12196 bool SMESH_MeshEditor::Make2DMeshFrom3D()
12198 // iterates on volume elements and detect all free faces on them
12199 SMESHDS_Mesh* aMesh = GetMeshDS();
12203 ElemFeatures faceType( SMDSAbs_Face );
12204 int nbFree = 0, nbExisted = 0, nbCreated = 0;
12205 SMDS_VolumeIteratorPtr vIt = aMesh->volumesIterator();
12208 const SMDS_MeshVolume* volume = vIt->next();
12209 SMDS_VolumeTool vTool( volume, /*ignoreCentralNodes=*/false );
12210 vTool.SetExternalNormal();
12211 const int iQuad = volume->IsQuadratic();
12212 faceType.SetQuad( iQuad );
12213 for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12215 if (!vTool.IsFreeFace(iface))
12218 vector<const SMDS_MeshNode *> nodes;
12219 int nbFaceNodes = vTool.NbFaceNodes(iface);
12220 const SMDS_MeshNode** faceNodes = vTool.GetFaceNodes(iface);
12222 for ( ; inode < nbFaceNodes; inode += iQuad+1)
12223 nodes.push_back(faceNodes[inode]);
12225 if (iQuad) // add medium nodes
12227 for ( inode = 1; inode < nbFaceNodes; inode += 2)
12228 nodes.push_back(faceNodes[inode]);
12229 if ( nbFaceNodes == 9 ) // bi-quadratic quad
12230 nodes.push_back(faceNodes[8]);
12232 // add new face based on volume nodes
12233 if (aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false) )
12235 nbExisted++; // face already exsist
12239 AddElement( nodes, faceType.SetPoly( nbFaceNodes/(iQuad+1) > 4 ));
12244 return ( nbFree == ( nbExisted + nbCreated ));
12249 inline const SMDS_MeshNode* getNodeWithSameID(SMESHDS_Mesh* mesh, const SMDS_MeshNode* node)
12251 if ( const SMDS_MeshNode* n = mesh->FindNode( node->GetID() ))
12253 return mesh->AddNodeWithID( node->X(),node->Y(),node->Z(), node->GetID() );
12256 //================================================================================
12258 * \brief Creates missing boundary elements
12259 * \param elements - elements whose boundary is to be checked
12260 * \param dimension - defines type of boundary elements to create
12261 * \param group - a group to store created boundary elements in
12262 * \param targetMesh - a mesh to store created boundary elements in
12263 * \param toCopyElements - if true, the checked elements will be copied into the targetMesh
12264 * \param toCopyExistingBoundary - if true, not only new but also pre-existing
12265 * boundary elements will be copied into the targetMesh
12266 * \param toAddExistingBondary - if true, not only new but also pre-existing
12267 * boundary elements will be added into the new group
12268 * \param aroundElements - if true, elements will be created on boundary of given
12269 * elements else, on boundary of the whole mesh.
12270 * \return nb of added boundary elements
12272 //================================================================================
12274 int SMESH_MeshEditor::MakeBoundaryMesh(const TIDSortedElemSet& elements,
12275 Bnd_Dimension dimension,
12276 SMESH_Group* group/*=0*/,
12277 SMESH_Mesh* targetMesh/*=0*/,
12278 bool toCopyElements/*=false*/,
12279 bool toCopyExistingBoundary/*=false*/,
12280 bool toAddExistingBondary/*= false*/,
12281 bool aroundElements/*= false*/)
12283 SMDSAbs_ElementType missType = (dimension == BND_2DFROM3D) ? SMDSAbs_Face : SMDSAbs_Edge;
12284 SMDSAbs_ElementType elemType = (dimension == BND_1DFROM2D) ? SMDSAbs_Face : SMDSAbs_Volume;
12285 // hope that all elements are of the same type, do not check them all
12286 if ( !elements.empty() && (*elements.begin())->GetType() != elemType )
12287 throw SALOME_Exception(LOCALIZED("wrong element type"));
12290 toCopyElements = toCopyExistingBoundary = false;
12292 SMESH_MeshEditor tgtEditor( targetMesh ? targetMesh : myMesh );
12293 SMESHDS_Mesh* aMesh = GetMeshDS(), *tgtMeshDS = tgtEditor.GetMeshDS();
12294 int nbAddedBnd = 0;
12296 // editor adding present bnd elements and optionally holding elements to add to the group
12297 SMESH_MeshEditor* presentEditor;
12298 SMESH_MeshEditor tgtEditor2( tgtEditor.GetMesh() );
12299 presentEditor = toAddExistingBondary ? &tgtEditor : &tgtEditor2;
12301 SMESH_MesherHelper helper( *myMesh );
12302 const TopAbs_ShapeEnum missShapeType = ( missType==SMDSAbs_Face ? TopAbs_FACE : TopAbs_EDGE );
12303 SMDS_VolumeTool vTool;
12304 TIDSortedElemSet avoidSet;
12305 const TIDSortedElemSet emptySet, *elemSet = aroundElements ? &elements : &emptySet;
12308 typedef vector<const SMDS_MeshNode*> TConnectivity;
12309 TConnectivity tgtNodes;
12310 ElemFeatures elemKind( missType );
12312 SMDS_ElemIteratorPtr eIt;
12313 if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
12314 else eIt = elemSetIterator( elements );
12316 while (eIt->more())
12318 const SMDS_MeshElement* elem = eIt->next();
12319 const int iQuad = elem->IsQuadratic();
12320 elemKind.SetQuad( iQuad );
12322 // ------------------------------------------------------------------------------------
12323 // 1. For an elem, get present bnd elements and connectivities of missing bnd elements
12324 // ------------------------------------------------------------------------------------
12325 vector<const SMDS_MeshElement*> presentBndElems;
12326 vector<TConnectivity> missingBndElems;
12327 TConnectivity nodes, elemNodes;
12328 if ( vTool.Set(elem, /*ignoreCentralNodes=*/true) ) // elem is a volume --------------
12330 vTool.SetExternalNormal();
12331 const SMDS_MeshElement* otherVol = 0;
12332 for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12334 if ( !vTool.IsFreeFace(iface, &otherVol) &&
12335 ( !aroundElements || elements.count( otherVol )))
12337 const SMDS_MeshNode** nn = vTool.GetFaceNodes(iface);
12338 const size_t nbFaceNodes = vTool.NbFaceNodes (iface);
12339 if ( missType == SMDSAbs_Edge ) // boundary edges
12341 nodes.resize( 2+iQuad );
12342 for ( int i = 0; i < nbFaceNodes; i += 1+iQuad)
12344 for ( int j = 0; j < nodes.size(); ++j )
12346 if ( const SMDS_MeshElement* edge =
12347 aMesh->FindElement(nodes,SMDSAbs_Edge,/*noMedium=*/false))
12348 presentBndElems.push_back( edge );
12350 missingBndElems.push_back( nodes );
12353 else // boundary face
12356 for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12357 nodes.push_back( nn[inode] ); // add corner nodes
12359 for ( inode = 1; inode < nbFaceNodes; inode += 2)
12360 nodes.push_back( nn[inode] ); // add medium nodes
12361 int iCenter = vTool.GetCenterNodeIndex(iface); // for HEX27
12363 nodes.push_back( vTool.GetNodes()[ iCenter ] );
12365 if (const SMDS_MeshElement * f = aMesh->FindElement( nodes,
12366 SMDSAbs_Face, /*noMedium=*/false ))
12367 presentBndElems.push_back( f );
12369 missingBndElems.push_back( nodes );
12371 if ( targetMesh != myMesh )
12373 // add 1D elements on face boundary to be added to a new mesh
12374 const SMDS_MeshElement* edge;
12375 for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12378 edge = aMesh->FindEdge( nn[inode], nn[inode+1], nn[inode+2]);
12380 edge = aMesh->FindEdge( nn[inode], nn[inode+1]);
12381 if ( edge && avoidSet.insert( edge ).second )
12382 presentBndElems.push_back( edge );
12388 else if ( elem->GetType() == SMDSAbs_Face ) // elem is a face ------------------------
12390 avoidSet.clear(), avoidSet.insert( elem );
12391 elemNodes.assign( SMDS_MeshElement::iterator( elem->interlacedNodesElemIterator() ),
12392 SMDS_MeshElement::iterator() );
12393 elemNodes.push_back( elemNodes[0] );
12394 nodes.resize( 2 + iQuad );
12395 const int nbLinks = elem->NbCornerNodes();
12396 for ( int i = 0, iN = 0; i < nbLinks; i++, iN += 1+iQuad )
12398 nodes[0] = elemNodes[iN];
12399 nodes[1] = elemNodes[iN+1+iQuad];
12400 if ( SMESH_MeshAlgos::FindFaceInSet( nodes[0], nodes[1], *elemSet, avoidSet))
12401 continue; // not free link
12403 if ( iQuad ) nodes[2] = elemNodes[iN+1];
12404 if ( const SMDS_MeshElement* edge =
12405 aMesh->FindElement(nodes,SMDSAbs_Edge,/*noMedium=*/false))
12406 presentBndElems.push_back( edge );
12408 missingBndElems.push_back( nodes );
12412 // ---------------------------------
12413 // 2. Add missing boundary elements
12414 // ---------------------------------
12415 if ( targetMesh != myMesh )
12416 // instead of making a map of nodes in this mesh and targetMesh,
12417 // we create nodes with same IDs.
12418 for ( size_t i = 0; i < missingBndElems.size(); ++i )
12420 TConnectivity& srcNodes = missingBndElems[i];
12421 tgtNodes.resize( srcNodes.size() );
12422 for ( inode = 0; inode < srcNodes.size(); ++inode )
12423 tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, srcNodes[inode] );
12424 if ( aroundElements && tgtEditor.GetMeshDS()->FindElement( tgtNodes,
12426 /*noMedium=*/false))
12428 tgtEditor.AddElement( tgtNodes, elemKind.SetPoly( tgtNodes.size()/(iQuad+1) > 4 ));
12432 for ( int i = 0; i < missingBndElems.size(); ++i )
12434 TConnectivity& nodes = missingBndElems[i];
12435 if ( aroundElements && tgtEditor.GetMeshDS()->FindElement( nodes,
12437 /*noMedium=*/false))
12439 SMDS_MeshElement* newElem =
12440 tgtEditor.AddElement( nodes, elemKind.SetPoly( nodes.size()/(iQuad+1) > 4 ));
12441 nbAddedBnd += bool( newElem );
12443 // try to set a new element to a shape
12444 if ( myMesh->HasShapeToMesh() )
12447 set< pair<TopAbs_ShapeEnum, int > > mediumShapes;
12448 const size_t nbN = nodes.size() / (iQuad+1 );
12449 for ( inode = 0; inode < nbN && ok; ++inode )
12451 pair<int, TopAbs_ShapeEnum> i_stype =
12452 helper.GetMediumPos( nodes[inode], nodes[(inode+1)%nbN]);
12453 if (( ok = ( i_stype.first > 0 && i_stype.second >= TopAbs_FACE )))
12454 mediumShapes.insert( make_pair ( i_stype.second, i_stype.first ));
12456 if ( ok && mediumShapes.size() > 1 )
12458 set< pair<TopAbs_ShapeEnum, int > >::iterator stype_i = mediumShapes.begin();
12459 pair<TopAbs_ShapeEnum, int> stype_i_0 = *stype_i;
12460 for ( ++stype_i; stype_i != mediumShapes.end() && ok; ++stype_i )
12462 if (( ok = ( stype_i->first != stype_i_0.first )))
12463 ok = helper.IsSubShape( aMesh->IndexToShape( stype_i->second ),
12464 aMesh->IndexToShape( stype_i_0.second ));
12467 if ( ok && mediumShapes.begin()->first == missShapeType )
12468 aMesh->SetMeshElementOnShape( newElem, mediumShapes.begin()->second );
12472 // ----------------------------------
12473 // 3. Copy present boundary elements
12474 // ----------------------------------
12475 if ( toCopyExistingBoundary )
12476 for ( int i = 0 ; i < presentBndElems.size(); ++i )
12478 const SMDS_MeshElement* e = presentBndElems[i];
12479 tgtNodes.resize( e->NbNodes() );
12480 for ( inode = 0; inode < nodes.size(); ++inode )
12481 tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, e->GetNode(inode) );
12482 presentEditor->AddElement( tgtNodes, elemKind.Init( e ));
12484 else // store present elements to add them to a group
12485 for ( int i = 0 ; i < presentBndElems.size(); ++i )
12487 presentEditor->myLastCreatedElems.Append( presentBndElems[i] );
12490 } // loop on given elements
12492 // ---------------------------------------------
12493 // 4. Fill group with boundary elements
12494 // ---------------------------------------------
12497 if ( SMESHDS_Group* g = dynamic_cast<SMESHDS_Group*>( group->GetGroupDS() ))
12498 for ( int i = 0; i < tgtEditor.myLastCreatedElems.Size(); ++i )
12499 g->SMDSGroup().Add( tgtEditor.myLastCreatedElems( i+1 ));
12501 tgtEditor.myLastCreatedElems.Clear();
12502 tgtEditor2.myLastCreatedElems.Clear();
12504 // -----------------------
12505 // 5. Copy given elements
12506 // -----------------------
12507 if ( toCopyElements && targetMesh != myMesh )
12509 if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
12510 else eIt = elemSetIterator( elements );
12511 while (eIt->more())
12513 const SMDS_MeshElement* elem = eIt->next();
12514 tgtNodes.resize( elem->NbNodes() );
12515 for ( inode = 0; inode < tgtNodes.size(); ++inode )
12516 tgtNodes[inode] = getNodeWithSameID( tgtMeshDS, elem->GetNode(inode) );
12517 tgtEditor.AddElement( tgtNodes, elemKind.Init( elem ));
12519 tgtEditor.myLastCreatedElems.Clear();
12525 //================================================================================
12527 * \brief Copy node position and set \a to node on the same geometry
12529 //================================================================================
12531 void SMESH_MeshEditor::copyPosition( const SMDS_MeshNode* from,
12532 const SMDS_MeshNode* to )
12534 if ( !from || !to ) return;
12536 SMDS_PositionPtr pos = from->GetPosition();
12537 if ( !pos || from->getshapeId() < 1 ) return;
12539 switch ( pos->GetTypeOfPosition() )
12541 case SMDS_TOP_3DSPACE: break;
12543 case SMDS_TOP_FACE:
12545 const SMDS_FacePosition* fPos = static_cast< const SMDS_FacePosition* >( pos );
12546 GetMeshDS()->SetNodeOnFace( to, from->getshapeId(),
12547 fPos->GetUParameter(), fPos->GetVParameter() );
12550 case SMDS_TOP_EDGE:
12552 // WARNING: it is dangerous to set equal nodes on one EDGE!!!!!!!!
12553 const SMDS_EdgePosition* ePos = static_cast< const SMDS_EdgePosition* >( pos );
12554 GetMeshDS()->SetNodeOnEdge( to, from->getshapeId(), ePos->GetUParameter() );
12557 case SMDS_TOP_VERTEX:
12559 GetMeshDS()->SetNodeOnVertex( to, from->getshapeId() );
12562 case SMDS_TOP_UNSPEC: