1 // Copyright (C) 2007-2013 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.
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"
54 #include <BRepAdaptor_Surface.hxx>
55 #include <BRepBuilderAPI_MakeEdge.hxx>
56 #include <BRepClass3d_SolidClassifier.hxx>
57 #include <BRep_Tool.hxx>
59 #include <Extrema_GenExtPS.hxx>
60 #include <Extrema_POnCurv.hxx>
61 #include <Extrema_POnSurf.hxx>
62 #include <Geom2d_Curve.hxx>
63 #include <GeomAdaptor_Surface.hxx>
64 #include <Geom_Curve.hxx>
65 #include <Geom_Surface.hxx>
66 #include <Precision.hxx>
67 #include <TColStd_ListOfInteger.hxx>
68 #include <TopAbs_State.hxx>
70 #include <TopExp_Explorer.hxx>
71 #include <TopTools_ListIteratorOfListOfShape.hxx>
72 #include <TopTools_ListOfShape.hxx>
73 #include <TopTools_SequenceOfShape.hxx>
75 #include <TopoDS_Face.hxx>
76 #include <TopoDS_Solid.hxx>
82 #include <gp_Trsf.hxx>
96 #include <boost/tuple/tuple.hpp>
98 #include <Standard_Failure.hxx>
99 #include <Standard_ErrorHandler.hxx>
101 #define cast2Node(elem) static_cast<const SMDS_MeshNode*>( elem )
104 using namespace SMESH::Controls;
108 SMDS_ElemIteratorPtr elemSetIterator( const TIDSortedElemSet& elements )
110 typedef SMDS_SetIterator< SMDS_pElement, TIDSortedElemSet::const_iterator> TSetIterator;
111 return SMDS_ElemIteratorPtr( new TSetIterator( elements.begin(), elements.end() ));
115 //=======================================================================
116 //function : SMESH_MeshEditor
118 //=======================================================================
120 SMESH_MeshEditor::SMESH_MeshEditor( SMESH_Mesh* theMesh )
121 :myMesh( theMesh ) // theMesh may be NULL
125 //================================================================================
127 * \brief Clears myLastCreatedNodes and myLastCreatedElems
129 //================================================================================
131 void SMESH_MeshEditor::CrearLastCreated()
133 myLastCreatedNodes.Clear();
134 myLastCreatedElems.Clear();
138 //=======================================================================
142 //=======================================================================
145 SMESH_MeshEditor::AddElement(const vector<const SMDS_MeshNode*> & node,
146 const SMDSAbs_ElementType type,
149 const double ballDiameter)
151 //MESSAGE("AddElement " <<node.size() << " " << type << " " << isPoly << " " << ID);
152 SMDS_MeshElement* e = 0;
153 int nbnode = node.size();
154 SMESHDS_Mesh* mesh = GetMeshDS();
159 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], ID);
160 else e = mesh->AddFace (node[0], node[1], node[2] );
162 else if (nbnode == 4) {
163 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3], ID);
164 else e = mesh->AddFace (node[0], node[1], node[2], node[3] );
166 else if (nbnode == 6) {
167 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
168 node[4], node[5], ID);
169 else e = mesh->AddFace (node[0], node[1], node[2], node[3],
172 else if (nbnode == 7) {
173 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
174 node[4], node[5], node[6], ID);
175 else e = mesh->AddFace (node[0], node[1], node[2], node[3],
176 node[4], node[5], node[6] );
178 else if (nbnode == 8) {
179 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
180 node[4], node[5], node[6], node[7], ID);
181 else e = mesh->AddFace (node[0], node[1], node[2], node[3],
182 node[4], node[5], node[6], node[7] );
184 else if (nbnode == 9) {
185 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
186 node[4], node[5], node[6], node[7], node[8], ID);
187 else e = mesh->AddFace (node[0], node[1], node[2], node[3],
188 node[4], node[5], node[6], node[7], node[8] );
191 if ( ID >= 1 ) e = mesh->AddPolygonalFaceWithID(node, ID);
192 else e = mesh->AddPolygonalFace (node );
199 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3], ID);
200 else e = mesh->AddVolume (node[0], node[1], node[2], node[3] );
202 else if (nbnode == 5) {
203 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
205 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
208 else if (nbnode == 6) {
209 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
210 node[4], node[5], ID);
211 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
214 else if (nbnode == 8) {
215 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
216 node[4], node[5], node[6], node[7], ID);
217 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
218 node[4], node[5], node[6], node[7] );
220 else if (nbnode == 10) {
221 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
222 node[4], node[5], node[6], node[7],
223 node[8], node[9], ID);
224 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
225 node[4], node[5], node[6], node[7],
228 else if (nbnode == 12) {
229 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
230 node[4], node[5], node[6], node[7],
231 node[8], node[9], node[10], node[11], ID);
232 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
233 node[4], node[5], node[6], node[7],
234 node[8], node[9], node[10], node[11] );
236 else if (nbnode == 13) {
237 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
238 node[4], node[5], node[6], node[7],
239 node[8], node[9], node[10],node[11],
241 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
242 node[4], node[5], node[6], node[7],
243 node[8], node[9], node[10],node[11],
246 else if (nbnode == 15) {
247 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
248 node[4], node[5], node[6], node[7],
249 node[8], node[9], node[10],node[11],
250 node[12],node[13],node[14],ID);
251 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
252 node[4], node[5], node[6], node[7],
253 node[8], node[9], node[10],node[11],
254 node[12],node[13],node[14] );
256 else if (nbnode == 20) {
257 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
258 node[4], node[5], node[6], node[7],
259 node[8], node[9], node[10],node[11],
260 node[12],node[13],node[14],node[15],
261 node[16],node[17],node[18],node[19],ID);
262 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
263 node[4], node[5], node[6], node[7],
264 node[8], node[9], node[10],node[11],
265 node[12],node[13],node[14],node[15],
266 node[16],node[17],node[18],node[19] );
268 else if (nbnode == 27) {
269 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
270 node[4], node[5], node[6], node[7],
271 node[8], node[9], node[10],node[11],
272 node[12],node[13],node[14],node[15],
273 node[16],node[17],node[18],node[19],
274 node[20],node[21],node[22],node[23],
275 node[24],node[25],node[26], ID);
276 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
277 node[4], node[5], node[6], node[7],
278 node[8], node[9], node[10],node[11],
279 node[12],node[13],node[14],node[15],
280 node[16],node[17],node[18],node[19],
281 node[20],node[21],node[22],node[23],
282 node[24],node[25],node[26] );
289 if ( ID >= 1 ) e = mesh->AddEdgeWithID(node[0], node[1], ID);
290 else e = mesh->AddEdge (node[0], node[1] );
292 else if ( nbnode == 3 ) {
293 if ( ID >= 1 ) e = mesh->AddEdgeWithID(node[0], node[1], node[2], ID);
294 else e = mesh->AddEdge (node[0], node[1], node[2] );
298 case SMDSAbs_0DElement:
300 if ( ID >= 1 ) e = mesh->Add0DElementWithID(node[0], ID);
301 else e = mesh->Add0DElement (node[0] );
306 if ( ID >= 1 ) e = mesh->AddNodeWithID(node[0]->X(), node[0]->Y(), node[0]->Z(), ID);
307 else e = mesh->AddNode (node[0]->X(), node[0]->Y(), node[0]->Z());
311 if ( ID >= 1 ) e = mesh->AddBallWithID(node[0], ballDiameter, ID);
312 else e = mesh->AddBall (node[0], ballDiameter);
317 if ( e ) myLastCreatedElems.Append( e );
321 //=======================================================================
325 //=======================================================================
327 SMDS_MeshElement* SMESH_MeshEditor::AddElement(const vector<int> & nodeIDs,
328 const SMDSAbs_ElementType type,
332 vector<const SMDS_MeshNode*> nodes;
333 nodes.reserve( nodeIDs.size() );
334 vector<int>::const_iterator id = nodeIDs.begin();
335 while ( id != nodeIDs.end() ) {
336 if ( const SMDS_MeshNode* node = GetMeshDS()->FindNode( *id++ ))
337 nodes.push_back( node );
341 return AddElement( nodes, type, isPoly, ID );
344 //=======================================================================
346 //purpose : Remove a node or an element.
347 // Modify a compute state of sub-meshes which become empty
348 //=======================================================================
350 int SMESH_MeshEditor::Remove (const list< int >& theIDs,
353 myLastCreatedElems.Clear();
354 myLastCreatedNodes.Clear();
356 SMESHDS_Mesh* aMesh = GetMeshDS();
357 set< SMESH_subMesh *> smmap;
360 list<int>::const_iterator it = theIDs.begin();
361 for ( ; it != theIDs.end(); it++ ) {
362 const SMDS_MeshElement * elem;
364 elem = aMesh->FindNode( *it );
366 elem = aMesh->FindElement( *it );
370 // Notify VERTEX sub-meshes about modification
372 const SMDS_MeshNode* node = cast2Node( elem );
373 if ( node->GetPosition()->GetTypeOfPosition() == SMDS_TOP_VERTEX )
374 if ( int aShapeID = node->getshapeId() )
375 if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( aShapeID ) )
378 // Find sub-meshes to notify about modification
379 // SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
380 // while ( nodeIt->more() ) {
381 // const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
382 // const SMDS_PositionPtr& aPosition = node->GetPosition();
383 // if ( aPosition.get() ) {
384 // if ( int aShapeID = aPosition->GetShapeId() ) {
385 // if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( aShapeID ) )
386 // smmap.insert( sm );
393 aMesh->RemoveNode( static_cast< const SMDS_MeshNode* >( elem ));
395 aMesh->RemoveElement( elem );
399 // Notify sub-meshes about modification
400 if ( !smmap.empty() ) {
401 set< SMESH_subMesh *>::iterator smIt;
402 for ( smIt = smmap.begin(); smIt != smmap.end(); smIt++ )
403 (*smIt)->ComputeStateEngine( SMESH_subMesh::MESH_ENTITY_REMOVED );
406 // // Check if the whole mesh becomes empty
407 // if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( 1 ) )
408 // sm->ComputeStateEngine( SMESH_subMesh::CHECK_COMPUTE_STATE );
413 //================================================================================
415 * \brief Create 0D elements on all nodes of the given object except those
416 * nodes on which a 0D element already exists.
417 * \param elements - Elements on whose nodes to create 0D elements; if empty,
418 * the all mesh is treated
419 * \param all0DElems - returns all 0D elements found or created on nodes of \a elements
421 //================================================================================
423 void SMESH_MeshEditor::Create0DElementsOnAllNodes( const TIDSortedElemSet& elements,
424 TIDSortedElemSet& all0DElems )
426 SMDS_ElemIteratorPtr elemIt;
427 if ( elements.empty() )
428 elemIt = GetMeshDS()->elementsIterator( SMDSAbs_Node );
430 elemIt = elemSetIterator( elements );
432 while ( elemIt->more() )
434 const SMDS_MeshElement* e = elemIt->next();
435 SMDS_ElemIteratorPtr nodeIt = e->nodesIterator();
436 while ( nodeIt->more() )
438 const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
439 SMDS_ElemIteratorPtr it0D = n->GetInverseElementIterator( SMDSAbs_0DElement );
441 all0DElems.insert( it0D->next() );
443 myLastCreatedElems.Append( GetMeshDS()->Add0DElement( n ));
444 all0DElems.insert( myLastCreatedElems.Last() );
450 //=======================================================================
451 //function : FindShape
452 //purpose : Return an index of the shape theElem is on
453 // or zero if a shape not found
454 //=======================================================================
456 int SMESH_MeshEditor::FindShape (const SMDS_MeshElement * theElem)
458 myLastCreatedElems.Clear();
459 myLastCreatedNodes.Clear();
461 SMESHDS_Mesh * aMesh = GetMeshDS();
462 if ( aMesh->ShapeToMesh().IsNull() )
465 int aShapeID = theElem->getshapeId();
469 if ( SMESHDS_SubMesh * sm = aMesh->MeshElements( aShapeID ))
470 if ( sm->Contains( theElem ))
473 if ( theElem->GetType() == SMDSAbs_Node ) {
474 MESSAGE( ":( Error: invalid myShapeId of node " << theElem->GetID() );
477 MESSAGE( ":( Error: invalid myShapeId of element " << theElem->GetID() );
480 TopoDS_Shape aShape; // the shape a node of theElem is on
481 if ( theElem->GetType() != SMDSAbs_Node )
483 SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator();
484 while ( nodeIt->more() ) {
485 const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
486 if ((aShapeID = node->getshapeId()) > 0) {
487 if ( SMESHDS_SubMesh * sm = aMesh->MeshElements( aShapeID ) ) {
488 if ( sm->Contains( theElem ))
490 if ( aShape.IsNull() )
491 aShape = aMesh->IndexToShape( aShapeID );
497 // None of nodes is on a proper shape,
498 // find the shape among ancestors of aShape on which a node is
499 if ( !aShape.IsNull() ) {
500 TopTools_ListIteratorOfListOfShape ancIt( GetMesh()->GetAncestors( aShape ));
501 for ( ; ancIt.More(); ancIt.Next() ) {
502 SMESHDS_SubMesh * sm = aMesh->MeshElements( ancIt.Value() );
503 if ( sm && sm->Contains( theElem ))
504 return aMesh->ShapeToIndex( ancIt.Value() );
509 const map<int,SMESHDS_SubMesh*>& id2sm = GetMeshDS()->SubMeshes();
510 map<int,SMESHDS_SubMesh*>::const_iterator id_sm = id2sm.begin();
511 for ( ; id_sm != id2sm.end(); ++id_sm )
512 if ( id_sm->second->Contains( theElem ))
516 //MESSAGE ("::FindShape() - SHAPE NOT FOUND")
520 //=======================================================================
521 //function : IsMedium
523 //=======================================================================
525 bool SMESH_MeshEditor::IsMedium(const SMDS_MeshNode* node,
526 const SMDSAbs_ElementType typeToCheck)
528 bool isMedium = false;
529 SMDS_ElemIteratorPtr it = node->GetInverseElementIterator(typeToCheck);
530 while (it->more() && !isMedium ) {
531 const SMDS_MeshElement* elem = it->next();
532 isMedium = elem->IsMediumNode(node);
537 //=======================================================================
538 //function : shiftNodesQuadTria
539 //purpose : Shift nodes in the array corresponded to quadratic triangle
540 // example: (0,1,2,3,4,5) -> (1,2,0,4,5,3)
541 //=======================================================================
543 static void shiftNodesQuadTria(vector< const SMDS_MeshNode* >& aNodes)
545 const SMDS_MeshNode* nd1 = aNodes[0];
546 aNodes[0] = aNodes[1];
547 aNodes[1] = aNodes[2];
549 const SMDS_MeshNode* nd2 = aNodes[3];
550 aNodes[3] = aNodes[4];
551 aNodes[4] = aNodes[5];
555 //=======================================================================
556 //function : nbEdgeConnectivity
557 //purpose : return number of the edges connected with the theNode.
558 // if theEdges has connections with the other type of the
559 // elements, return -1
560 //=======================================================================
562 static int nbEdgeConnectivity(const SMDS_MeshNode* theNode)
564 // SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator();
566 // while(elemIt->more()) {
571 return theNode->NbInverseElements();
574 //=======================================================================
575 //function : getNodesFromTwoTria
577 //=======================================================================
579 static bool getNodesFromTwoTria(const SMDS_MeshElement * theTria1,
580 const SMDS_MeshElement * theTria2,
581 vector< const SMDS_MeshNode*>& N1,
582 vector< const SMDS_MeshNode*>& N2)
584 N1.assign( theTria1->begin_nodes(), theTria1->end_nodes() );
585 if ( N1.size() < 6 ) return false;
586 N2.assign( theTria2->begin_nodes(), theTria2->end_nodes() );
587 if ( N2.size() < 6 ) return false;
589 int sames[3] = {-1,-1,-1};
601 if(nbsames!=2) return false;
603 shiftNodesQuadTria(N1);
605 shiftNodesQuadTria(N1);
608 i = sames[0] + sames[1] + sames[2];
610 shiftNodesQuadTria(N2);
612 // now we receive following N1 and N2 (using numeration as in the image below)
613 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
614 // i.e. first nodes from both arrays form a new diagonal
618 //=======================================================================
619 //function : InverseDiag
620 //purpose : Replace two neighbour triangles with ones built on the same 4 nodes
621 // but having other common link.
622 // Return False if args are improper
623 //=======================================================================
625 bool SMESH_MeshEditor::InverseDiag (const SMDS_MeshElement * theTria1,
626 const SMDS_MeshElement * theTria2 )
628 MESSAGE("InverseDiag");
629 myLastCreatedElems.Clear();
630 myLastCreatedNodes.Clear();
632 if (!theTria1 || !theTria2)
635 const SMDS_VtkFace* F1 = dynamic_cast<const SMDS_VtkFace*>( theTria1 );
636 if (!F1) return false;
637 const SMDS_VtkFace* F2 = dynamic_cast<const SMDS_VtkFace*>( theTria2 );
638 if (!F2) return false;
639 if ((theTria1->GetEntityType() == SMDSEntity_Triangle) &&
640 (theTria2->GetEntityType() == SMDSEntity_Triangle)) {
642 // 1 +--+ A theTria1: ( 1 A B ) A->2 ( 1 2 B ) 1 +--+ A
643 // | /| theTria2: ( B A 2 ) B->1 ( 1 A 2 ) |\ |
647 // put nodes in array and find out indices of the same ones
648 const SMDS_MeshNode* aNodes [6];
649 int sameInd [] = { 0, 0, 0, 0, 0, 0 };
651 SMDS_ElemIteratorPtr it = theTria1->nodesIterator();
652 while ( it->more() ) {
653 aNodes[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
655 if ( i > 2 ) // theTria2
656 // find same node of theTria1
657 for ( int j = 0; j < 3; j++ )
658 if ( aNodes[ i ] == aNodes[ j ]) {
667 return false; // theTria1 is not a triangle
668 it = theTria2->nodesIterator();
670 if ( i == 6 && it->more() )
671 return false; // theTria2 is not a triangle
674 // find indices of 1,2 and of A,B in theTria1
675 int iA = 0, iB = 0, i1 = 0, i2 = 0;
676 for ( i = 0; i < 6; i++ ) {
677 if ( sameInd [ i ] == 0 ) {
686 // nodes 1 and 2 should not be the same
687 if ( aNodes[ i1 ] == aNodes[ i2 ] )
691 aNodes[ iA ] = aNodes[ i2 ];
693 aNodes[ sameInd[ iB ]] = aNodes[ i1 ];
695 GetMeshDS()->ChangeElementNodes( theTria1, aNodes, 3 );
696 GetMeshDS()->ChangeElementNodes( theTria2, &aNodes[ 3 ], 3 );
700 } // end if(F1 && F2)
702 // check case of quadratic faces
703 if (theTria1->GetEntityType() != SMDSEntity_Quad_Triangle &&
704 theTria1->GetEntityType() != SMDSEntity_BiQuad_Triangle)
706 if (theTria2->GetEntityType() != SMDSEntity_Quad_Triangle&&
707 theTria2->GetEntityType() != SMDSEntity_BiQuad_Triangle)
711 // 1 +--+--+ 2 theTria1: (1 2 4 5 9 7) or (2 4 1 9 7 5) or (4 1 2 7 5 9)
712 // | /| theTria2: (2 3 4 6 8 9) or (3 4 2 8 9 6) or (4 2 3 9 6 8)
720 vector< const SMDS_MeshNode* > N1;
721 vector< const SMDS_MeshNode* > N2;
722 if(!getNodesFromTwoTria(theTria1,theTria2,N1,N2))
724 // now we receive following N1 and N2 (using numeration as above image)
725 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
726 // i.e. first nodes from both arrays determ new diagonal
728 vector< const SMDS_MeshNode*> N1new( N1.size() );
729 vector< const SMDS_MeshNode*> N2new( N2.size() );
730 N1new.back() = N1.back(); // central node of biquadratic
731 N2new.back() = N2.back();
732 N1new[0] = N1[0]; N2new[0] = N1[0];
733 N1new[1] = N2[0]; N2new[1] = N1[1];
734 N1new[2] = N2[1]; N2new[2] = N2[0];
735 N1new[3] = N1[4]; N2new[3] = N1[3];
736 N1new[4] = N2[3]; N2new[4] = N2[5];
737 N1new[5] = N1[5]; N2new[5] = N1[4];
738 // change nodes in faces
739 GetMeshDS()->ChangeElementNodes( theTria1, &N1new[0], N1new.size() );
740 GetMeshDS()->ChangeElementNodes( theTria2, &N2new[0], N2new.size() );
742 // move the central node of biquadratic triangle
743 SMESH_MesherHelper helper( *GetMesh() );
744 for ( int is2nd = 0; is2nd < 2; ++is2nd )
746 const SMDS_MeshElement* tria = is2nd ? theTria2 : theTria1;
747 vector< const SMDS_MeshNode*>& nodes = is2nd ? N2new : N1new;
748 if ( nodes.size() < 7 )
750 helper.SetSubShape( tria->getshapeId() );
751 const TopoDS_Face& F = TopoDS::Face( helper.GetSubShape() );
755 xyz = ( SMESH_TNodeXYZ( nodes[3] ) +
756 SMESH_TNodeXYZ( nodes[4] ) +
757 SMESH_TNodeXYZ( nodes[5] )) / 3.;
762 gp_XY uv = ( helper.GetNodeUV( F, nodes[3], nodes[2], &checkUV ) +
763 helper.GetNodeUV( F, nodes[4], nodes[0], &checkUV ) +
764 helper.GetNodeUV( F, nodes[5], nodes[1], &checkUV )) / 3.;
766 Handle(Geom_Surface) S = BRep_Tool::Surface(F,loc);
767 xyz = S->Value( uv.X(), uv.Y() );
768 xyz.Transform( loc );
769 if ( nodes[6]->GetPosition()->GetTypeOfPosition() == SMDS_TOP_FACE && // set UV
770 nodes[6]->getshapeId() > 0 )
771 GetMeshDS()->SetNodeOnFace( nodes[6], nodes[6]->getshapeId(), uv.X(), uv.Y() );
773 GetMeshDS()->MoveNode( nodes[6], xyz.X(), xyz.Y(), xyz.Z() );
778 //=======================================================================
779 //function : findTriangles
780 //purpose : find triangles sharing theNode1-theNode2 link
781 //=======================================================================
783 static bool findTriangles(const SMDS_MeshNode * theNode1,
784 const SMDS_MeshNode * theNode2,
785 const SMDS_MeshElement*& theTria1,
786 const SMDS_MeshElement*& theTria2)
788 if ( !theNode1 || !theNode2 ) return false;
790 theTria1 = theTria2 = 0;
792 set< const SMDS_MeshElement* > emap;
793 SMDS_ElemIteratorPtr it = theNode1->GetInverseElementIterator(SMDSAbs_Face);
795 const SMDS_MeshElement* elem = it->next();
796 if ( elem->NbCornerNodes() == 3 )
799 it = theNode2->GetInverseElementIterator(SMDSAbs_Face);
801 const SMDS_MeshElement* elem = it->next();
802 if ( emap.count( elem )) {
810 // theTria1 must be element with minimum ID
811 if ( theTria2->GetID() < theTria1->GetID() )
812 std::swap( theTria2, theTria1 );
820 //=======================================================================
821 //function : InverseDiag
822 //purpose : Replace two neighbour triangles sharing theNode1-theNode2 link
823 // with ones built on the same 4 nodes but having other common link.
824 // Return false if proper faces not found
825 //=======================================================================
827 bool SMESH_MeshEditor::InverseDiag (const SMDS_MeshNode * theNode1,
828 const SMDS_MeshNode * theNode2)
830 myLastCreatedElems.Clear();
831 myLastCreatedNodes.Clear();
833 MESSAGE( "::InverseDiag()" );
835 const SMDS_MeshElement *tr1, *tr2;
836 if ( !findTriangles( theNode1, theNode2, tr1, tr2 ))
839 const SMDS_VtkFace* F1 = dynamic_cast<const SMDS_VtkFace*>( tr1 );
840 if (!F1) return false;
841 const SMDS_VtkFace* F2 = dynamic_cast<const SMDS_VtkFace*>( tr2 );
842 if (!F2) return false;
843 if ((tr1->GetEntityType() == SMDSEntity_Triangle) &&
844 (tr2->GetEntityType() == SMDSEntity_Triangle)) {
846 // 1 +--+ A tr1: ( 1 A B ) A->2 ( 1 2 B ) 1 +--+ A
847 // | /| tr2: ( B A 2 ) B->1 ( 1 A 2 ) |\ |
851 // put nodes in array
852 // and find indices of 1,2 and of A in tr1 and of B in tr2
853 int i, iA1 = 0, i1 = 0;
854 const SMDS_MeshNode* aNodes1 [3];
855 SMDS_ElemIteratorPtr it;
856 for (i = 0, it = tr1->nodesIterator(); it->more(); i++ ) {
857 aNodes1[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
858 if ( aNodes1[ i ] == theNode1 )
859 iA1 = i; // node A in tr1
860 else if ( aNodes1[ i ] != theNode2 )
864 const SMDS_MeshNode* aNodes2 [3];
865 for (i = 0, it = tr2->nodesIterator(); it->more(); i++ ) {
866 aNodes2[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
867 if ( aNodes2[ i ] == theNode2 )
868 iB2 = i; // node B in tr2
869 else if ( aNodes2[ i ] != theNode1 )
873 // nodes 1 and 2 should not be the same
874 if ( aNodes1[ i1 ] == aNodes2[ i2 ] )
878 aNodes1[ iA1 ] = aNodes2[ i2 ];
880 aNodes2[ iB2 ] = aNodes1[ i1 ];
882 GetMeshDS()->ChangeElementNodes( tr1, aNodes1, 3 );
883 GetMeshDS()->ChangeElementNodes( tr2, aNodes2, 3 );
888 // check case of quadratic faces
889 return InverseDiag(tr1,tr2);
892 //=======================================================================
893 //function : getQuadrangleNodes
894 //purpose : fill theQuadNodes - nodes of a quadrangle resulting from
895 // fusion of triangles tr1 and tr2 having shared link on
896 // theNode1 and theNode2
897 //=======================================================================
899 bool getQuadrangleNodes(const SMDS_MeshNode * theQuadNodes [],
900 const SMDS_MeshNode * theNode1,
901 const SMDS_MeshNode * theNode2,
902 const SMDS_MeshElement * tr1,
903 const SMDS_MeshElement * tr2 )
905 if( tr1->NbNodes() != tr2->NbNodes() )
907 // find the 4-th node to insert into tr1
908 const SMDS_MeshNode* n4 = 0;
909 SMDS_ElemIteratorPtr it = tr2->nodesIterator();
911 while ( !n4 && i<3 ) {
912 const SMDS_MeshNode * n = cast2Node( it->next() );
914 bool isDiag = ( n == theNode1 || n == theNode2 );
918 // Make an array of nodes to be in a quadrangle
919 int iNode = 0, iFirstDiag = -1;
920 it = tr1->nodesIterator();
923 const SMDS_MeshNode * n = cast2Node( it->next() );
925 bool isDiag = ( n == theNode1 || n == theNode2 );
927 if ( iFirstDiag < 0 )
929 else if ( iNode - iFirstDiag == 1 )
930 theQuadNodes[ iNode++ ] = n4; // insert the 4-th node between diagonal nodes
932 else if ( n == n4 ) {
933 return false; // tr1 and tr2 should not have all the same nodes
935 theQuadNodes[ iNode++ ] = n;
937 if ( iNode == 3 ) // diagonal nodes have 0 and 2 indices
938 theQuadNodes[ iNode ] = n4;
943 //=======================================================================
944 //function : DeleteDiag
945 //purpose : Replace two neighbour triangles sharing theNode1-theNode2 link
946 // with a quadrangle built on the same 4 nodes.
947 // Return false if proper faces not found
948 //=======================================================================
950 bool SMESH_MeshEditor::DeleteDiag (const SMDS_MeshNode * theNode1,
951 const SMDS_MeshNode * theNode2)
953 myLastCreatedElems.Clear();
954 myLastCreatedNodes.Clear();
956 MESSAGE( "::DeleteDiag()" );
958 const SMDS_MeshElement *tr1, *tr2;
959 if ( !findTriangles( theNode1, theNode2, tr1, tr2 ))
962 const SMDS_VtkFace* F1 = dynamic_cast<const SMDS_VtkFace*>( tr1 );
963 if (!F1) return false;
964 const SMDS_VtkFace* F2 = dynamic_cast<const SMDS_VtkFace*>( tr2 );
965 if (!F2) return false;
966 SMESHDS_Mesh * aMesh = GetMeshDS();
968 if ((tr1->GetEntityType() == SMDSEntity_Triangle) &&
969 (tr2->GetEntityType() == SMDSEntity_Triangle)) {
971 const SMDS_MeshNode* aNodes [ 4 ];
972 if ( ! getQuadrangleNodes( aNodes, theNode1, theNode2, tr1, tr2 ))
975 const SMDS_MeshElement* newElem = 0;
976 newElem = aMesh->AddFace( aNodes[0], aNodes[1], aNodes[2], aNodes[3] );
977 myLastCreatedElems.Append(newElem);
978 AddToSameGroups( newElem, tr1, aMesh );
979 int aShapeId = tr1->getshapeId();
982 aMesh->SetMeshElementOnShape( newElem, aShapeId );
984 aMesh->RemoveElement( tr1 );
985 aMesh->RemoveElement( tr2 );
990 // check case of quadratic faces
991 if (tr1->GetEntityType() != SMDSEntity_Quad_Triangle)
993 if (tr2->GetEntityType() != SMDSEntity_Quad_Triangle)
997 // 1 +--+--+ 2 tr1: (1 2 4 5 9 7) or (2 4 1 9 7 5) or (4 1 2 7 5 9)
998 // | /| tr2: (2 3 4 6 8 9) or (3 4 2 8 9 6) or (4 2 3 9 6 8)
1006 vector< const SMDS_MeshNode* > N1;
1007 vector< const SMDS_MeshNode* > N2;
1008 if(!getNodesFromTwoTria(tr1,tr2,N1,N2))
1010 // now we receive following N1 and N2 (using numeration as above image)
1011 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
1012 // i.e. first nodes from both arrays determ new diagonal
1014 const SMDS_MeshNode* aNodes[8];
1024 const SMDS_MeshElement* newElem = 0;
1025 newElem = aMesh->AddFace( aNodes[0], aNodes[1], aNodes[2], aNodes[3],
1026 aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
1027 myLastCreatedElems.Append(newElem);
1028 AddToSameGroups( newElem, tr1, aMesh );
1029 int aShapeId = tr1->getshapeId();
1032 aMesh->SetMeshElementOnShape( newElem, aShapeId );
1034 aMesh->RemoveElement( tr1 );
1035 aMesh->RemoveElement( tr2 );
1037 // remove middle node (9)
1038 GetMeshDS()->RemoveNode( N1[4] );
1043 //=======================================================================
1044 //function : Reorient
1045 //purpose : Reverse theElement orientation
1046 //=======================================================================
1048 bool SMESH_MeshEditor::Reorient (const SMDS_MeshElement * theElem)
1050 MESSAGE("Reorient");
1051 myLastCreatedElems.Clear();
1052 myLastCreatedNodes.Clear();
1056 SMDS_ElemIteratorPtr it = theElem->nodesIterator();
1057 if ( !it || !it->more() )
1060 const SMDSAbs_ElementType type = theElem->GetType();
1061 if ( type < SMDSAbs_Edge || type > SMDSAbs_Volume )
1064 const SMDSAbs_EntityType geomType = theElem->GetEntityType();
1065 if ( geomType == SMDSEntity_Polyhedra ) // polyhedron
1067 const SMDS_VtkVolume* aPolyedre =
1068 dynamic_cast<const SMDS_VtkVolume*>( theElem );
1070 MESSAGE("Warning: bad volumic element");
1073 const int nbFaces = aPolyedre->NbFaces();
1074 vector<const SMDS_MeshNode *> poly_nodes;
1075 vector<int> quantities (nbFaces);
1077 // reverse each face of the polyedre
1078 for (int iface = 1; iface <= nbFaces; iface++) {
1079 int inode, nbFaceNodes = aPolyedre->NbFaceNodes(iface);
1080 quantities[iface - 1] = nbFaceNodes;
1082 for (inode = nbFaceNodes; inode >= 1; inode--) {
1083 const SMDS_MeshNode* curNode = aPolyedre->GetFaceNode(iface, inode);
1084 poly_nodes.push_back(curNode);
1087 return GetMeshDS()->ChangePolyhedronNodes( theElem, poly_nodes, quantities );
1089 else // other elements
1091 vector<const SMDS_MeshNode*> nodes( theElem->begin_nodes(), theElem->end_nodes() );
1092 const std::vector<int>& interlace = SMDS_MeshCell::reverseSmdsOrder( geomType );
1093 if ( interlace.empty() )
1095 std::reverse( nodes.begin(), nodes.end() ); // polygon
1097 else if ( interlace.size() > 1 )
1099 SMDS_MeshCell::applyInterlace( interlace, nodes );
1101 return GetMeshDS()->ChangeElementNodes( theElem, &nodes[0], nodes.size() );
1106 //================================================================================
1108 * \brief Reorient faces.
1109 * \param theFaces - the faces to reorient. If empty the whole mesh is meant
1110 * \param theDirection - desired direction of normal of \a theFace
1111 * \param theFace - one of \a theFaces that sould be oriented according to
1112 * \a theDirection and whose orientation defines orientation of other faces
1113 * \return number of reoriented faces.
1115 //================================================================================
1117 int SMESH_MeshEditor::Reorient2D (TIDSortedElemSet & theFaces,
1118 const gp_Dir& theDirection,
1119 const SMDS_MeshElement * theFace)
1122 if ( !theFace || theFace->GetType() != SMDSAbs_Face ) return nbReori;
1124 if ( theFaces.empty() )
1126 SMDS_FaceIteratorPtr fIt = GetMeshDS()->facesIterator(/*idInceasingOrder=*/true);
1127 while ( fIt->more() )
1128 theFaces.insert( theFaces.end(), fIt->next() );
1131 // orient theFace according to theDirection
1133 SMESH_MeshAlgos::FaceNormal( theFace, normal, /*normalized=*/false );
1134 if ( normal * theDirection.XYZ() < 0 )
1135 nbReori += Reorient( theFace );
1137 // Orient other faces
1139 set< const SMDS_MeshElement* > startFaces, visitedFaces;
1140 TIDSortedElemSet avoidSet;
1141 set< SMESH_TLink > checkedLinks;
1142 pair< set< SMESH_TLink >::iterator, bool > linkIt_isNew;
1144 if ( theFaces.size() > 1 )// leave 1 face to prevent finding not selected faces
1145 theFaces.erase( theFace );
1146 startFaces.insert( theFace );
1148 int nodeInd1, nodeInd2;
1149 const SMDS_MeshElement* otherFace;
1150 vector< const SMDS_MeshElement* > facesNearLink;
1151 vector< std::pair< int, int > > nodeIndsOfFace;
1153 set< const SMDS_MeshElement* >::iterator startFace = startFaces.begin();
1154 while ( !startFaces.empty() )
1156 startFace = startFaces.begin();
1157 theFace = *startFace;
1158 startFaces.erase( startFace );
1159 if ( !visitedFaces.insert( theFace ).second )
1163 avoidSet.insert(theFace);
1165 NLink link( theFace->GetNode( 0 ), 0 );
1167 const int nbNodes = theFace->NbCornerNodes();
1168 for ( int i = 0; i < nbNodes; ++i ) // loop on links of theFace
1170 link.second = theFace->GetNode(( i+1 ) % nbNodes );
1171 linkIt_isNew = checkedLinks.insert( link );
1172 if ( !linkIt_isNew.second )
1174 // link has already been checked and won't be encountered more
1175 // if the group (theFaces) is manifold
1176 //checkedLinks.erase( linkIt_isNew.first );
1180 facesNearLink.clear();
1181 nodeIndsOfFace.clear();
1182 while (( otherFace = SMESH_MeshAlgos::FindFaceInSet( link.first, link.second,
1184 &nodeInd1, &nodeInd2 )))
1185 if ( otherFace != theFace)
1187 facesNearLink.push_back( otherFace );
1188 nodeIndsOfFace.push_back( make_pair( nodeInd1, nodeInd2 ));
1189 avoidSet.insert( otherFace );
1191 if ( facesNearLink.size() > 1 )
1193 // NON-MANIFOLD mesh shell !
1194 // select a face most co-directed with theFace,
1195 // other faces won't be visited this time
1197 SMESH_MeshAlgos::FaceNormal( theFace, NF, /*normalized=*/false );
1198 double proj, maxProj = -1;
1199 for ( size_t i = 0; i < facesNearLink.size(); ++i ) {
1200 SMESH_MeshAlgos::FaceNormal( facesNearLink[i], NOF, /*normalized=*/false );
1201 if (( proj = Abs( NF * NOF )) > maxProj ) {
1203 otherFace = facesNearLink[i];
1204 nodeInd1 = nodeIndsOfFace[i].first;
1205 nodeInd2 = nodeIndsOfFace[i].second;
1208 // not to visit rejected faces
1209 for ( size_t i = 0; i < facesNearLink.size(); ++i )
1210 if ( facesNearLink[i] != otherFace && theFaces.size() > 1 )
1211 visitedFaces.insert( facesNearLink[i] );
1213 else if ( facesNearLink.size() == 1 )
1215 otherFace = facesNearLink[0];
1216 nodeInd1 = nodeIndsOfFace.back().first;
1217 nodeInd2 = nodeIndsOfFace.back().second;
1219 if ( otherFace && otherFace != theFace)
1221 // link must be reverse in otherFace if orientation ot otherFace
1222 // is same as that of theFace
1223 if ( abs(nodeInd2-nodeInd1) == 1 ? nodeInd2 > nodeInd1 : nodeInd1 > nodeInd2 )
1225 nbReori += Reorient( otherFace );
1227 startFaces.insert( otherFace );
1230 std::swap( link.first, link.second ); // reverse the link
1236 //=======================================================================
1237 //function : getBadRate
1239 //=======================================================================
1241 static double getBadRate (const SMDS_MeshElement* theElem,
1242 SMESH::Controls::NumericalFunctorPtr& theCrit)
1244 SMESH::Controls::TSequenceOfXYZ P;
1245 if ( !theElem || !theCrit->GetPoints( theElem, P ))
1247 return theCrit->GetBadRate( theCrit->GetValue( P ), theElem->NbNodes() );
1248 //return theCrit->GetBadRate( theCrit->GetValue( theElem->GetID() ), theElem->NbNodes() );
1251 //=======================================================================
1252 //function : QuadToTri
1253 //purpose : Cut quadrangles into triangles.
1254 // theCrit is used to select a diagonal to cut
1255 //=======================================================================
1257 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet & theElems,
1258 SMESH::Controls::NumericalFunctorPtr theCrit)
1260 myLastCreatedElems.Clear();
1261 myLastCreatedNodes.Clear();
1263 if ( !theCrit.get() )
1266 SMESHDS_Mesh * aMesh = GetMeshDS();
1268 Handle(Geom_Surface) surface;
1269 SMESH_MesherHelper helper( *GetMesh() );
1271 TIDSortedElemSet::iterator itElem;
1272 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
1274 const SMDS_MeshElement* elem = *itElem;
1275 if ( !elem || elem->GetType() != SMDSAbs_Face )
1277 if ( elem->NbCornerNodes() != 4 )
1280 // retrieve element nodes
1281 vector< const SMDS_MeshNode* > aNodes( elem->begin_nodes(), elem->end_nodes() );
1283 // compare two sets of possible triangles
1284 double aBadRate1, aBadRate2; // to what extent a set is bad
1285 SMDS_FaceOfNodes tr1 ( aNodes[0], aNodes[1], aNodes[2] );
1286 SMDS_FaceOfNodes tr2 ( aNodes[2], aNodes[3], aNodes[0] );
1287 aBadRate1 = getBadRate( &tr1, theCrit ) + getBadRate( &tr2, theCrit );
1289 SMDS_FaceOfNodes tr3 ( aNodes[1], aNodes[2], aNodes[3] );
1290 SMDS_FaceOfNodes tr4 ( aNodes[3], aNodes[0], aNodes[1] );
1291 aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit );
1293 const int aShapeId = FindShape( elem );
1294 const SMDS_MeshElement* newElem1 = 0;
1295 const SMDS_MeshElement* newElem2 = 0;
1297 if ( !elem->IsQuadratic() ) // split liner quadrangle
1299 // for MaxElementLength2D functor we return minimum diagonal for splitting,
1300 // because aBadRate1=2*len(diagonal 1-3); aBadRate2=2*len(diagonal 2-4)
1301 if ( aBadRate1 <= aBadRate2 ) {
1302 // tr1 + tr2 is better
1303 newElem1 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
1304 newElem2 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
1307 // tr3 + tr4 is better
1308 newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
1309 newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
1312 else // split quadratic quadrangle
1314 helper.SetIsQuadratic( true );
1315 helper.SetIsBiQuadratic( aNodes.size() == 9 );
1317 helper.AddTLinks( static_cast< const SMDS_MeshFace* >( elem ));
1318 if ( aNodes.size() == 9 )
1320 helper.SetIsBiQuadratic( true );
1321 if ( aBadRate1 <= aBadRate2 )
1322 helper.AddTLinkNode( aNodes[0], aNodes[2], aNodes[8] );
1324 helper.AddTLinkNode( aNodes[1], aNodes[3], aNodes[8] );
1326 // create a new element
1327 if ( aBadRate1 <= aBadRate2 ) {
1328 newElem1 = helper.AddFace( aNodes[2], aNodes[3], aNodes[0] );
1329 newElem2 = helper.AddFace( aNodes[2], aNodes[0], aNodes[1] );
1332 newElem1 = helper.AddFace( aNodes[3], aNodes[0], aNodes[1] );
1333 newElem2 = helper.AddFace( aNodes[3], aNodes[1], aNodes[2] );
1337 // care of a new element
1339 myLastCreatedElems.Append(newElem1);
1340 myLastCreatedElems.Append(newElem2);
1341 AddToSameGroups( newElem1, elem, aMesh );
1342 AddToSameGroups( newElem2, elem, aMesh );
1344 // put a new triangle on the same shape
1346 aMesh->SetMeshElementOnShape( newElem1, aShapeId );
1347 aMesh->SetMeshElementOnShape( newElem2, aShapeId );
1349 aMesh->RemoveElement( elem );
1354 //=======================================================================
1356 * \brief Split each of given quadrangles into 4 triangles.
1357 * \param theElems - The faces to be splitted. If empty all faces are split.
1359 //=======================================================================
1361 void SMESH_MeshEditor::QuadTo4Tri (TIDSortedElemSet & theElems)
1363 myLastCreatedElems.Clear();
1364 myLastCreatedNodes.Clear();
1366 SMESH_MesherHelper helper( *GetMesh() );
1367 helper.SetElementsOnShape( true );
1369 SMDS_ElemIteratorPtr faceIt;
1370 if ( theElems.empty() ) faceIt = GetMeshDS()->elementsIterator(SMDSAbs_Face);
1371 else faceIt = elemSetIterator( theElems );
1374 gp_XY uv [9]; uv[8] = gp_XY(0,0);
1376 vector< const SMDS_MeshNode* > nodes;
1377 SMESHDS_SubMesh* subMeshDS;
1379 Handle(Geom_Surface) surface;
1380 TopLoc_Location loc;
1382 while ( faceIt->more() )
1384 const SMDS_MeshElement* quad = faceIt->next();
1385 if ( !quad || quad->NbCornerNodes() != 4 )
1388 // get a surface the quad is on
1390 if ( quad->getshapeId() < 1 )
1393 helper.SetSubShape( 0 );
1396 else if ( quad->getshapeId() != helper.GetSubShapeID() )
1398 helper.SetSubShape( quad->getshapeId() );
1399 if ( !helper.GetSubShape().IsNull() &&
1400 helper.GetSubShape().ShapeType() == TopAbs_FACE )
1402 F = TopoDS::Face( helper.GetSubShape() );
1403 surface = BRep_Tool::Surface( F, loc );
1404 subMeshDS = GetMeshDS()->MeshElements( quad->getshapeId() );
1408 helper.SetSubShape( 0 );
1413 // create a central node
1415 const SMDS_MeshNode* nCentral;
1416 nodes.assign( quad->begin_nodes(), quad->end_nodes() );
1418 if ( nodes.size() == 9 )
1420 nCentral = nodes.back();
1427 for ( ; iN < nodes.size(); ++iN )
1428 xyz[ iN ] = SMESH_TNodeXYZ( nodes[ iN ] );
1430 for ( ; iN < 8; ++iN ) // mid-side points of a linear qudrangle
1431 xyz[ iN ] = 0.5 * ( xyz[ iN - 4 ] + xyz[( iN - 3 )%4 ] );
1433 xyz[ 8 ] = helper.calcTFI( 0.5, 0.5,
1434 xyz[0], xyz[1], xyz[2], xyz[3],
1435 xyz[4], xyz[5], xyz[6], xyz[7] );
1439 for ( ; iN < nodes.size(); ++iN )
1440 uv[ iN ] = helper.GetNodeUV( F, nodes[iN], nodes[(iN+2)%4], &checkUV );
1442 for ( ; iN < 8; ++iN ) // UV of mid-side points of a linear qudrangle
1443 uv[ iN ] = helper.GetMiddleUV( surface, uv[ iN - 4 ], uv[( iN - 3 )%4 ] );
1445 uv[ 8 ] = helper.calcTFI( 0.5, 0.5,
1446 uv[0], uv[1], uv[2], uv[3],
1447 uv[4], uv[5], uv[6], uv[7] );
1449 gp_Pnt p = surface->Value( uv[8].X(), uv[8].Y() ).Transformed( loc );
1453 nCentral = helper.AddNode( xyz[8].X(), xyz[8].Y(), xyz[8].Z(), /*id=*/0,
1454 uv[8].X(), uv[8].Y() );
1455 myLastCreatedNodes.Append( nCentral );
1458 // create 4 triangles
1460 GetMeshDS()->RemoveFreeElement( quad, subMeshDS, /*fromGroups=*/false );
1462 helper.SetIsQuadratic ( nodes.size() > 4 );
1463 helper.SetIsBiQuadratic( nodes.size() == 9 );
1464 if ( helper.GetIsQuadratic() )
1465 helper.AddTLinks( static_cast< const SMDS_MeshFace*>( quad ));
1467 for ( int i = 0; i < 4; ++i )
1469 SMDS_MeshElement* tria = helper.AddFace( nodes[ i ],
1472 ReplaceElemInGroups( tria, quad, GetMeshDS() );
1473 myLastCreatedElems.Append( tria );
1478 //=======================================================================
1479 //function : BestSplit
1480 //purpose : Find better diagonal for cutting.
1481 //=======================================================================
1483 int SMESH_MeshEditor::BestSplit (const SMDS_MeshElement* theQuad,
1484 SMESH::Controls::NumericalFunctorPtr theCrit)
1486 myLastCreatedElems.Clear();
1487 myLastCreatedNodes.Clear();
1492 if (!theQuad || theQuad->GetType() != SMDSAbs_Face )
1495 if( theQuad->NbNodes()==4 ||
1496 (theQuad->NbNodes()==8 && theQuad->IsQuadratic()) ) {
1498 // retrieve element nodes
1499 const SMDS_MeshNode* aNodes [4];
1500 SMDS_ElemIteratorPtr itN = theQuad->nodesIterator();
1502 //while (itN->more())
1504 aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
1506 // compare two sets of possible triangles
1507 double aBadRate1, aBadRate2; // to what extent a set is bad
1508 SMDS_FaceOfNodes tr1 ( aNodes[0], aNodes[1], aNodes[2] );
1509 SMDS_FaceOfNodes tr2 ( aNodes[2], aNodes[3], aNodes[0] );
1510 aBadRate1 = getBadRate( &tr1, theCrit ) + getBadRate( &tr2, theCrit );
1512 SMDS_FaceOfNodes tr3 ( aNodes[1], aNodes[2], aNodes[3] );
1513 SMDS_FaceOfNodes tr4 ( aNodes[3], aNodes[0], aNodes[1] );
1514 aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit );
1515 // for MaxElementLength2D functor we return minimum diagonal for splitting,
1516 // because aBadRate1=2*len(diagonal 1-3); aBadRate2=2*len(diagonal 2-4)
1517 if (aBadRate1 <= aBadRate2) // tr1 + tr2 is better
1518 return 1; // diagonal 1-3
1520 return 2; // diagonal 2-4
1527 // Methods of splitting volumes into tetra
1529 const int theHexTo5_1[5*4+1] =
1531 0, 1, 2, 5, 0, 4, 5, 7, 0, 2, 3, 7, 2, 5, 6, 7, 0, 5, 2, 7, -1
1533 const int theHexTo5_2[5*4+1] =
1535 1, 2, 3, 6, 1, 4, 5, 6, 0, 1, 3, 4, 3, 4, 6, 7, 1, 3, 4, 6, -1
1537 const int* theHexTo5[2] = { theHexTo5_1, theHexTo5_2 };
1539 const int theHexTo6_1[6*4+1] =
1541 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
1543 const int theHexTo6_2[6*4+1] =
1545 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
1547 const int theHexTo6_3[6*4+1] =
1549 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
1551 const int theHexTo6_4[6*4+1] =
1553 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
1555 const int* theHexTo6[4] = { theHexTo6_1, theHexTo6_2, theHexTo6_3, theHexTo6_4 };
1557 const int thePyraTo2_1[2*4+1] =
1559 0, 1, 2, 4, 0, 2, 3, 4, -1
1561 const int thePyraTo2_2[2*4+1] =
1563 1, 2, 3, 4, 1, 3, 0, 4, -1
1565 const int* thePyraTo2[2] = { thePyraTo2_1, thePyraTo2_2 };
1567 const int thePentaTo3_1[3*4+1] =
1569 0, 1, 2, 3, 1, 3, 4, 2, 2, 3, 4, 5, -1
1571 const int thePentaTo3_2[3*4+1] =
1573 1, 2, 0, 4, 2, 4, 5, 0, 0, 4, 5, 3, -1
1575 const int thePentaTo3_3[3*4+1] =
1577 2, 0, 1, 5, 0, 5, 3, 1, 1, 5, 3, 4, -1
1579 const int thePentaTo3_4[3*4+1] =
1581 0, 1, 2, 3, 1, 3, 4, 5, 2, 3, 1, 5, -1
1583 const int thePentaTo3_5[3*4+1] =
1585 1, 2, 0, 4, 2, 4, 5, 3, 0, 4, 2, 3, -1
1587 const int thePentaTo3_6[3*4+1] =
1589 2, 0, 1, 5, 0, 5, 3, 4, 1, 5, 0, 4, -1
1591 const int* thePentaTo3[6] = { thePentaTo3_1, thePentaTo3_2, thePentaTo3_3,
1592 thePentaTo3_4, thePentaTo3_5, thePentaTo3_6 };
1594 struct TTriangleFacet //!< stores indices of three nodes of tetra facet
1597 TTriangleFacet(int n1, int n2, int n3): _n1(n1), _n2(n2), _n3(n3) {}
1598 bool contains(int n) const { return ( n == _n1 || n == _n2 || n == _n3 ); }
1599 bool hasAdjacentTetra( const SMDS_MeshElement* elem ) const;
1604 const int* _connectivity; //!< foursomes of tetra connectivy finished by -1
1605 bool _baryNode; //!< additional node is to be created at cell barycenter
1606 bool _ownConn; //!< to delete _connectivity in destructor
1607 map<int, const SMDS_MeshNode*> _faceBaryNode; //!< map face index to node at BC of face
1609 TSplitMethod( int nbTet=0, const int* conn=0, bool addNode=false)
1610 : _nbTetra(nbTet), _connectivity(conn), _baryNode(addNode), _ownConn(false) {}
1611 ~TSplitMethod() { if ( _ownConn ) delete [] _connectivity; _connectivity = 0; }
1612 bool hasFacet( const TTriangleFacet& facet ) const
1614 const int* tetConn = _connectivity;
1615 for ( ; tetConn[0] >= 0; tetConn += 4 )
1616 if (( facet.contains( tetConn[0] ) +
1617 facet.contains( tetConn[1] ) +
1618 facet.contains( tetConn[2] ) +
1619 facet.contains( tetConn[3] )) == 3 )
1625 //=======================================================================
1627 * \brief return TSplitMethod for the given element
1629 //=======================================================================
1631 TSplitMethod getSplitMethod( SMDS_VolumeTool& vol, const int theMethodFlags)
1633 const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
1635 // at HEXA_TO_24 method, each face of volume is split into triangles each based on
1636 // an edge and a face barycenter; tertaherdons are based on triangles and
1637 // a volume barycenter
1638 const bool is24TetMode = ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_24 );
1640 // Find out how adjacent volumes are split
1642 vector < list< TTriangleFacet > > triaSplitsByFace( vol.NbFaces() ); // splits of each side
1643 int hasAdjacentSplits = 0, maxTetConnSize = 0;
1644 for ( int iF = 0; iF < vol.NbFaces(); ++iF )
1646 int nbNodes = vol.NbFaceNodes( iF ) / iQ;
1647 maxTetConnSize += 4 * ( nbNodes - (is24TetMode ? 0 : 2));
1648 if ( nbNodes < 4 ) continue;
1650 list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
1651 const int* nInd = vol.GetFaceNodesIndices( iF );
1654 TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
1655 TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
1656 if ( t012.hasAdjacentTetra( vol.Element() )) triaSplits.push_back( t012 );
1657 else if ( t123.hasAdjacentTetra( vol.Element() )) triaSplits.push_back( t123 );
1661 int iCom = 0; // common node of triangle faces to split into
1662 for ( int iVar = 0; iVar < nbNodes; ++iVar, ++iCom )
1664 TTriangleFacet t012( nInd[ iQ * ( iCom )],
1665 nInd[ iQ * ( (iCom+1)%nbNodes )],
1666 nInd[ iQ * ( (iCom+2)%nbNodes )]);
1667 TTriangleFacet t023( nInd[ iQ * ( iCom )],
1668 nInd[ iQ * ( (iCom+2)%nbNodes )],
1669 nInd[ iQ * ( (iCom+3)%nbNodes )]);
1670 if ( t012.hasAdjacentTetra( vol.Element() ) && t023.hasAdjacentTetra( vol.Element() ))
1672 triaSplits.push_back( t012 );
1673 triaSplits.push_back( t023 );
1678 if ( !triaSplits.empty() )
1679 hasAdjacentSplits = true;
1682 // Among variants of split method select one compliant with adjacent volumes
1684 TSplitMethod method;
1685 if ( !vol.Element()->IsPoly() && !is24TetMode )
1687 int nbVariants = 2, nbTet = 0;
1688 const int** connVariants = 0;
1689 switch ( vol.Element()->GetEntityType() )
1691 case SMDSEntity_Hexa:
1692 case SMDSEntity_Quad_Hexa:
1693 case SMDSEntity_TriQuad_Hexa:
1694 if ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_5 )
1695 connVariants = theHexTo5, nbTet = 5;
1697 connVariants = theHexTo6, nbTet = 6, nbVariants = 4;
1699 case SMDSEntity_Pyramid:
1700 case SMDSEntity_Quad_Pyramid:
1701 connVariants = thePyraTo2; nbTet = 2;
1703 case SMDSEntity_Penta:
1704 case SMDSEntity_Quad_Penta:
1705 connVariants = thePentaTo3; nbTet = 3; nbVariants = 6;
1710 for ( int variant = 0; variant < nbVariants && method._nbTetra == 0; ++variant )
1712 // check method compliancy with adjacent tetras,
1713 // all found splits must be among facets of tetras described by this method
1714 method = TSplitMethod( nbTet, connVariants[variant] );
1715 if ( hasAdjacentSplits && method._nbTetra > 0 )
1717 bool facetCreated = true;
1718 for ( int iF = 0; facetCreated && iF < triaSplitsByFace.size(); ++iF )
1720 list< TTriangleFacet >::const_iterator facet = triaSplitsByFace[iF].begin();
1721 for ( ; facetCreated && facet != triaSplitsByFace[iF].end(); ++facet )
1722 facetCreated = method.hasFacet( *facet );
1724 if ( !facetCreated )
1725 method = TSplitMethod(0); // incompatible method
1729 if ( method._nbTetra < 1 )
1731 // No standard method is applicable, use a generic solution:
1732 // each facet of a volume is split into triangles and
1733 // each of triangles and a volume barycenter form a tetrahedron.
1735 const bool isHex27 = ( vol.Element()->GetEntityType() == SMDSEntity_TriQuad_Hexa );
1737 int* connectivity = new int[ maxTetConnSize + 1 ];
1738 method._connectivity = connectivity;
1739 method._ownConn = true;
1740 method._baryNode = !isHex27; // to create central node or not
1743 int baryCenInd = vol.NbNodes() - int( isHex27 );
1744 for ( int iF = 0; iF < vol.NbFaces(); ++iF )
1746 const int nbNodes = vol.NbFaceNodes( iF ) / iQ;
1747 const int* nInd = vol.GetFaceNodesIndices( iF );
1748 // find common node of triangle facets of tetra to create
1749 int iCommon = 0; // index in linear numeration
1750 const list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
1751 if ( !triaSplits.empty() )
1754 const TTriangleFacet* facet = &triaSplits.front();
1755 for ( ; iCommon < nbNodes-1 ; ++iCommon )
1756 if ( facet->contains( nInd[ iQ * iCommon ]) &&
1757 facet->contains( nInd[ iQ * ((iCommon+2)%nbNodes) ]))
1760 else if ( nbNodes > 3 && !is24TetMode )
1762 // find the best method of splitting into triangles by aspect ratio
1763 SMESH::Controls::NumericalFunctorPtr aspectRatio( new SMESH::Controls::AspectRatio);
1764 map< double, int > badness2iCommon;
1765 const SMDS_MeshNode** nodes = vol.GetFaceNodes( iF );
1766 int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
1767 for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCommon )
1770 for ( int iLast = iCommon+2; iLast < iCommon+nbNodes; ++iLast )
1772 SMDS_FaceOfNodes tria ( nodes[ iQ*( iCommon )],
1773 nodes[ iQ*((iLast-1)%nbNodes)],
1774 nodes[ iQ*((iLast )%nbNodes)]);
1775 badness += getBadRate( &tria, aspectRatio );
1777 badness2iCommon.insert( make_pair( badness, iCommon ));
1779 // use iCommon with lowest badness
1780 iCommon = badness2iCommon.begin()->second;
1782 if ( iCommon >= nbNodes )
1783 iCommon = 0; // something wrong
1785 // fill connectivity of tetrahedra based on a current face
1786 int nbTet = nbNodes - 2;
1787 if ( is24TetMode && nbNodes > 3 && triaSplits.empty())
1792 faceBaryCenInd = vol.GetCenterNodeIndex( iF );
1793 method._faceBaryNode[ iF ] = vol.GetNodes()[ faceBaryCenInd ];
1797 method._faceBaryNode[ iF ] = 0;
1798 faceBaryCenInd = baryCenInd + method._faceBaryNode.size();
1801 for ( int i = 0; i < nbTet; ++i )
1803 int i1 = i, i2 = (i+1) % nbNodes;
1804 if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
1805 connectivity[ connSize++ ] = nInd[ iQ * i1 ];
1806 connectivity[ connSize++ ] = nInd[ iQ * i2 ];
1807 connectivity[ connSize++ ] = faceBaryCenInd;
1808 connectivity[ connSize++ ] = baryCenInd;
1813 for ( int i = 0; i < nbTet; ++i )
1815 int i1 = (iCommon+1+i) % nbNodes, i2 = (iCommon+2+i) % nbNodes;
1816 if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
1817 connectivity[ connSize++ ] = nInd[ iQ * iCommon ];
1818 connectivity[ connSize++ ] = nInd[ iQ * i1 ];
1819 connectivity[ connSize++ ] = nInd[ iQ * i2 ];
1820 connectivity[ connSize++ ] = baryCenInd;
1823 method._nbTetra += nbTet;
1825 } // loop on volume faces
1827 connectivity[ connSize++ ] = -1;
1829 } // end of generic solution
1833 //================================================================================
1835 * \brief Check if there is a tetraherdon adjacent to the given element via this facet
1837 //================================================================================
1839 bool TTriangleFacet::hasAdjacentTetra( const SMDS_MeshElement* elem ) const
1841 // find the tetrahedron including the three nodes of facet
1842 const SMDS_MeshNode* n1 = elem->GetNode(_n1);
1843 const SMDS_MeshNode* n2 = elem->GetNode(_n2);
1844 const SMDS_MeshNode* n3 = elem->GetNode(_n3);
1845 SMDS_ElemIteratorPtr volIt1 = n1->GetInverseElementIterator(SMDSAbs_Volume);
1846 while ( volIt1->more() )
1848 const SMDS_MeshElement* v = volIt1->next();
1849 SMDSAbs_EntityType type = v->GetEntityType();
1850 if ( type != SMDSEntity_Tetra && type != SMDSEntity_Quad_Tetra )
1852 if ( type == SMDSEntity_Quad_Tetra && v->GetNodeIndex( n1 ) > 3 )
1853 continue; // medium node not allowed
1854 const int ind2 = v->GetNodeIndex( n2 );
1855 if ( ind2 < 0 || 3 < ind2 )
1857 const int ind3 = v->GetNodeIndex( n3 );
1858 if ( ind3 < 0 || 3 < ind3 )
1865 //=======================================================================
1867 * \brief A key of a face of volume
1869 //=======================================================================
1871 struct TVolumeFaceKey: pair< pair< int, int>, pair< int, int> >
1873 TVolumeFaceKey( SMDS_VolumeTool& vol, int iF )
1875 TIDSortedNodeSet sortedNodes;
1876 const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
1877 int nbNodes = vol.NbFaceNodes( iF );
1878 const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iF );
1879 for ( int i = 0; i < nbNodes; i += iQ )
1880 sortedNodes.insert( fNodes[i] );
1881 TIDSortedNodeSet::iterator n = sortedNodes.begin();
1882 first.first = (*(n++))->GetID();
1883 first.second = (*(n++))->GetID();
1884 second.first = (*(n++))->GetID();
1885 second.second = ( sortedNodes.size() > 3 ) ? (*(n++))->GetID() : 0;
1890 //=======================================================================
1891 //function : SplitVolumesIntoTetra
1892 //purpose : Split volume elements into tetrahedra.
1893 //=======================================================================
1895 void SMESH_MeshEditor::SplitVolumesIntoTetra (const TIDSortedElemSet & theElems,
1896 const int theMethodFlags)
1898 // std-like iterator on coordinates of nodes of mesh element
1899 typedef SMDS_StdIterator< SMESH_TNodeXYZ, SMDS_ElemIteratorPtr > NXyzIterator;
1900 NXyzIterator xyzEnd;
1902 SMDS_VolumeTool volTool;
1903 SMESH_MesherHelper helper( *GetMesh());
1905 SMESHDS_SubMesh* subMesh = 0;//GetMeshDS()->MeshElements(1);
1906 SMESHDS_SubMesh* fSubMesh = 0;//subMesh;
1908 SMESH_SequenceOfElemPtr newNodes, newElems;
1910 // map face of volume to it's baricenrtic node
1911 map< TVolumeFaceKey, const SMDS_MeshNode* > volFace2BaryNode;
1914 TIDSortedElemSet::const_iterator elem = theElems.begin();
1915 for ( ; elem != theElems.end(); ++elem )
1917 if ( (*elem)->GetType() != SMDSAbs_Volume )
1919 SMDSAbs_EntityType geomType = (*elem)->GetEntityType();
1920 if ( geomType == SMDSEntity_Tetra || geomType == SMDSEntity_Quad_Tetra )
1923 if ( !volTool.Set( *elem, /*ignoreCentralNodes=*/false )) continue; // strange...
1925 TSplitMethod splitMethod = getSplitMethod( volTool, theMethodFlags );
1926 if ( splitMethod._nbTetra < 1 ) continue;
1928 // find submesh to add new tetras to
1929 if ( !subMesh || !subMesh->Contains( *elem ))
1931 int shapeID = FindShape( *elem );
1932 helper.SetSubShape( shapeID ); // helper will add tetras to the found submesh
1933 subMesh = GetMeshDS()->MeshElements( shapeID );
1936 if ( (*elem)->IsQuadratic() )
1939 // add quadratic links to the helper
1940 for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
1942 const SMDS_MeshNode** fNodes = volTool.GetFaceNodes( iF );
1943 int nbN = volTool.NbFaceNodes( iF ) - bool( volTool.GetCenterNodeIndex(iF) > 0 );
1944 for ( int iN = 0; iN < nbN; iN += iQ )
1945 helper.AddTLinkNode( fNodes[iN], fNodes[iN+2], fNodes[iN+1] );
1947 helper.SetIsQuadratic( true );
1952 helper.SetIsQuadratic( false );
1954 vector<const SMDS_MeshNode*> nodes( (*elem)->begin_nodes(), (*elem)->end_nodes() );
1955 helper.SetElementsOnShape( true );
1956 if ( splitMethod._baryNode )
1958 // make a node at barycenter
1959 volTool.GetBaryCenter( bc[0], bc[1], bc[2] );
1960 SMDS_MeshNode* gcNode = helper.AddNode( bc[0], bc[1], bc[2] );
1961 nodes.push_back( gcNode );
1962 newNodes.Append( gcNode );
1964 if ( !splitMethod._faceBaryNode.empty() )
1966 // make or find baricentric nodes of faces
1967 map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.begin();
1968 for ( ; iF_n != splitMethod._faceBaryNode.end(); ++iF_n )
1970 map< TVolumeFaceKey, const SMDS_MeshNode* >::iterator f_n =
1971 volFace2BaryNode.insert
1972 ( make_pair( TVolumeFaceKey( volTool,iF_n->first ), iF_n->second )).first;
1975 volTool.GetFaceBaryCenter( iF_n->first, bc[0], bc[1], bc[2] );
1976 newNodes.Append( f_n->second = helper.AddNode( bc[0], bc[1], bc[2] ));
1978 nodes.push_back( iF_n->second = f_n->second );
1983 vector<const SMDS_MeshElement* > tetras( splitMethod._nbTetra ); // splits of a volume
1984 const int* tetConn = splitMethod._connectivity;
1985 for ( int i = 0; i < splitMethod._nbTetra; ++i, tetConn += 4 )
1986 newElems.Append( tetras[ i ] = helper.AddVolume( nodes[ tetConn[0] ],
1987 nodes[ tetConn[1] ],
1988 nodes[ tetConn[2] ],
1989 nodes[ tetConn[3] ]));
1991 ReplaceElemInGroups( *elem, tetras, GetMeshDS() );
1993 // Split faces on sides of the split volume
1995 const SMDS_MeshNode** volNodes = volTool.GetNodes();
1996 for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
1998 const int nbNodes = volTool.NbFaceNodes( iF ) / iQ;
1999 if ( nbNodes < 4 ) continue;
2001 // find an existing face
2002 vector<const SMDS_MeshNode*> fNodes( volTool.GetFaceNodes( iF ),
2003 volTool.GetFaceNodes( iF ) + volTool.NbFaceNodes( iF ));
2004 while ( const SMDS_MeshElement* face = GetMeshDS()->FindElement( fNodes, SMDSAbs_Face,
2005 /*noMedium=*/false))
2008 helper.SetElementsOnShape( false );
2009 vector< const SMDS_MeshElement* > triangles;
2011 // find submesh to add new triangles in
2012 if ( !fSubMesh || !fSubMesh->Contains( face ))
2014 int shapeID = FindShape( face );
2015 fSubMesh = GetMeshDS()->MeshElements( shapeID );
2017 map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.find(iF);
2018 if ( iF_n != splitMethod._faceBaryNode.end() )
2020 for ( int iN = 0; iN < nbNodes*iQ; iN += iQ )
2022 const SMDS_MeshNode* n1 = fNodes[iN];
2023 const SMDS_MeshNode *n2 = fNodes[(iN+iQ)%(nbNodes*iQ)];
2024 const SMDS_MeshNode *n3 = iF_n->second;
2025 if ( !volTool.IsFaceExternal( iF ))
2027 triangles.push_back( helper.AddFace( n1,n2,n3 ));
2029 if ( fSubMesh && n3->getshapeId() < 1 )
2030 fSubMesh->AddNode( n3 );
2035 // among possible triangles create ones discribed by split method
2036 const int* nInd = volTool.GetFaceNodesIndices( iF );
2037 int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
2038 int iCom = 0; // common node of triangle faces to split into
2039 list< TTriangleFacet > facets;
2040 for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCom )
2042 TTriangleFacet t012( nInd[ iQ * ( iCom )],
2043 nInd[ iQ * ( (iCom+1)%nbNodes )],
2044 nInd[ iQ * ( (iCom+2)%nbNodes )]);
2045 TTriangleFacet t023( nInd[ iQ * ( iCom )],
2046 nInd[ iQ * ( (iCom+2)%nbNodes )],
2047 nInd[ iQ * ( (iCom+3)%nbNodes )]);
2048 if ( splitMethod.hasFacet( t012 ) && splitMethod.hasFacet( t023 ))
2050 facets.push_back( t012 );
2051 facets.push_back( t023 );
2052 for ( int iLast = iCom+4; iLast < iCom+nbNodes; ++iLast )
2053 facets.push_back( TTriangleFacet( nInd[ iQ * ( iCom )],
2054 nInd[ iQ * ((iLast-1)%nbNodes )],
2055 nInd[ iQ * ((iLast )%nbNodes )]));
2059 list< TTriangleFacet >::iterator facet = facets.begin();
2060 for ( ; facet != facets.end(); ++facet )
2062 if ( !volTool.IsFaceExternal( iF ))
2063 swap( facet->_n2, facet->_n3 );
2064 triangles.push_back( helper.AddFace( volNodes[ facet->_n1 ],
2065 volNodes[ facet->_n2 ],
2066 volNodes[ facet->_n3 ]));
2069 for ( int i = 0; i < triangles.size(); ++i )
2071 if ( !triangles[i] ) continue;
2073 fSubMesh->AddElement( triangles[i]);
2074 newElems.Append( triangles[i] );
2076 ReplaceElemInGroups( face, triangles, GetMeshDS() );
2077 GetMeshDS()->RemoveFreeElement( face, fSubMesh, /*fromGroups=*/false );
2080 } // loop on volume faces to split them into triangles
2082 GetMeshDS()->RemoveFreeElement( *elem, subMesh, /*fromGroups=*/false );
2084 if ( geomType == SMDSEntity_TriQuad_Hexa )
2086 // remove medium nodes that could become free
2087 for ( int i = 20; i < volTool.NbNodes(); ++i )
2088 if ( volNodes[i]->NbInverseElements() == 0 )
2089 GetMeshDS()->RemoveNode( volNodes[i] );
2091 } // loop on volumes to split
2093 myLastCreatedNodes = newNodes;
2094 myLastCreatedElems = newElems;
2097 //=======================================================================
2098 //function : AddToSameGroups
2099 //purpose : add elemToAdd to the groups the elemInGroups belongs to
2100 //=======================================================================
2102 void SMESH_MeshEditor::AddToSameGroups (const SMDS_MeshElement* elemToAdd,
2103 const SMDS_MeshElement* elemInGroups,
2104 SMESHDS_Mesh * aMesh)
2106 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2107 if (!groups.empty()) {
2108 set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2109 for ( ; grIt != groups.end(); grIt++ ) {
2110 SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2111 if ( group && group->Contains( elemInGroups ))
2112 group->SMDSGroup().Add( elemToAdd );
2118 //=======================================================================
2119 //function : RemoveElemFromGroups
2120 //purpose : Remove removeelem to the groups the elemInGroups belongs to
2121 //=======================================================================
2122 void SMESH_MeshEditor::RemoveElemFromGroups (const SMDS_MeshElement* removeelem,
2123 SMESHDS_Mesh * aMesh)
2125 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2126 if (!groups.empty())
2128 set<SMESHDS_GroupBase*>::const_iterator GrIt = groups.begin();
2129 for (; GrIt != groups.end(); GrIt++)
2131 SMESHDS_Group* grp = dynamic_cast<SMESHDS_Group*>(*GrIt);
2132 if (!grp || grp->IsEmpty()) continue;
2133 grp->SMDSGroup().Remove(removeelem);
2138 //================================================================================
2140 * \brief Replace elemToRm by elemToAdd in the all groups
2142 //================================================================================
2144 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement* elemToRm,
2145 const SMDS_MeshElement* elemToAdd,
2146 SMESHDS_Mesh * aMesh)
2148 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2149 if (!groups.empty()) {
2150 set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2151 for ( ; grIt != groups.end(); grIt++ ) {
2152 SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2153 if ( group && group->SMDSGroup().Remove( elemToRm ) && elemToAdd )
2154 group->SMDSGroup().Add( elemToAdd );
2159 //================================================================================
2161 * \brief Replace elemToRm by elemToAdd in the all groups
2163 //================================================================================
2165 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement* elemToRm,
2166 const vector<const SMDS_MeshElement*>& elemToAdd,
2167 SMESHDS_Mesh * aMesh)
2169 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2170 if (!groups.empty())
2172 set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2173 for ( ; grIt != groups.end(); grIt++ ) {
2174 SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2175 if ( group && group->SMDSGroup().Remove( elemToRm ) )
2176 for ( int i = 0; i < elemToAdd.size(); ++i )
2177 group->SMDSGroup().Add( elemToAdd[ i ] );
2182 //=======================================================================
2183 //function : QuadToTri
2184 //purpose : Cut quadrangles into triangles.
2185 // theCrit is used to select a diagonal to cut
2186 //=======================================================================
2188 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet & theElems,
2189 const bool the13Diag)
2191 myLastCreatedElems.Clear();
2192 myLastCreatedNodes.Clear();
2194 MESSAGE( "::QuadToTri()" );
2196 SMESHDS_Mesh * aMesh = GetMeshDS();
2198 Handle(Geom_Surface) surface;
2199 SMESH_MesherHelper helper( *GetMesh() );
2201 TIDSortedElemSet::iterator itElem;
2202 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
2203 const SMDS_MeshElement* elem = *itElem;
2204 if ( !elem || elem->GetType() != SMDSAbs_Face )
2206 bool isquad = elem->NbNodes()==4 || elem->NbNodes()==8;
2207 if(!isquad) continue;
2209 if(elem->NbNodes()==4) {
2210 // retrieve element nodes
2211 const SMDS_MeshNode* aNodes [4];
2212 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
2214 while ( itN->more() )
2215 aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
2217 int aShapeId = FindShape( elem );
2218 const SMDS_MeshElement* newElem1 = 0;
2219 const SMDS_MeshElement* newElem2 = 0;
2221 newElem1 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
2222 newElem2 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
2225 newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
2226 newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
2228 myLastCreatedElems.Append(newElem1);
2229 myLastCreatedElems.Append(newElem2);
2230 // put a new triangle on the same shape and add to the same groups
2233 aMesh->SetMeshElementOnShape( newElem1, aShapeId );
2234 aMesh->SetMeshElementOnShape( newElem2, aShapeId );
2236 AddToSameGroups( newElem1, elem, aMesh );
2237 AddToSameGroups( newElem2, elem, aMesh );
2238 //aMesh->RemoveFreeElement(elem, aMesh->MeshElements(aShapeId), true);
2239 aMesh->RemoveElement( elem );
2242 // Quadratic quadrangle
2244 if( elem->NbNodes()==8 && elem->IsQuadratic() ) {
2246 // get surface elem is on
2247 int aShapeId = FindShape( elem );
2248 if ( aShapeId != helper.GetSubShapeID() ) {
2252 shape = aMesh->IndexToShape( aShapeId );
2253 if ( !shape.IsNull() && shape.ShapeType() == TopAbs_FACE ) {
2254 TopoDS_Face face = TopoDS::Face( shape );
2255 surface = BRep_Tool::Surface( face );
2256 if ( !surface.IsNull() )
2257 helper.SetSubShape( shape );
2261 const SMDS_MeshNode* aNodes [8];
2262 const SMDS_MeshNode* inFaceNode = 0;
2263 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
2265 while ( itN->more() ) {
2266 aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
2267 if ( !inFaceNode && helper.GetNodeUVneedInFaceNode() &&
2268 aNodes[ i-1 ]->GetPosition()->GetTypeOfPosition() == SMDS_TOP_FACE )
2270 inFaceNode = aNodes[ i-1 ];
2274 // find middle point for (0,1,2,3)
2275 // and create a node in this point;
2277 if ( surface.IsNull() ) {
2279 p += gp_XYZ(aNodes[i]->X(), aNodes[i]->Y(), aNodes[i]->Z() );
2283 TopoDS_Face geomFace = TopoDS::Face( helper.GetSubShape() );
2286 uv += helper.GetNodeUV( geomFace, aNodes[i], inFaceNode );
2288 p = surface->Value( uv.X(), uv.Y() ).XYZ();
2290 const SMDS_MeshNode* newN = aMesh->AddNode( p.X(), p.Y(), p.Z() );
2291 myLastCreatedNodes.Append(newN);
2293 // create a new element
2294 const SMDS_MeshElement* newElem1 = 0;
2295 const SMDS_MeshElement* newElem2 = 0;
2297 newElem1 = aMesh->AddFace(aNodes[2], aNodes[3], aNodes[0],
2298 aNodes[6], aNodes[7], newN );
2299 newElem2 = aMesh->AddFace(aNodes[2], aNodes[0], aNodes[1],
2300 newN, aNodes[4], aNodes[5] );
2303 newElem1 = aMesh->AddFace(aNodes[3], aNodes[0], aNodes[1],
2304 aNodes[7], aNodes[4], newN );
2305 newElem2 = aMesh->AddFace(aNodes[3], aNodes[1], aNodes[2],
2306 newN, aNodes[5], aNodes[6] );
2308 myLastCreatedElems.Append(newElem1);
2309 myLastCreatedElems.Append(newElem2);
2310 // put a new triangle on the same shape and add to the same groups
2313 aMesh->SetMeshElementOnShape( newElem1, aShapeId );
2314 aMesh->SetMeshElementOnShape( newElem2, aShapeId );
2316 AddToSameGroups( newElem1, elem, aMesh );
2317 AddToSameGroups( newElem2, elem, aMesh );
2318 aMesh->RemoveElement( elem );
2325 //=======================================================================
2326 //function : getAngle
2328 //=======================================================================
2330 double getAngle(const SMDS_MeshElement * tr1,
2331 const SMDS_MeshElement * tr2,
2332 const SMDS_MeshNode * n1,
2333 const SMDS_MeshNode * n2)
2335 double angle = 2. * M_PI; // bad angle
2338 SMESH::Controls::TSequenceOfXYZ P1, P2;
2339 if ( !SMESH::Controls::NumericalFunctor::GetPoints( tr1, P1 ) ||
2340 !SMESH::Controls::NumericalFunctor::GetPoints( tr2, P2 ))
2343 if(!tr1->IsQuadratic())
2344 N1 = gp_Vec( P1(2) - P1(1) ) ^ gp_Vec( P1(3) - P1(1) );
2346 N1 = gp_Vec( P1(3) - P1(1) ) ^ gp_Vec( P1(5) - P1(1) );
2347 if ( N1.SquareMagnitude() <= gp::Resolution() )
2349 if(!tr2->IsQuadratic())
2350 N2 = gp_Vec( P2(2) - P2(1) ) ^ gp_Vec( P2(3) - P2(1) );
2352 N2 = gp_Vec( P2(3) - P2(1) ) ^ gp_Vec( P2(5) - P2(1) );
2353 if ( N2.SquareMagnitude() <= gp::Resolution() )
2356 // find the first diagonal node n1 in the triangles:
2357 // take in account a diagonal link orientation
2358 const SMDS_MeshElement *nFirst[2], *tr[] = { tr1, tr2 };
2359 for ( int t = 0; t < 2; t++ ) {
2360 SMDS_ElemIteratorPtr it = tr[ t ]->nodesIterator();
2361 int i = 0, iDiag = -1;
2362 while ( it->more()) {
2363 const SMDS_MeshElement *n = it->next();
2364 if ( n == n1 || n == n2 ) {
2368 if ( i - iDiag == 1 )
2369 nFirst[ t ] = ( n == n1 ? n2 : n1 );
2378 if ( nFirst[ 0 ] == nFirst[ 1 ] )
2381 angle = N1.Angle( N2 );
2386 // =================================================
2387 // class generating a unique ID for a pair of nodes
2388 // and able to return nodes by that ID
2389 // =================================================
2393 LinkID_Gen( const SMESHDS_Mesh* theMesh )
2394 :myMesh( theMesh ), myMaxID( theMesh->MaxNodeID() + 1)
2397 long GetLinkID (const SMDS_MeshNode * n1,
2398 const SMDS_MeshNode * n2) const
2400 return ( Min(n1->GetID(),n2->GetID()) * myMaxID + Max(n1->GetID(),n2->GetID()));
2403 bool GetNodes (const long theLinkID,
2404 const SMDS_MeshNode* & theNode1,
2405 const SMDS_MeshNode* & theNode2) const
2407 theNode1 = myMesh->FindNode( theLinkID / myMaxID );
2408 if ( !theNode1 ) return false;
2409 theNode2 = myMesh->FindNode( theLinkID % myMaxID );
2410 if ( !theNode2 ) return false;
2416 const SMESHDS_Mesh* myMesh;
2421 //=======================================================================
2422 //function : TriToQuad
2423 //purpose : Fuse neighbour triangles into quadrangles.
2424 // theCrit is used to select a neighbour to fuse with.
2425 // theMaxAngle is a max angle between element normals at which
2426 // fusion is still performed.
2427 //=======================================================================
2429 bool SMESH_MeshEditor::TriToQuad (TIDSortedElemSet & theElems,
2430 SMESH::Controls::NumericalFunctorPtr theCrit,
2431 const double theMaxAngle)
2433 myLastCreatedElems.Clear();
2434 myLastCreatedNodes.Clear();
2436 MESSAGE( "::TriToQuad()" );
2438 if ( !theCrit.get() )
2441 SMESHDS_Mesh * aMesh = GetMeshDS();
2443 // Prepare data for algo: build
2444 // 1. map of elements with their linkIDs
2445 // 2. map of linkIDs with their elements
2447 map< SMESH_TLink, list< const SMDS_MeshElement* > > mapLi_listEl;
2448 map< SMESH_TLink, list< const SMDS_MeshElement* > >::iterator itLE;
2449 map< const SMDS_MeshElement*, set< SMESH_TLink > > mapEl_setLi;
2450 map< const SMDS_MeshElement*, set< SMESH_TLink > >::iterator itEL;
2452 TIDSortedElemSet::iterator itElem;
2453 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
2455 const SMDS_MeshElement* elem = *itElem;
2456 if(!elem || elem->GetType() != SMDSAbs_Face ) continue;
2457 bool IsTria = ( elem->NbCornerNodes()==3 );
2458 if (!IsTria) continue;
2460 // retrieve element nodes
2461 const SMDS_MeshNode* aNodes [4];
2462 SMDS_NodeIteratorPtr itN = elem->nodeIterator();
2465 aNodes[ i++ ] = itN->next();
2466 aNodes[ 3 ] = aNodes[ 0 ];
2469 for ( i = 0; i < 3; i++ ) {
2470 SMESH_TLink link( aNodes[i], aNodes[i+1] );
2471 // check if elements sharing a link can be fused
2472 itLE = mapLi_listEl.find( link );
2473 if ( itLE != mapLi_listEl.end() ) {
2474 if ((*itLE).second.size() > 1 ) // consider only 2 elems adjacent by a link
2476 const SMDS_MeshElement* elem2 = (*itLE).second.front();
2477 //if ( FindShape( elem ) != FindShape( elem2 ))
2478 // continue; // do not fuse triangles laying on different shapes
2479 if ( getAngle( elem, elem2, aNodes[i], aNodes[i+1] ) > theMaxAngle )
2480 continue; // avoid making badly shaped quads
2481 (*itLE).second.push_back( elem );
2484 mapLi_listEl[ link ].push_back( elem );
2486 mapEl_setLi [ elem ].insert( link );
2489 // Clean the maps from the links shared by a sole element, ie
2490 // links to which only one element is bound in mapLi_listEl
2492 for ( itLE = mapLi_listEl.begin(); itLE != mapLi_listEl.end(); itLE++ ) {
2493 int nbElems = (*itLE).second.size();
2494 if ( nbElems < 2 ) {
2495 const SMDS_MeshElement* elem = (*itLE).second.front();
2496 SMESH_TLink link = (*itLE).first;
2497 mapEl_setLi[ elem ].erase( link );
2498 if ( mapEl_setLi[ elem ].empty() )
2499 mapEl_setLi.erase( elem );
2503 // Algo: fuse triangles into quadrangles
2505 while ( ! mapEl_setLi.empty() ) {
2506 // Look for the start element:
2507 // the element having the least nb of shared links
2508 const SMDS_MeshElement* startElem = 0;
2510 for ( itEL = mapEl_setLi.begin(); itEL != mapEl_setLi.end(); itEL++ ) {
2511 int nbLinks = (*itEL).second.size();
2512 if ( nbLinks < minNbLinks ) {
2513 startElem = (*itEL).first;
2514 minNbLinks = nbLinks;
2515 if ( minNbLinks == 1 )
2520 // search elements to fuse starting from startElem or links of elements
2521 // fused earlyer - startLinks
2522 list< SMESH_TLink > startLinks;
2523 while ( startElem || !startLinks.empty() ) {
2524 while ( !startElem && !startLinks.empty() ) {
2525 // Get an element to start, by a link
2526 SMESH_TLink linkId = startLinks.front();
2527 startLinks.pop_front();
2528 itLE = mapLi_listEl.find( linkId );
2529 if ( itLE != mapLi_listEl.end() ) {
2530 list< const SMDS_MeshElement* > & listElem = (*itLE).second;
2531 list< const SMDS_MeshElement* >::iterator itE = listElem.begin();
2532 for ( ; itE != listElem.end() ; itE++ )
2533 if ( mapEl_setLi.find( (*itE) ) != mapEl_setLi.end() )
2535 mapLi_listEl.erase( itLE );
2540 // Get candidates to be fused
2541 const SMDS_MeshElement *tr1 = startElem, *tr2 = 0, *tr3 = 0;
2542 const SMESH_TLink *link12, *link13;
2544 ASSERT( mapEl_setLi.find( tr1 ) != mapEl_setLi.end() );
2545 set< SMESH_TLink >& setLi = mapEl_setLi[ tr1 ];
2546 ASSERT( !setLi.empty() );
2547 set< SMESH_TLink >::iterator itLi;
2548 for ( itLi = setLi.begin(); itLi != setLi.end(); itLi++ )
2550 const SMESH_TLink & link = (*itLi);
2551 itLE = mapLi_listEl.find( link );
2552 if ( itLE == mapLi_listEl.end() )
2555 const SMDS_MeshElement* elem = (*itLE).second.front();
2557 elem = (*itLE).second.back();
2558 mapLi_listEl.erase( itLE );
2559 if ( mapEl_setLi.find( elem ) == mapEl_setLi.end())
2570 // add other links of elem to list of links to re-start from
2571 set< SMESH_TLink >& links = mapEl_setLi[ elem ];
2572 set< SMESH_TLink >::iterator it;
2573 for ( it = links.begin(); it != links.end(); it++ ) {
2574 const SMESH_TLink& link2 = (*it);
2575 if ( link2 != link )
2576 startLinks.push_back( link2 );
2580 // Get nodes of possible quadrangles
2581 const SMDS_MeshNode *n12 [4], *n13 [4];
2582 bool Ok12 = false, Ok13 = false;
2583 const SMDS_MeshNode *linkNode1, *linkNode2;
2585 linkNode1 = link12->first;
2586 linkNode2 = link12->second;
2587 if ( tr2 && getQuadrangleNodes( n12, linkNode1, linkNode2, tr1, tr2 ))
2591 linkNode1 = link13->first;
2592 linkNode2 = link13->second;
2593 if ( tr3 && getQuadrangleNodes( n13, linkNode1, linkNode2, tr1, tr3 ))
2597 // Choose a pair to fuse
2598 if ( Ok12 && Ok13 ) {
2599 SMDS_FaceOfNodes quad12 ( n12[ 0 ], n12[ 1 ], n12[ 2 ], n12[ 3 ] );
2600 SMDS_FaceOfNodes quad13 ( n13[ 0 ], n13[ 1 ], n13[ 2 ], n13[ 3 ] );
2601 double aBadRate12 = getBadRate( &quad12, theCrit );
2602 double aBadRate13 = getBadRate( &quad13, theCrit );
2603 if ( aBadRate13 < aBadRate12 )
2610 // and remove fused elems and remove links from the maps
2611 mapEl_setLi.erase( tr1 );
2614 mapEl_setLi.erase( tr2 );
2615 mapLi_listEl.erase( *link12 );
2616 if ( tr1->NbNodes() == 3 )
2618 const SMDS_MeshElement* newElem = 0;
2619 newElem = aMesh->AddFace(n12[0], n12[1], n12[2], n12[3] );
2620 myLastCreatedElems.Append(newElem);
2621 AddToSameGroups( newElem, tr1, aMesh );
2622 int aShapeId = tr1->getshapeId();
2624 aMesh->SetMeshElementOnShape( newElem, aShapeId );
2625 aMesh->RemoveElement( tr1 );
2626 aMesh->RemoveElement( tr2 );
2629 vector< const SMDS_MeshNode* > N1;
2630 vector< const SMDS_MeshNode* > N2;
2631 getNodesFromTwoTria(tr1,tr2,N1,N2);
2632 // now we receive following N1 and N2 (using numeration as in image in InverseDiag())
2633 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
2634 // i.e. first nodes from both arrays form a new diagonal
2635 const SMDS_MeshNode* aNodes[8];
2644 const SMDS_MeshElement* newElem = 0;
2645 if ( N1.size() == 7 || N2.size() == 7 ) // biquadratic
2646 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
2647 aNodes[4], aNodes[5], aNodes[6], aNodes[7], N1[4]);
2649 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
2650 aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
2651 myLastCreatedElems.Append(newElem);
2652 AddToSameGroups( newElem, tr1, aMesh );
2653 int aShapeId = tr1->getshapeId();
2655 aMesh->SetMeshElementOnShape( newElem, aShapeId );
2656 aMesh->RemoveElement( tr1 );
2657 aMesh->RemoveElement( tr2 );
2658 // remove middle node (9)
2659 if ( N1[4]->NbInverseElements() == 0 )
2660 aMesh->RemoveNode( N1[4] );
2661 if ( N1.size() == 7 && N1[6]->NbInverseElements() == 0 )
2662 aMesh->RemoveNode( N1[6] );
2663 if ( N2.size() == 7 && N2[6]->NbInverseElements() == 0 )
2664 aMesh->RemoveNode( N2[6] );
2669 mapEl_setLi.erase( tr3 );
2670 mapLi_listEl.erase( *link13 );
2671 if ( tr1->NbNodes() == 3 ) {
2672 const SMDS_MeshElement* newElem = 0;
2673 newElem = aMesh->AddFace(n13[0], n13[1], n13[2], n13[3] );
2674 myLastCreatedElems.Append(newElem);
2675 AddToSameGroups( newElem, tr1, aMesh );
2676 int aShapeId = tr1->getshapeId();
2678 aMesh->SetMeshElementOnShape( newElem, aShapeId );
2679 aMesh->RemoveElement( tr1 );
2680 aMesh->RemoveElement( tr3 );
2683 vector< const SMDS_MeshNode* > N1;
2684 vector< const SMDS_MeshNode* > N2;
2685 getNodesFromTwoTria(tr1,tr3,N1,N2);
2686 // now we receive following N1 and N2 (using numeration as above image)
2687 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
2688 // i.e. first nodes from both arrays form a new diagonal
2689 const SMDS_MeshNode* aNodes[8];
2698 const SMDS_MeshElement* newElem = 0;
2699 if ( N1.size() == 7 || N2.size() == 7 ) // biquadratic
2700 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
2701 aNodes[4], aNodes[5], aNodes[6], aNodes[7], N1[4]);
2703 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
2704 aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
2705 myLastCreatedElems.Append(newElem);
2706 AddToSameGroups( newElem, tr1, aMesh );
2707 int aShapeId = tr1->getshapeId();
2709 aMesh->SetMeshElementOnShape( newElem, aShapeId );
2710 aMesh->RemoveElement( tr1 );
2711 aMesh->RemoveElement( tr3 );
2712 // remove middle node (9)
2713 if ( N1[4]->NbInverseElements() == 0 )
2714 aMesh->RemoveNode( N1[4] );
2715 if ( N1.size() == 7 && N1[6]->NbInverseElements() == 0 )
2716 aMesh->RemoveNode( N1[6] );
2717 if ( N2.size() == 7 && N2[6]->NbInverseElements() == 0 )
2718 aMesh->RemoveNode( N2[6] );
2722 // Next element to fuse: the rejected one
2724 startElem = Ok12 ? tr3 : tr2;
2726 } // if ( startElem )
2727 } // while ( startElem || !startLinks.empty() )
2728 } // while ( ! mapEl_setLi.empty() )
2734 /*#define DUMPSO(txt) \
2735 // cout << txt << endl;
2736 //=============================================================================
2740 //=============================================================================
2741 static void swap( int i1, int i2, int idNodes[], gp_Pnt P[] )
2745 int tmp = idNodes[ i1 ];
2746 idNodes[ i1 ] = idNodes[ i2 ];
2747 idNodes[ i2 ] = tmp;
2748 gp_Pnt Ptmp = P[ i1 ];
2751 DUMPSO( i1 << "(" << idNodes[ i2 ] << ") <-> " << i2 << "(" << idNodes[ i1 ] << ")");
2754 //=======================================================================
2755 //function : SortQuadNodes
2756 //purpose : Set 4 nodes of a quadrangle face in a good order.
2757 // Swap 1<->2 or 2<->3 nodes and correspondingly return
2759 //=======================================================================
2761 int SMESH_MeshEditor::SortQuadNodes (const SMDS_Mesh * theMesh,
2766 for ( i = 0; i < 4; i++ ) {
2767 const SMDS_MeshNode *n = theMesh->FindNode( idNodes[i] );
2769 P[ i ].SetCoord( n->X(), n->Y(), n->Z() );
2772 gp_Vec V1(P[0], P[1]);
2773 gp_Vec V2(P[0], P[2]);
2774 gp_Vec V3(P[0], P[3]);
2776 gp_Vec Cross1 = V1 ^ V2;
2777 gp_Vec Cross2 = V2 ^ V3;
2780 if (Cross1.Dot(Cross2) < 0)
2785 if (Cross1.Dot(Cross2) < 0)
2789 swap ( i, i + 1, idNodes, P );
2791 // for ( int ii = 0; ii < 4; ii++ ) {
2792 // const SMDS_MeshNode *n = theMesh->FindNode( idNodes[ii] );
2793 // DUMPSO( ii << "(" << idNodes[ii] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
2799 //=======================================================================
2800 //function : SortHexaNodes
2801 //purpose : Set 8 nodes of a hexahedron in a good order.
2802 // Return success status
2803 //=======================================================================
2805 bool SMESH_MeshEditor::SortHexaNodes (const SMDS_Mesh * theMesh,
2810 DUMPSO( "INPUT: ========================================");
2811 for ( i = 0; i < 8; i++ ) {
2812 const SMDS_MeshNode *n = theMesh->FindNode( idNodes[i] );
2813 if ( !n ) return false;
2814 P[ i ].SetCoord( n->X(), n->Y(), n->Z() );
2815 DUMPSO( i << "(" << idNodes[i] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
2817 DUMPSO( "========================================");
2820 set<int> faceNodes; // ids of bottom face nodes, to be found
2821 set<int> checkedId1; // ids of tried 2-nd nodes
2822 Standard_Real leastDist = DBL_MAX; // dist of the 4-th node from 123 plane
2823 const Standard_Real tol = 1.e-6; // tolerance to find nodes in plane
2824 int iMin, iLoop1 = 0;
2826 // Loop to try the 2-nd nodes
2828 while ( leastDist > DBL_MIN && ++iLoop1 < 8 )
2830 // Find not checked 2-nd node
2831 for ( i = 1; i < 8; i++ )
2832 if ( checkedId1.find( idNodes[i] ) == checkedId1.end() ) {
2833 int id1 = idNodes[i];
2834 swap ( 1, i, idNodes, P );
2835 checkedId1.insert ( id1 );
2839 // Find the 3-d node so that 1-2-3 triangle to be on a hexa face,
2840 // ie that all but meybe one (id3 which is on the same face) nodes
2841 // lay on the same side from the triangle plane.
2843 bool manyInPlane = false; // more than 4 nodes lay in plane
2845 while ( ++iLoop2 < 6 ) {
2847 // get 1-2-3 plane coeffs
2848 Standard_Real A, B, C, D;
2849 gp_Vec N = gp_Vec (P[0], P[1]).Crossed( gp_Vec (P[0], P[2]) );
2850 if ( N.SquareMagnitude() > gp::Resolution() )
2852 gp_Pln pln ( P[0], N );
2853 pln.Coefficients( A, B, C, D );
2855 // find the node (iMin) closest to pln
2856 Standard_Real dist[ 8 ], minDist = DBL_MAX;
2858 for ( i = 3; i < 8; i++ ) {
2859 dist[i] = A * P[i].X() + B * P[i].Y() + C * P[i].Z() + D;
2860 if ( fabs( dist[i] ) < minDist ) {
2861 minDist = fabs( dist[i] );
2864 if ( fabs( dist[i] ) <= tol )
2865 idInPln.insert( idNodes[i] );
2868 // there should not be more than 4 nodes in bottom plane
2869 if ( idInPln.size() > 1 )
2871 DUMPSO( "### idInPln.size() = " << idInPln.size());
2872 // idInPlane does not contain the first 3 nodes
2873 if ( manyInPlane || idInPln.size() == 5)
2874 return false; // all nodes in one plane
2877 // set the 1-st node to be not in plane
2878 for ( i = 3; i < 8; i++ ) {
2879 if ( idInPln.find( idNodes[ i ] ) == idInPln.end() ) {
2880 DUMPSO( "### Reset 0-th node");
2881 swap( 0, i, idNodes, P );
2886 // reset to re-check second nodes
2887 leastDist = DBL_MAX;
2891 break; // from iLoop2;
2894 // check that the other 4 nodes are on the same side
2895 bool sameSide = true;
2896 bool isNeg = dist[ iMin == 3 ? 4 : 3 ] <= 0.;
2897 for ( i = 3; sameSide && i < 8; i++ ) {
2899 sameSide = ( isNeg == dist[i] <= 0.);
2902 // keep best solution
2903 if ( sameSide && minDist < leastDist ) {
2904 leastDist = minDist;
2906 faceNodes.insert( idNodes[ 1 ] );
2907 faceNodes.insert( idNodes[ 2 ] );
2908 faceNodes.insert( idNodes[ iMin ] );
2909 DUMPSO( "loop " << iLoop2 << " id2 " << idNodes[ 1 ] << " id3 " << idNodes[ 2 ]
2910 << " leastDist = " << leastDist);
2911 if ( leastDist <= DBL_MIN )
2916 // set next 3-d node to check
2917 int iNext = 2 + iLoop2;
2919 DUMPSO( "Try 2-nd");
2920 swap ( 2, iNext, idNodes, P );
2922 } // while ( iLoop2 < 6 )
2925 if ( faceNodes.empty() ) return false;
2927 // Put the faceNodes in proper places
2928 for ( i = 4; i < 8; i++ ) {
2929 if ( faceNodes.find( idNodes[ i ] ) != faceNodes.end() ) {
2930 // find a place to put
2932 while ( faceNodes.find( idNodes[ iTo ] ) != faceNodes.end() )
2934 DUMPSO( "Set faceNodes");
2935 swap ( iTo, i, idNodes, P );
2940 // Set nodes of the found bottom face in good order
2941 DUMPSO( " Found bottom face: ");
2942 i = SortQuadNodes( theMesh, idNodes );
2944 gp_Pnt Ptmp = P[ i ];
2949 // for ( int ii = 0; ii < 4; ii++ ) {
2950 // const SMDS_MeshNode *n = theMesh->FindNode( idNodes[ii] );
2951 // DUMPSO( ii << "(" << idNodes[ii] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
2954 // Gravity center of the top and bottom faces
2955 gp_Pnt aGCb = ( P[0].XYZ() + P[1].XYZ() + P[2].XYZ() + P[3].XYZ() ) / 4.;
2956 gp_Pnt aGCt = ( P[4].XYZ() + P[5].XYZ() + P[6].XYZ() + P[7].XYZ() ) / 4.;
2958 // Get direction from the bottom to the top face
2959 gp_Vec upDir ( aGCb, aGCt );
2960 Standard_Real upDirSize = upDir.Magnitude();
2961 if ( upDirSize <= gp::Resolution() ) return false;
2964 // Assure that the bottom face normal points up
2965 gp_Vec Nb = gp_Vec (P[0], P[1]).Crossed( gp_Vec (P[0], P[2]) );
2966 Nb += gp_Vec (P[0], P[2]).Crossed( gp_Vec (P[0], P[3]) );
2967 if ( Nb.Dot( upDir ) < 0 ) {
2968 DUMPSO( "Reverse bottom face");
2969 swap( 1, 3, idNodes, P );
2972 // Find 5-th node - the one closest to the 1-st among the last 4 nodes.
2973 Standard_Real minDist = DBL_MAX;
2974 for ( i = 4; i < 8; i++ ) {
2975 // projection of P[i] to the plane defined by P[0] and upDir
2976 gp_Pnt Pp = P[i].Translated( upDir * ( upDir.Dot( gp_Vec( P[i], P[0] ))));
2977 Standard_Real sqDist = P[0].SquareDistance( Pp );
2978 if ( sqDist < minDist ) {
2983 DUMPSO( "Set 4-th");
2984 swap ( 4, iMin, idNodes, P );
2986 // Set nodes of the top face in good order
2987 DUMPSO( "Sort top face");
2988 i = SortQuadNodes( theMesh, &idNodes[4] );
2991 gp_Pnt Ptmp = P[ i ];
2996 // Assure that direction of the top face normal is from the bottom face
2997 gp_Vec Nt = gp_Vec (P[4], P[5]).Crossed( gp_Vec (P[4], P[6]) );
2998 Nt += gp_Vec (P[4], P[6]).Crossed( gp_Vec (P[4], P[7]) );
2999 if ( Nt.Dot( upDir ) < 0 ) {
3000 DUMPSO( "Reverse top face");
3001 swap( 5, 7, idNodes, P );
3004 // DUMPSO( "OUTPUT: ========================================");
3005 // for ( i = 0; i < 8; i++ ) {
3006 // float *p = ugrid->GetPoint(idNodes[i]);
3007 // DUMPSO( i << "(" << idNodes[i] << ") : " << p[0] << " " << p[1] << " " << p[2]);
3013 //================================================================================
3015 * \brief Return nodes linked to the given one
3016 * \param theNode - the node
3017 * \param linkedNodes - the found nodes
3018 * \param type - the type of elements to check
3020 * Medium nodes are ignored
3022 //================================================================================
3024 void SMESH_MeshEditor::GetLinkedNodes( const SMDS_MeshNode* theNode,
3025 TIDSortedElemSet & linkedNodes,
3026 SMDSAbs_ElementType type )
3028 SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(type);
3029 while ( elemIt->more() )
3031 const SMDS_MeshElement* elem = elemIt->next();
3032 if(elem->GetType() == SMDSAbs_0DElement)
3035 SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
3036 if ( elem->GetType() == SMDSAbs_Volume )
3038 SMDS_VolumeTool vol( elem );
3039 while ( nodeIt->more() ) {
3040 const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
3041 if ( theNode != n && vol.IsLinked( theNode, n ))
3042 linkedNodes.insert( n );
3047 for ( int i = 0; nodeIt->more(); ++i ) {
3048 const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
3049 if ( n == theNode ) {
3050 int iBefore = i - 1;
3052 if ( elem->IsQuadratic() ) {
3053 int nb = elem->NbNodes() / 2;
3054 iAfter = SMESH_MesherHelper::WrapIndex( iAfter, nb );
3055 iBefore = SMESH_MesherHelper::WrapIndex( iBefore, nb );
3057 linkedNodes.insert( elem->GetNodeWrap( iAfter ));
3058 linkedNodes.insert( elem->GetNodeWrap( iBefore ));
3065 //=======================================================================
3066 //function : laplacianSmooth
3067 //purpose : pulls theNode toward the center of surrounding nodes directly
3068 // connected to that node along an element edge
3069 //=======================================================================
3071 void laplacianSmooth(const SMDS_MeshNode* theNode,
3072 const Handle(Geom_Surface)& theSurface,
3073 map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
3075 // find surrounding nodes
3077 TIDSortedElemSet nodeSet;
3078 SMESH_MeshEditor::GetLinkedNodes( theNode, nodeSet, SMDSAbs_Face );
3080 // compute new coodrs
3082 double coord[] = { 0., 0., 0. };
3083 TIDSortedElemSet::iterator nodeSetIt = nodeSet.begin();
3084 for ( ; nodeSetIt != nodeSet.end(); nodeSetIt++ ) {
3085 const SMDS_MeshNode* node = cast2Node(*nodeSetIt);
3086 if ( theSurface.IsNull() ) { // smooth in 3D
3087 coord[0] += node->X();
3088 coord[1] += node->Y();
3089 coord[2] += node->Z();
3091 else { // smooth in 2D
3092 ASSERT( theUVMap.find( node ) != theUVMap.end() );
3093 gp_XY* uv = theUVMap[ node ];
3094 coord[0] += uv->X();
3095 coord[1] += uv->Y();
3098 int nbNodes = nodeSet.size();
3101 coord[0] /= nbNodes;
3102 coord[1] /= nbNodes;
3104 if ( !theSurface.IsNull() ) {
3105 ASSERT( theUVMap.find( theNode ) != theUVMap.end() );
3106 theUVMap[ theNode ]->SetCoord( coord[0], coord[1] );
3107 gp_Pnt p3d = theSurface->Value( coord[0], coord[1] );
3113 coord[2] /= nbNodes;
3117 const_cast< SMDS_MeshNode* >( theNode )->setXYZ(coord[0],coord[1],coord[2]);
3120 //=======================================================================
3121 //function : centroidalSmooth
3122 //purpose : pulls theNode toward the element-area-weighted centroid of the
3123 // surrounding elements
3124 //=======================================================================
3126 void centroidalSmooth(const SMDS_MeshNode* theNode,
3127 const Handle(Geom_Surface)& theSurface,
3128 map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
3130 gp_XYZ aNewXYZ(0.,0.,0.);
3131 SMESH::Controls::Area anAreaFunc;
3132 double totalArea = 0.;
3137 SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(SMDSAbs_Face);
3138 while ( elemIt->more() )
3140 const SMDS_MeshElement* elem = elemIt->next();
3143 gp_XYZ elemCenter(0.,0.,0.);
3144 SMESH::Controls::TSequenceOfXYZ aNodePoints;
3145 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3146 int nn = elem->NbNodes();
3147 if(elem->IsQuadratic()) nn = nn/2;
3149 //while ( itN->more() ) {
3151 const SMDS_MeshNode* aNode = static_cast<const SMDS_MeshNode*>( itN->next() );
3153 gp_XYZ aP( aNode->X(), aNode->Y(), aNode->Z() );
3154 aNodePoints.push_back( aP );
3155 if ( !theSurface.IsNull() ) { // smooth in 2D
3156 ASSERT( theUVMap.find( aNode ) != theUVMap.end() );
3157 gp_XY* uv = theUVMap[ aNode ];
3158 aP.SetCoord( uv->X(), uv->Y(), 0. );
3162 double elemArea = anAreaFunc.GetValue( aNodePoints );
3163 totalArea += elemArea;
3165 aNewXYZ += elemCenter * elemArea;
3167 aNewXYZ /= totalArea;
3168 if ( !theSurface.IsNull() ) {
3169 theUVMap[ theNode ]->SetCoord( aNewXYZ.X(), aNewXYZ.Y() );
3170 aNewXYZ = theSurface->Value( aNewXYZ.X(), aNewXYZ.Y() ).XYZ();
3175 const_cast< SMDS_MeshNode* >( theNode )->setXYZ(aNewXYZ.X(),aNewXYZ.Y(),aNewXYZ.Z());
3178 //=======================================================================
3179 //function : getClosestUV
3180 //purpose : return UV of closest projection
3181 //=======================================================================
3183 static bool getClosestUV (Extrema_GenExtPS& projector,
3184 const gp_Pnt& point,
3187 projector.Perform( point );
3188 if ( projector.IsDone() ) {
3189 double u, v, minVal = DBL_MAX;
3190 for ( int i = projector.NbExt(); i > 0; i-- )
3191 #if OCC_VERSION_LARGE > 0x06040000 // Porting to OCCT6.5.1
3192 if ( projector.SquareDistance( i ) < minVal ) {
3193 minVal = projector.SquareDistance( i );
3195 if ( projector.Value( i ) < minVal ) {
3196 minVal = projector.Value( i );
3198 projector.Point( i ).Parameter( u, v );
3200 result.SetCoord( u, v );
3206 //=======================================================================
3208 //purpose : Smooth theElements during theNbIterations or until a worst
3209 // element has aspect ratio <= theTgtAspectRatio.
3210 // Aspect Ratio varies in range [1.0, inf].
3211 // If theElements is empty, the whole mesh is smoothed.
3212 // theFixedNodes contains additionally fixed nodes. Nodes built
3213 // on edges and boundary nodes are always fixed.
3214 //=======================================================================
3216 void SMESH_MeshEditor::Smooth (TIDSortedElemSet & theElems,
3217 set<const SMDS_MeshNode*> & theFixedNodes,
3218 const SmoothMethod theSmoothMethod,
3219 const int theNbIterations,
3220 double theTgtAspectRatio,
3223 myLastCreatedElems.Clear();
3224 myLastCreatedNodes.Clear();
3226 MESSAGE((theSmoothMethod==LAPLACIAN ? "LAPLACIAN" : "CENTROIDAL") << "--::Smooth()");
3228 if ( theTgtAspectRatio < 1.0 )
3229 theTgtAspectRatio = 1.0;
3231 const double disttol = 1.e-16;
3233 SMESH::Controls::AspectRatio aQualityFunc;
3235 SMESHDS_Mesh* aMesh = GetMeshDS();
3237 if ( theElems.empty() ) {
3238 // add all faces to theElems
3239 SMDS_FaceIteratorPtr fIt = aMesh->facesIterator();
3240 while ( fIt->more() ) {
3241 const SMDS_MeshElement* face = fIt->next();
3242 theElems.insert( theElems.end(), face );
3245 // get all face ids theElems are on
3246 set< int > faceIdSet;
3247 TIDSortedElemSet::iterator itElem;
3249 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
3250 int fId = FindShape( *itElem );
3251 // check that corresponding submesh exists and a shape is face
3253 faceIdSet.find( fId ) == faceIdSet.end() &&
3254 aMesh->MeshElements( fId )) {
3255 TopoDS_Shape F = aMesh->IndexToShape( fId );
3256 if ( !F.IsNull() && F.ShapeType() == TopAbs_FACE )
3257 faceIdSet.insert( fId );
3260 faceIdSet.insert( 0 ); // to smooth elements that are not on any TopoDS_Face
3262 // ===============================================
3263 // smooth elements on each TopoDS_Face separately
3264 // ===============================================
3266 set< int >::reverse_iterator fId = faceIdSet.rbegin(); // treate 0 fId at the end
3267 for ( ; fId != faceIdSet.rend(); ++fId ) {
3268 // get face surface and submesh
3269 Handle(Geom_Surface) surface;
3270 SMESHDS_SubMesh* faceSubMesh = 0;
3272 double fToler2 = 0, f,l;
3273 double u1 = 0, u2 = 0, v1 = 0, v2 = 0;
3274 bool isUPeriodic = false, isVPeriodic = false;
3276 face = TopoDS::Face( aMesh->IndexToShape( *fId ));
3277 surface = BRep_Tool::Surface( face );
3278 faceSubMesh = aMesh->MeshElements( *fId );
3279 fToler2 = BRep_Tool::Tolerance( face );
3280 fToler2 *= fToler2 * 10.;
3281 isUPeriodic = surface->IsUPeriodic();
3284 isVPeriodic = surface->IsVPeriodic();
3287 surface->Bounds( u1, u2, v1, v2 );
3289 // ---------------------------------------------------------
3290 // for elements on a face, find movable and fixed nodes and
3291 // compute UV for them
3292 // ---------------------------------------------------------
3293 bool checkBoundaryNodes = false;
3294 bool isQuadratic = false;
3295 set<const SMDS_MeshNode*> setMovableNodes;
3296 map< const SMDS_MeshNode*, gp_XY* > uvMap, uvMap2;
3297 list< gp_XY > listUV; // uvs the 2 uvMaps refer to
3298 list< const SMDS_MeshElement* > elemsOnFace;
3300 Extrema_GenExtPS projector;
3301 GeomAdaptor_Surface surfAdaptor;
3302 if ( !surface.IsNull() ) {
3303 surfAdaptor.Load( surface );
3304 projector.Initialize( surfAdaptor, 20,20, 1e-5,1e-5 );
3306 int nbElemOnFace = 0;
3307 itElem = theElems.begin();
3308 // loop on not yet smoothed elements: look for elems on a face
3309 while ( itElem != theElems.end() ) {
3310 if ( faceSubMesh && nbElemOnFace == faceSubMesh->NbElements() )
3311 break; // all elements found
3313 const SMDS_MeshElement* elem = *itElem;
3314 if ( !elem || elem->GetType() != SMDSAbs_Face || elem->NbNodes() < 3 ||
3315 ( faceSubMesh && !faceSubMesh->Contains( elem ))) {
3319 elemsOnFace.push_back( elem );
3320 theElems.erase( itElem++ );
3324 isQuadratic = elem->IsQuadratic();
3326 // get movable nodes of elem
3327 const SMDS_MeshNode* node;
3328 SMDS_TypeOfPosition posType;
3329 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3330 int nn = 0, nbn = elem->NbNodes();
3331 if(elem->IsQuadratic())
3333 while ( nn++ < nbn ) {
3334 node = static_cast<const SMDS_MeshNode*>( itN->next() );
3335 const SMDS_PositionPtr& pos = node->GetPosition();
3336 posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
3337 if (posType != SMDS_TOP_EDGE &&
3338 posType != SMDS_TOP_VERTEX &&
3339 theFixedNodes.find( node ) == theFixedNodes.end())
3341 // check if all faces around the node are on faceSubMesh
3342 // because a node on edge may be bound to face
3343 SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
3345 if ( faceSubMesh ) {
3346 while ( eIt->more() && all ) {
3347 const SMDS_MeshElement* e = eIt->next();
3348 all = faceSubMesh->Contains( e );
3352 setMovableNodes.insert( node );
3354 checkBoundaryNodes = true;
3356 if ( posType == SMDS_TOP_3DSPACE )
3357 checkBoundaryNodes = true;
3360 if ( surface.IsNull() )
3363 // get nodes to check UV
3364 list< const SMDS_MeshNode* > uvCheckNodes;
3365 itN = elem->nodesIterator();
3366 nn = 0; nbn = elem->NbNodes();
3367 if(elem->IsQuadratic())
3369 while ( nn++ < nbn ) {
3370 node = static_cast<const SMDS_MeshNode*>( itN->next() );
3371 if ( uvMap.find( node ) == uvMap.end() )
3372 uvCheckNodes.push_back( node );
3373 // add nodes of elems sharing node
3374 // SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
3375 // while ( eIt->more() ) {
3376 // const SMDS_MeshElement* e = eIt->next();
3377 // if ( e != elem ) {
3378 // SMDS_ElemIteratorPtr nIt = e->nodesIterator();
3379 // while ( nIt->more() ) {
3380 // const SMDS_MeshNode* n =
3381 // static_cast<const SMDS_MeshNode*>( nIt->next() );
3382 // if ( uvMap.find( n ) == uvMap.end() )
3383 // uvCheckNodes.push_back( n );
3389 list< const SMDS_MeshNode* >::iterator n = uvCheckNodes.begin();
3390 for ( ; n != uvCheckNodes.end(); ++n ) {
3393 const SMDS_PositionPtr& pos = node->GetPosition();
3394 posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
3396 switch ( posType ) {
3397 case SMDS_TOP_FACE: {
3398 SMDS_FacePosition* fPos = ( SMDS_FacePosition* ) pos;
3399 uv.SetCoord( fPos->GetUParameter(), fPos->GetVParameter() );
3402 case SMDS_TOP_EDGE: {
3403 TopoDS_Shape S = aMesh->IndexToShape( node->getshapeId() );
3404 Handle(Geom2d_Curve) pcurve;
3405 if ( !S.IsNull() && S.ShapeType() == TopAbs_EDGE )
3406 pcurve = BRep_Tool::CurveOnSurface( TopoDS::Edge( S ), face, f,l );
3407 if ( !pcurve.IsNull() ) {
3408 double u = (( SMDS_EdgePosition* ) pos )->GetUParameter();
3409 uv = pcurve->Value( u ).XY();
3413 case SMDS_TOP_VERTEX: {
3414 TopoDS_Shape S = aMesh->IndexToShape( node->getshapeId() );
3415 if ( !S.IsNull() && S.ShapeType() == TopAbs_VERTEX )
3416 uv = BRep_Tool::Parameters( TopoDS::Vertex( S ), face ).XY();
3421 // check existing UV
3422 bool project = true;
3423 gp_Pnt pNode ( node->X(), node->Y(), node->Z() );
3424 double dist1 = DBL_MAX, dist2 = 0;
3425 if ( posType != SMDS_TOP_3DSPACE ) {
3426 dist1 = pNode.SquareDistance( surface->Value( uv.X(), uv.Y() ));
3427 project = dist1 > fToler2;
3429 if ( project ) { // compute new UV
3431 if ( !getClosestUV( projector, pNode, newUV )) {
3432 MESSAGE("Node Projection Failed " << node);
3436 newUV.SetX( ElCLib::InPeriod( newUV.X(), u1, u2 ));
3438 newUV.SetY( ElCLib::InPeriod( newUV.Y(), v1, v2 ));
3440 if ( posType != SMDS_TOP_3DSPACE )
3441 dist2 = pNode.SquareDistance( surface->Value( newUV.X(), newUV.Y() ));
3442 if ( dist2 < dist1 )
3446 // store UV in the map
3447 listUV.push_back( uv );
3448 uvMap.insert( make_pair( node, &listUV.back() ));
3450 } // loop on not yet smoothed elements
3452 if ( !faceSubMesh || nbElemOnFace != faceSubMesh->NbElements() )
3453 checkBoundaryNodes = true;
3455 // fix nodes on mesh boundary
3457 if ( checkBoundaryNodes ) {
3458 map< SMESH_TLink, int > linkNbMap; // how many times a link encounters in elemsOnFace
3459 map< SMESH_TLink, int >::iterator link_nb;
3460 // put all elements links to linkNbMap
3461 list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
3462 for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
3463 const SMDS_MeshElement* elem = (*elemIt);
3464 int nbn = elem->NbCornerNodes();
3465 // loop on elem links: insert them in linkNbMap
3466 for ( int iN = 0; iN < nbn; ++iN ) {
3467 const SMDS_MeshNode* n1 = elem->GetNode( iN );
3468 const SMDS_MeshNode* n2 = elem->GetNode(( iN+1 ) % nbn);
3469 SMESH_TLink link( n1, n2 );
3470 link_nb = linkNbMap.insert( make_pair( link, 0 )).first;
3474 // remove nodes that are in links encountered only once from setMovableNodes
3475 for ( link_nb = linkNbMap.begin(); link_nb != linkNbMap.end(); ++link_nb ) {
3476 if ( link_nb->second == 1 ) {
3477 setMovableNodes.erase( link_nb->first.node1() );
3478 setMovableNodes.erase( link_nb->first.node2() );
3483 // -----------------------------------------------------
3484 // for nodes on seam edge, compute one more UV ( uvMap2 );
3485 // find movable nodes linked to nodes on seam and which
3486 // are to be smoothed using the second UV ( uvMap2 )
3487 // -----------------------------------------------------
3489 set<const SMDS_MeshNode*> nodesNearSeam; // to smooth using uvMap2
3490 if ( !surface.IsNull() ) {
3491 TopExp_Explorer eExp( face, TopAbs_EDGE );
3492 for ( ; eExp.More(); eExp.Next() ) {
3493 TopoDS_Edge edge = TopoDS::Edge( eExp.Current() );
3494 if ( !BRep_Tool::IsClosed( edge, face ))
3496 SMESHDS_SubMesh* sm = aMesh->MeshElements( edge );
3497 if ( !sm ) continue;
3498 // find out which parameter varies for a node on seam
3501 Handle(Geom2d_Curve) pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
3502 if ( pcurve.IsNull() ) continue;
3503 uv1 = pcurve->Value( f );
3505 pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
3506 if ( pcurve.IsNull() ) continue;
3507 uv2 = pcurve->Value( f );
3508 int iPar = Abs( uv1.X() - uv2.X() ) > Abs( uv1.Y() - uv2.Y() ) ? 1 : 2;
3510 if ( uv1.Coord( iPar ) > uv2.Coord( iPar )) {
3511 gp_Pnt2d tmp = uv1; uv1 = uv2; uv2 = tmp;
3513 // get nodes on seam and its vertices
3514 list< const SMDS_MeshNode* > seamNodes;
3515 SMDS_NodeIteratorPtr nSeamIt = sm->GetNodes();
3516 while ( nSeamIt->more() ) {
3517 const SMDS_MeshNode* node = nSeamIt->next();
3518 if ( !isQuadratic || !IsMedium( node ))
3519 seamNodes.push_back( node );
3521 TopExp_Explorer vExp( edge, TopAbs_VERTEX );
3522 for ( ; vExp.More(); vExp.Next() ) {
3523 sm = aMesh->MeshElements( vExp.Current() );
3525 nSeamIt = sm->GetNodes();
3526 while ( nSeamIt->more() )
3527 seamNodes.push_back( nSeamIt->next() );
3530 // loop on nodes on seam
3531 list< const SMDS_MeshNode* >::iterator noSeIt = seamNodes.begin();
3532 for ( ; noSeIt != seamNodes.end(); ++noSeIt ) {
3533 const SMDS_MeshNode* nSeam = *noSeIt;
3534 map< const SMDS_MeshNode*, gp_XY* >::iterator n_uv = uvMap.find( nSeam );
3535 if ( n_uv == uvMap.end() )
3538 n_uv->second->SetCoord( iPar, uv1.Coord( iPar ));
3539 // set the second UV
3540 listUV.push_back( *n_uv->second );
3541 listUV.back().SetCoord( iPar, uv2.Coord( iPar ));
3542 if ( uvMap2.empty() )
3543 uvMap2 = uvMap; // copy the uvMap contents
3544 uvMap2[ nSeam ] = &listUV.back();
3546 // collect movable nodes linked to ones on seam in nodesNearSeam
3547 SMDS_ElemIteratorPtr eIt = nSeam->GetInverseElementIterator(SMDSAbs_Face);
3548 while ( eIt->more() ) {
3549 const SMDS_MeshElement* e = eIt->next();
3550 int nbUseMap1 = 0, nbUseMap2 = 0;
3551 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
3552 int nn = 0, nbn = e->NbNodes();
3553 if(e->IsQuadratic()) nbn = nbn/2;
3554 while ( nn++ < nbn )
3556 const SMDS_MeshNode* n =
3557 static_cast<const SMDS_MeshNode*>( nIt->next() );
3559 setMovableNodes.find( n ) == setMovableNodes.end() )
3561 // add only nodes being closer to uv2 than to uv1
3562 gp_Pnt pMid (0.5 * ( n->X() + nSeam->X() ),
3563 0.5 * ( n->Y() + nSeam->Y() ),
3564 0.5 * ( n->Z() + nSeam->Z() ));
3566 getClosestUV( projector, pMid, uv );
3567 if ( uv.Coord( iPar ) > uvMap[ n ]->Coord( iPar ) ) {
3568 nodesNearSeam.insert( n );
3574 // for centroidalSmooth all element nodes must
3575 // be on one side of a seam
3576 if ( theSmoothMethod == CENTROIDAL && nbUseMap1 && nbUseMap2 ) {
3577 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
3579 while ( nn++ < nbn ) {
3580 const SMDS_MeshNode* n =
3581 static_cast<const SMDS_MeshNode*>( nIt->next() );
3582 setMovableNodes.erase( n );
3586 } // loop on nodes on seam
3587 } // loop on edge of a face
3588 } // if ( !face.IsNull() )
3590 if ( setMovableNodes.empty() ) {
3591 MESSAGE( "Face id : " << *fId << " - NO SMOOTHING: no nodes to move!!!");
3592 continue; // goto next face
3600 double maxRatio = -1., maxDisplacement = -1.;
3601 set<const SMDS_MeshNode*>::iterator nodeToMove;
3602 for ( it = 0; it < theNbIterations; it++ ) {
3603 maxDisplacement = 0.;
3604 nodeToMove = setMovableNodes.begin();
3605 for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
3606 const SMDS_MeshNode* node = (*nodeToMove);
3607 gp_XYZ aPrevPos ( node->X(), node->Y(), node->Z() );
3610 bool map2 = ( nodesNearSeam.find( node ) != nodesNearSeam.end() );
3611 if ( theSmoothMethod == LAPLACIAN )
3612 laplacianSmooth( node, surface, map2 ? uvMap2 : uvMap );
3614 centroidalSmooth( node, surface, map2 ? uvMap2 : uvMap );
3616 // node displacement
3617 gp_XYZ aNewPos ( node->X(), node->Y(), node->Z() );
3618 Standard_Real aDispl = (aPrevPos - aNewPos).SquareModulus();
3619 if ( aDispl > maxDisplacement )
3620 maxDisplacement = aDispl;
3622 // no node movement => exit
3623 //if ( maxDisplacement < 1.e-16 ) {
3624 if ( maxDisplacement < disttol ) {
3625 MESSAGE("-- no node movement --");
3629 // check elements quality
3631 list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
3632 for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
3633 const SMDS_MeshElement* elem = (*elemIt);
3634 if ( !elem || elem->GetType() != SMDSAbs_Face )
3636 SMESH::Controls::TSequenceOfXYZ aPoints;
3637 if ( aQualityFunc.GetPoints( elem, aPoints )) {
3638 double aValue = aQualityFunc.GetValue( aPoints );
3639 if ( aValue > maxRatio )
3643 if ( maxRatio <= theTgtAspectRatio ) {
3644 MESSAGE("-- quality achived --");
3647 if (it+1 == theNbIterations) {
3648 MESSAGE("-- Iteration limit exceeded --");
3650 } // smoothing iterations
3652 MESSAGE(" Face id: " << *fId <<
3653 " Nb iterstions: " << it <<
3654 " Displacement: " << maxDisplacement <<
3655 " Aspect Ratio " << maxRatio);
3657 // ---------------------------------------
3658 // new nodes positions are computed,
3659 // record movement in DS and set new UV
3660 // ---------------------------------------
3661 nodeToMove = setMovableNodes.begin();
3662 for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
3663 SMDS_MeshNode* node = const_cast< SMDS_MeshNode* > (*nodeToMove);
3664 aMesh->MoveNode( node, node->X(), node->Y(), node->Z() );
3665 map< const SMDS_MeshNode*, gp_XY* >::iterator node_uv = uvMap.find( node );
3666 if ( node_uv != uvMap.end() ) {
3667 gp_XY* uv = node_uv->second;
3669 ( SMDS_PositionPtr( new SMDS_FacePosition( uv->X(), uv->Y() )));
3673 // move medium nodes of quadratic elements
3676 SMESH_MesherHelper helper( *GetMesh() );
3677 helper.SetSubShape( face );
3678 vector<const SMDS_MeshNode*> nodes;
3680 list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
3681 for ( ; elemIt != elemsOnFace.end(); ++elemIt )
3683 const SMDS_MeshElement* QF = *elemIt;
3684 if ( QF->IsQuadratic() )
3686 nodes.assign( SMDS_MeshElement::iterator( QF->interlacedNodesElemIterator() ),
3687 SMDS_MeshElement::iterator() );
3688 nodes.push_back( nodes[0] );
3690 for (size_t i = 1; i < nodes.size(); i += 2 ) // i points to a medium node
3692 if ( !surface.IsNull() )
3694 gp_XY uv1 = helper.GetNodeUV( face, nodes[i-1], nodes[i+1], &checkUV );
3695 gp_XY uv2 = helper.GetNodeUV( face, nodes[i+1], nodes[i-1], &checkUV );
3696 gp_XY uv = helper.GetMiddleUV( surface, uv1, uv2 );
3697 xyz = surface->Value( uv.X(), uv.Y() );
3700 xyz = 0.5 * ( SMESH_TNodeXYZ( nodes[i-1] ) + SMESH_TNodeXYZ( nodes[i+1] ));
3702 if (( SMESH_TNodeXYZ( nodes[i] ) - xyz.XYZ() ).Modulus() > disttol )
3703 // we have to move a medium node
3704 aMesh->MoveNode( nodes[i], xyz.X(), xyz.Y(), xyz.Z() );
3710 } // loop on face ids
3714 //=======================================================================
3715 //function : isReverse
3716 //purpose : Return true if normal of prevNodes is not co-directied with
3717 // gp_Vec(prevNodes[iNotSame],nextNodes[iNotSame]).
3718 // iNotSame is where prevNodes and nextNodes are different.
3719 // If result is true then future volume orientation is OK
3720 //=======================================================================
3722 static bool isReverse(const SMDS_MeshElement* face,
3723 const vector<const SMDS_MeshNode*>& prevNodes,
3724 const vector<const SMDS_MeshNode*>& nextNodes,
3728 SMESH_TNodeXYZ pP = prevNodes[ iNotSame ];
3729 SMESH_TNodeXYZ pN = nextNodes[ iNotSame ];
3730 gp_XYZ extrDir( pN - pP ), faceNorm;
3731 SMESH_MeshAlgos::FaceNormal( face, faceNorm, /*normalized=*/false );
3733 return faceNorm * extrDir < 0.0;
3736 //=======================================================================
3738 * \brief Create elements by sweeping an element
3739 * \param elem - element to sweep
3740 * \param newNodesItVec - nodes generated from each node of the element
3741 * \param newElems - generated elements
3742 * \param nbSteps - number of sweeping steps
3743 * \param srcElements - to append elem for each generated element
3745 //=======================================================================
3747 void SMESH_MeshEditor::sweepElement(const SMDS_MeshElement* elem,
3748 const vector<TNodeOfNodeListMapItr> & newNodesItVec,
3749 list<const SMDS_MeshElement*>& newElems,
3751 SMESH_SequenceOfElemPtr& srcElements)
3753 //MESSAGE("sweepElement " << nbSteps);
3754 SMESHDS_Mesh* aMesh = GetMeshDS();
3756 const int nbNodes = elem->NbNodes();
3757 const int nbCorners = elem->NbCornerNodes();
3758 SMDSAbs_EntityType baseType = elem->GetEntityType(); /* it can change in case of
3759 polyhedron creation !!! */
3760 // Loop on elem nodes:
3761 // find new nodes and detect same nodes indices
3762 vector < list< const SMDS_MeshNode* >::const_iterator > itNN( nbNodes );
3763 vector<const SMDS_MeshNode*> prevNod( nbNodes );
3764 vector<const SMDS_MeshNode*> nextNod( nbNodes );
3765 vector<const SMDS_MeshNode*> midlNod( nbNodes );
3767 int iNode, nbSame = 0, nbDouble = 0, iNotSameNode = 0;
3768 vector<int> sames(nbNodes);
3769 vector<bool> isSingleNode(nbNodes);
3771 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
3772 TNodeOfNodeListMapItr nnIt = newNodesItVec[ iNode ];
3773 const SMDS_MeshNode* node = nnIt->first;
3774 const list< const SMDS_MeshNode* > & listNewNodes = nnIt->second;
3775 if ( listNewNodes.empty() )
3778 itNN [ iNode ] = listNewNodes.begin();
3779 prevNod[ iNode ] = node;
3780 nextNod[ iNode ] = listNewNodes.front();
3782 isSingleNode[iNode] = (listNewNodes.size()==nbSteps); /* medium node of quadratic or
3783 corner node of linear */
3784 if ( prevNod[ iNode ] != nextNod [ iNode ])
3785 nbDouble += !isSingleNode[iNode];
3787 if( iNode < nbCorners ) { // check corners only
3788 if ( prevNod[ iNode ] == nextNod [ iNode ])
3789 sames[nbSame++] = iNode;
3791 iNotSameNode = iNode;
3795 if ( nbSame == nbNodes || nbSame > 2) {
3796 MESSAGE( " Too many same nodes of element " << elem->GetID() );
3800 if ( elem->GetType() == SMDSAbs_Face && !isReverse( elem, prevNod, nextNod, iNotSameNode ))
3802 // fix nodes order to have bottom normal external
3803 if ( baseType == SMDSEntity_Polygon )
3805 std::reverse( itNN.begin(), itNN.end() );
3806 std::reverse( prevNod.begin(), prevNod.end() );
3807 std::reverse( midlNod.begin(), midlNod.end() );
3808 std::reverse( nextNod.begin(), nextNod.end() );
3809 std::reverse( isSingleNode.begin(), isSingleNode.end() );
3813 const vector<int>& ind = SMDS_MeshCell::reverseSmdsOrder( baseType );
3814 SMDS_MeshCell::applyInterlace( ind, itNN );
3815 SMDS_MeshCell::applyInterlace( ind, prevNod );
3816 SMDS_MeshCell::applyInterlace( ind, nextNod );
3817 SMDS_MeshCell::applyInterlace( ind, midlNod );
3818 SMDS_MeshCell::applyInterlace( ind, isSingleNode );
3821 sames[nbSame] = iNotSameNode;
3822 for ( int j = 0; j <= nbSame; ++j )
3823 for ( size_t i = 0; i < ind.size(); ++i )
3824 if ( ind[i] == sames[j] )
3829 iNotSameNode = sames[nbSame];
3834 int iSameNode = 0, iBeforeSame = 0, iAfterSame = 0, iOpposSame = 0;
3836 iSameNode = sames[ nbSame-1 ];
3837 iBeforeSame = ( iSameNode + nbCorners - 1 ) % nbCorners;
3838 iAfterSame = ( iSameNode + 1 ) % nbCorners;
3839 iOpposSame = ( iSameNode - 2 < 0 ? iSameNode + 2 : iSameNode - 2 );
3842 // make new elements
3843 for (int iStep = 0; iStep < nbSteps; iStep++ )
3846 for ( iNode = 0; iNode < nbNodes; iNode++ )
3848 midlNod[ iNode ] = isSingleNode[iNode] ? 0 : *itNN[ iNode ]++;
3849 nextNod[ iNode ] = *itNN[ iNode ]++;
3852 SMDS_MeshElement* aNewElem = 0;
3853 /*if(!elem->IsPoly())*/ {
3854 switch ( baseType ) {
3856 case SMDSEntity_Node: { // sweep NODE
3857 if ( nbSame == 0 ) {
3858 if ( isSingleNode[0] )
3859 aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ] );
3861 aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ], midlNod[ 0 ] );
3867 case SMDSEntity_Edge: { // sweep EDGE
3868 if ( nbDouble == 0 )
3870 if ( nbSame == 0 ) // ---> quadrangle
3871 aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
3872 nextNod[ 1 ], nextNod[ 0 ] );
3873 else // ---> triangle
3874 aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
3875 nextNod[ iNotSameNode ] );
3877 else // ---> polygon
3879 vector<const SMDS_MeshNode*> poly_nodes;
3880 poly_nodes.push_back( prevNod[0] );
3881 poly_nodes.push_back( prevNod[1] );
3882 if ( prevNod[1] != nextNod[1] )
3884 if ( midlNod[1]) poly_nodes.push_back( midlNod[1]);
3885 poly_nodes.push_back( nextNod[1] );
3887 if ( prevNod[0] != nextNod[0] )
3889 poly_nodes.push_back( nextNod[0] );
3890 if ( midlNod[0]) poly_nodes.push_back( midlNod[0]);
3892 switch ( poly_nodes.size() ) {
3894 aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ], poly_nodes[ 2 ]);
3897 aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ],
3898 poly_nodes[ 2 ], poly_nodes[ 3 ]);
3901 aNewElem = aMesh->AddPolygonalFace (poly_nodes);
3906 case SMDSEntity_Triangle: // TRIANGLE --->
3908 if ( nbDouble > 0 ) break;
3909 if ( nbSame == 0 ) // ---> pentahedron
3910 aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
3911 nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ] );
3913 else if ( nbSame == 1 ) // ---> pyramid
3914 aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
3915 nextNod[ iAfterSame ], nextNod[ iBeforeSame ],
3916 nextNod[ iSameNode ]);
3918 else // 2 same nodes: ---> tetrahedron
3919 aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
3920 nextNod[ iNotSameNode ]);
3923 case SMDSEntity_Quad_Edge: // sweep quadratic EDGE --->
3927 if ( nbDouble+nbSame == 2 )
3929 if(nbSame==0) { // ---> quadratic quadrangle
3930 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
3931 prevNod[2], midlNod[1], nextNod[2], midlNod[0]);
3933 else { //(nbSame==1) // ---> quadratic triangle
3935 return; // medium node on axis
3937 else if(sames[0]==0)
3938 aNewElem = aMesh->AddFace(prevNod[0], nextNod[1], prevNod[1],
3939 nextNod[2], midlNod[1], prevNod[2]);
3941 aNewElem = aMesh->AddFace(prevNod[0], nextNod[0], prevNod[1],
3942 midlNod[0], nextNod[2], prevNod[2]);
3945 else if ( nbDouble == 3 )
3947 if ( nbSame == 0 ) { // ---> bi-quadratic quadrangle
3948 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
3949 prevNod[2], midlNod[1], nextNod[2], midlNod[0], midlNod[2]);
3956 case SMDSEntity_Quadrangle: { // sweep QUADRANGLE --->
3957 if ( nbDouble > 0 ) break;
3959 if ( nbSame == 0 ) // ---> hexahedron
3960 aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ], prevNod[ 3 ],
3961 nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ], nextNod[ 3 ]);
3963 else if ( nbSame == 1 ) { // ---> pyramid + pentahedron
3964 aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
3965 nextNod[ iAfterSame ], nextNod[ iBeforeSame ],
3966 nextNod[ iSameNode ]);
3967 newElems.push_back( aNewElem );
3968 aNewElem = aMesh->AddVolume (prevNod[ iAfterSame ], prevNod[ iOpposSame ],
3969 prevNod[ iBeforeSame ], nextNod[ iAfterSame ],
3970 nextNod[ iOpposSame ], nextNod[ iBeforeSame ] );
3972 else if ( nbSame == 2 ) { // ---> pentahedron
3973 if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] )
3974 // iBeforeSame is same too
3975 aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iOpposSame ],
3976 nextNod[ iOpposSame ], prevNod[ iSameNode ],
3977 prevNod[ iAfterSame ], nextNod[ iAfterSame ]);
3979 // iAfterSame is same too
3980 aNewElem = aMesh->AddVolume (prevNod[ iSameNode ], prevNod[ iBeforeSame ],
3981 nextNod[ iBeforeSame ], prevNod[ iAfterSame ],
3982 prevNod[ iOpposSame ], nextNod[ iOpposSame ]);
3986 case SMDSEntity_Quad_Triangle: // sweep (Bi)Quadratic TRIANGLE --->
3987 case SMDSEntity_BiQuad_Triangle: /* ??? */ {
3988 if ( nbDouble+nbSame != 3 ) break;
3990 // ---> pentahedron with 15 nodes
3991 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
3992 nextNod[0], nextNod[1], nextNod[2],
3993 prevNod[3], prevNod[4], prevNod[5],
3994 nextNod[3], nextNod[4], nextNod[5],
3995 midlNod[0], midlNod[1], midlNod[2]);
3997 else if(nbSame==1) {
3998 // ---> 2d order pyramid of 13 nodes
3999 int apex = iSameNode;
4000 int i0 = ( apex + 1 ) % nbCorners;
4001 int i1 = ( apex - 1 + nbCorners ) % nbCorners;
4005 aNewElem = aMesh->AddVolume(prevNod[i1], prevNod[i0],
4006 nextNod[i0], nextNod[i1], prevNod[apex],
4007 prevNod[i01], midlNod[i0],
4008 nextNod[i01], midlNod[i1],
4009 prevNod[i1a], prevNod[i0a],
4010 nextNod[i0a], nextNod[i1a]);
4012 else if(nbSame==2) {
4013 // ---> 2d order tetrahedron of 10 nodes
4014 int n1 = iNotSameNode;
4015 int n2 = ( n1 + 1 ) % nbCorners;
4016 int n3 = ( n1 + nbCorners - 1 ) % nbCorners;
4020 aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], prevNod[n3], nextNod[n1],
4021 prevNod[n12], prevNod[n23], prevNod[n31],
4022 midlNod[n1], nextNod[n12], nextNod[n31]);
4026 case SMDSEntity_Quad_Quadrangle: { // sweep Quadratic QUADRANGLE --->
4028 if ( nbDouble != 4 ) break;
4029 // ---> hexahedron with 20 nodes
4030 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
4031 nextNod[0], nextNod[1], nextNod[2], nextNod[3],
4032 prevNod[4], prevNod[5], prevNod[6], prevNod[7],
4033 nextNod[4], nextNod[5], nextNod[6], nextNod[7],
4034 midlNod[0], midlNod[1], midlNod[2], midlNod[3]);
4036 else if(nbSame==1) {
4037 // ---> pyramid + pentahedron - can not be created since it is needed
4038 // additional middle node at the center of face
4039 INFOS( " Sweep for face " << elem->GetID() << " can not be created" );
4042 else if( nbSame == 2 ) {
4043 if ( nbDouble != 2 ) break;
4044 // ---> 2d order Pentahedron with 15 nodes
4046 if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] ) {
4047 // iBeforeSame is same too
4054 // iAfterSame is same too
4064 aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], nextNod[n2],
4065 prevNod[n4], prevNod[n5], nextNod[n5],
4066 prevNod[n12], midlNod[n2], nextNod[n12],
4067 prevNod[n45], midlNod[n5], nextNod[n45],
4068 prevNod[n14], prevNod[n25], nextNod[n25]);
4072 case SMDSEntity_BiQuad_Quadrangle: { // sweep BiQuadratic QUADRANGLE --->
4074 if( nbSame == 0 && nbDouble == 9 ) {
4075 // ---> tri-quadratic hexahedron with 27 nodes
4076 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
4077 nextNod[0], nextNod[1], nextNod[2], nextNod[3],
4078 prevNod[4], prevNod[5], prevNod[6], prevNod[7],
4079 nextNod[4], nextNod[5], nextNod[6], nextNod[7],
4080 midlNod[0], midlNod[1], midlNod[2], midlNod[3],
4081 prevNod[8], // bottom center
4082 midlNod[4], midlNod[5], midlNod[6], midlNod[7],
4083 nextNod[8], // top center
4084 midlNod[8]);// elem center
4092 case SMDSEntity_Polygon: { // sweep POLYGON
4094 if ( nbNodes == 6 && nbSame == 0 && nbDouble == 0 ) {
4095 // ---> hexagonal prism
4096 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
4097 prevNod[3], prevNod[4], prevNod[5],
4098 nextNod[0], nextNod[1], nextNod[2],
4099 nextNod[3], nextNod[4], nextNod[5]);
4103 case SMDSEntity_Ball:
4111 if ( !aNewElem && elem->GetType() == SMDSAbs_Face ) // try to create a polyherdal prism
4113 if ( baseType != SMDSEntity_Polygon )
4115 const std::vector<int>& ind = SMDS_MeshCell::interlacedSmdsOrder(baseType);
4116 SMDS_MeshCell::applyInterlace( ind, prevNod );
4117 SMDS_MeshCell::applyInterlace( ind, nextNod );
4118 SMDS_MeshCell::applyInterlace( ind, midlNod );
4119 SMDS_MeshCell::applyInterlace( ind, itNN );
4120 SMDS_MeshCell::applyInterlace( ind, isSingleNode );
4121 baseType = SMDSEntity_Polygon; // WARNING: change baseType !!!!
4123 vector<const SMDS_MeshNode*> polyedre_nodes (nbNodes*2 + 4*nbNodes);
4124 vector<int> quantities (nbNodes + 2);
4125 polyedre_nodes.clear();
4129 for (int inode = 0; inode < nbNodes; inode++)
4130 polyedre_nodes.push_back( prevNod[inode] );
4131 quantities.push_back( nbNodes );
4134 polyedre_nodes.push_back( nextNod[0] );
4135 for (int inode = nbNodes; inode-1; --inode )
4136 polyedre_nodes.push_back( nextNod[inode-1] );
4137 quantities.push_back( nbNodes );
4140 for (int iface = 0; iface < nbNodes; iface++)
4142 const int prevNbNodes = polyedre_nodes.size();
4143 int inextface = (iface+1) % nbNodes;
4144 polyedre_nodes.push_back( prevNod[inextface] );
4145 polyedre_nodes.push_back( prevNod[iface] );
4146 if ( prevNod[iface] != nextNod[iface] )
4148 if ( midlNod[ iface ]) polyedre_nodes.push_back( midlNod[ iface ]);
4149 polyedre_nodes.push_back( nextNod[iface] );
4151 if ( prevNod[inextface] != nextNod[inextface] )
4153 polyedre_nodes.push_back( nextNod[inextface] );
4154 if ( midlNod[ inextface ]) polyedre_nodes.push_back( midlNod[ inextface ]);
4156 const int nbFaceNodes = polyedre_nodes.size() - prevNbNodes;
4157 if ( nbFaceNodes > 2 )
4158 quantities.push_back( nbFaceNodes );
4159 else // degenerated face
4160 polyedre_nodes.resize( prevNbNodes );
4162 aNewElem = aMesh->AddPolyhedralVolume (polyedre_nodes, quantities);
4166 newElems.push_back( aNewElem );
4167 myLastCreatedElems.Append(aNewElem);
4168 srcElements.Append( elem );
4171 // set new prev nodes
4172 for ( iNode = 0; iNode < nbNodes; iNode++ )
4173 prevNod[ iNode ] = nextNod[ iNode ];
4178 //=======================================================================
4180 * \brief Create 1D and 2D elements around swept elements
4181 * \param mapNewNodes - source nodes and ones generated from them
4182 * \param newElemsMap - source elements and ones generated from them
4183 * \param elemNewNodesMap - nodes generated from each node of each element
4184 * \param elemSet - all swept elements
4185 * \param nbSteps - number of sweeping steps
4186 * \param srcElements - to append elem for each generated element
4188 //=======================================================================
4190 void SMESH_MeshEditor::makeWalls (TNodeOfNodeListMap & mapNewNodes,
4191 TElemOfElemListMap & newElemsMap,
4192 TElemOfVecOfNnlmiMap & elemNewNodesMap,
4193 TIDSortedElemSet& elemSet,
4195 SMESH_SequenceOfElemPtr& srcElements)
4197 ASSERT( newElemsMap.size() == elemNewNodesMap.size() );
4198 SMESHDS_Mesh* aMesh = GetMeshDS();
4200 // Find nodes belonging to only one initial element - sweep them into edges.
4202 TNodeOfNodeListMapItr nList = mapNewNodes.begin();
4203 for ( ; nList != mapNewNodes.end(); nList++ )
4205 const SMDS_MeshNode* node =
4206 static_cast<const SMDS_MeshNode*>( nList->first );
4207 if ( newElemsMap.count( node ))
4208 continue; // node was extruded into edge
4209 SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator();
4210 int nbInitElems = 0;
4211 const SMDS_MeshElement* el = 0;
4212 SMDSAbs_ElementType highType = SMDSAbs_Edge; // count most complex elements only
4213 while ( eIt->more() && nbInitElems < 2 ) {
4215 SMDSAbs_ElementType type = el->GetType();
4216 if ( type == SMDSAbs_Volume || type < highType ) continue;
4217 if ( type > highType ) {
4221 nbInitElems += elemSet.count(el);
4223 if ( nbInitElems < 2 ) {
4224 bool NotCreateEdge = el && el->IsMediumNode(node);
4225 if(!NotCreateEdge) {
4226 vector<TNodeOfNodeListMapItr> newNodesItVec( 1, nList );
4227 list<const SMDS_MeshElement*> newEdges;
4228 sweepElement( node, newNodesItVec, newEdges, nbSteps, srcElements );
4233 // Make a ceiling for each element ie an equal element of last new nodes.
4234 // Find free links of faces - make edges and sweep them into faces.
4236 TElemOfElemListMap::iterator itElem = newElemsMap.begin();
4237 TElemOfVecOfNnlmiMap::iterator itElemNodes = elemNewNodesMap.begin();
4238 for ( ; itElem != newElemsMap.end(); itElem++, itElemNodes++ )
4240 const SMDS_MeshElement* elem = itElem->first;
4241 vector<TNodeOfNodeListMapItr>& vecNewNodes = itElemNodes->second;
4243 if(itElem->second.size()==0) continue;
4245 const bool isQuadratic = elem->IsQuadratic();
4247 if ( elem->GetType() == SMDSAbs_Edge ) {
4248 // create a ceiling edge
4249 if ( !isQuadratic ) {
4250 if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
4251 vecNewNodes[ 1 ]->second.back())) {
4252 myLastCreatedElems.Append(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
4253 vecNewNodes[ 1 ]->second.back()));
4254 srcElements.Append( elem );
4258 if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
4259 vecNewNodes[ 1 ]->second.back(),
4260 vecNewNodes[ 2 ]->second.back())) {
4261 myLastCreatedElems.Append(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
4262 vecNewNodes[ 1 ]->second.back(),
4263 vecNewNodes[ 2 ]->second.back()));
4264 srcElements.Append( elem );
4268 if ( elem->GetType() != SMDSAbs_Face )
4271 bool hasFreeLinks = false;
4273 TIDSortedElemSet avoidSet;
4274 avoidSet.insert( elem );
4276 set<const SMDS_MeshNode*> aFaceLastNodes;
4277 int iNode, nbNodes = vecNewNodes.size();
4278 if ( !isQuadratic ) {
4279 // loop on the face nodes
4280 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4281 aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4282 // look for free links of the face
4283 int iNext = ( iNode + 1 == nbNodes ) ? 0 : iNode + 1;
4284 const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
4285 const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
4286 // check if a link n1-n2 is free
4287 if ( ! SMESH_MeshAlgos::FindFaceInSet ( n1, n2, elemSet, avoidSet )) {
4288 hasFreeLinks = true;
4289 // make a new edge and a ceiling for a new edge
4290 const SMDS_MeshElement* edge;
4291 if ( ! ( edge = aMesh->FindEdge( n1, n2 ))) {
4292 myLastCreatedElems.Append( edge = aMesh->AddEdge( n1, n2 )); // free link edge
4293 srcElements.Append( myLastCreatedElems.Last() );
4295 n1 = vecNewNodes[ iNode ]->second.back();
4296 n2 = vecNewNodes[ iNext ]->second.back();
4297 if ( !aMesh->FindEdge( n1, n2 )) {
4298 myLastCreatedElems.Append(aMesh->AddEdge( n1, n2 )); // new edge ceiling
4299 srcElements.Append( edge );
4304 else { // elem is quadratic face
4305 int nbn = nbNodes/2;
4306 for ( iNode = 0; iNode < nbn; iNode++ ) {
4307 aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4308 int iNext = ( iNode + 1 == nbn ) ? 0 : iNode + 1;
4309 const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
4310 const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
4311 const SMDS_MeshNode* n3 = vecNewNodes[ iNode+nbn ]->first;
4312 // check if a link is free
4313 if ( ! SMESH_MeshAlgos::FindFaceInSet ( n1, n2, elemSet, avoidSet ) &&
4314 ! SMESH_MeshAlgos::FindFaceInSet ( n1, n3, elemSet, avoidSet ) &&
4315 ! SMESH_MeshAlgos::FindFaceInSet ( n3, n2, elemSet, avoidSet ) ) {
4316 hasFreeLinks = true;
4317 // make an edge and a ceiling for a new edge
4319 if ( !aMesh->FindEdge( n1, n2, n3 )) {
4320 myLastCreatedElems.Append(aMesh->AddEdge( n1, n2, n3 )); // free link edge
4321 srcElements.Append( elem );
4323 n1 = vecNewNodes[ iNode ]->second.back();
4324 n2 = vecNewNodes[ iNext ]->second.back();
4325 n3 = vecNewNodes[ iNode+nbn ]->second.back();
4326 if ( !aMesh->FindEdge( n1, n2, n3 )) {
4327 myLastCreatedElems.Append(aMesh->AddEdge( n1, n2, n3 )); // ceiling edge
4328 srcElements.Append( elem );
4332 for ( iNode = nbn; iNode < nbNodes; iNode++ ) {
4333 aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4337 // sweep free links into faces
4339 if ( hasFreeLinks ) {
4340 list<const SMDS_MeshElement*> & newVolumes = itElem->second;
4341 int iVol, volNb, nbVolumesByStep = newVolumes.size() / nbSteps;
4343 set<const SMDS_MeshNode*> initNodeSet, topNodeSet, faceNodeSet;
4344 set<const SMDS_MeshNode*> initNodeSetNoCenter/*, topNodeSetNoCenter*/;
4345 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4346 initNodeSet.insert( vecNewNodes[ iNode ]->first );
4347 topNodeSet .insert( vecNewNodes[ iNode ]->second.back() );
4349 if ( isQuadratic && nbNodes % 2 ) { // node set for the case of a biquadratic
4350 initNodeSetNoCenter = initNodeSet; // swept face and a not biquadratic volume
4351 initNodeSetNoCenter.erase( vecNewNodes.back()->first );
4353 for ( volNb = 0; volNb < nbVolumesByStep; volNb++ ) {
4354 list<const SMDS_MeshElement*>::iterator v = newVolumes.begin();
4355 std::advance( v, volNb );
4356 // find indices of free faces of a volume and their source edges
4357 list< int > freeInd;
4358 list< const SMDS_MeshElement* > srcEdges; // source edges of free faces
4359 SMDS_VolumeTool vTool( *v, /*ignoreCentralNodes=*/false );
4360 int iF, nbF = vTool.NbFaces();
4361 for ( iF = 0; iF < nbF; iF ++ ) {
4362 if (vTool.IsFreeFace( iF ) &&
4363 vTool.GetFaceNodes( iF, faceNodeSet ) &&
4364 initNodeSet != faceNodeSet) // except an initial face
4366 if ( nbSteps == 1 && faceNodeSet == topNodeSet )
4368 if ( faceNodeSet == initNodeSetNoCenter )
4370 freeInd.push_back( iF );
4371 // find source edge of a free face iF
4372 vector<const SMDS_MeshNode*> commonNodes; // shared by the initial and free faces
4373 commonNodes.resize( initNodeSet.size(), NULL ); // avoid spoiling memory
4374 std::set_intersection( faceNodeSet.begin(), faceNodeSet.end(),
4375 initNodeSet.begin(), initNodeSet.end(),
4376 commonNodes.begin());
4377 if ( (*v)->IsQuadratic() )
4378 srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1],commonNodes[2]));
4380 srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1]));
4382 if ( !srcEdges.back() )
4384 cout << "SMESH_MeshEditor::makeWalls(), no source edge found for a free face #"
4385 << iF << " of volume #" << vTool.ID() << endl;
4390 if ( freeInd.empty() )
4393 // create faces for all steps;
4394 // if such a face has been already created by sweep of edge,
4395 // assure that its orientation is OK
4396 for ( int iStep = 0; iStep < nbSteps; iStep++ ) {
4397 vTool.Set( *v, /*ignoreCentralNodes=*/false );
4398 vTool.SetExternalNormal();
4399 const int nextShift = vTool.IsForward() ? +1 : -1;
4400 list< int >::iterator ind = freeInd.begin();
4401 list< const SMDS_MeshElement* >::iterator srcEdge = srcEdges.begin();
4402 for ( ; ind != freeInd.end(); ++ind, ++srcEdge ) // loop on free faces
4404 const SMDS_MeshNode** nodes = vTool.GetFaceNodes( *ind );
4405 int nbn = vTool.NbFaceNodes( *ind );
4406 const SMDS_MeshElement * f = 0;
4407 if ( nbn == 3 ) ///// triangle
4409 f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ]);
4411 nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
4413 const SMDS_MeshNode* newOrder[3] = { nodes[ 1 - nextShift ],
4415 nodes[ 1 + nextShift ] };
4417 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4419 myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
4423 else if ( nbn == 4 ) ///// quadrangle
4425 f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ], nodes[ 3 ]);
4427 nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
4429 const SMDS_MeshNode* newOrder[4] = { nodes[ 0 ], nodes[ 2-nextShift ],
4430 nodes[ 2 ], nodes[ 2+nextShift ] };
4432 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4434 myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
4435 newOrder[ 2 ], newOrder[ 3 ]));
4438 else if ( nbn == 6 && isQuadratic ) /////// quadratic triangle
4440 f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[1], nodes[3], nodes[5] );
4442 nodes[2] != f->GetNodeWrap( f->GetNodeIndex( nodes[0] ) + 2*nextShift ))
4444 const SMDS_MeshNode* newOrder[6] = { nodes[2 - 2*nextShift],
4446 nodes[2 + 2*nextShift],
4447 nodes[3 - 2*nextShift],
4449 nodes[3 + 2*nextShift]};
4451 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4453 myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ],
4461 else if ( nbn == 8 && isQuadratic ) /////// quadratic quadrangle
4463 f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[6],
4464 nodes[1], nodes[3], nodes[5], nodes[7] );
4466 nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
4468 const SMDS_MeshNode* newOrder[8] = { nodes[0],
4469 nodes[4 - 2*nextShift],
4471 nodes[4 + 2*nextShift],
4473 nodes[5 - 2*nextShift],
4475 nodes[5 + 2*nextShift] };
4477 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4479 myLastCreatedElems.Append(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
4480 newOrder[ 2 ], newOrder[ 3 ],
4481 newOrder[ 4 ], newOrder[ 5 ],
4482 newOrder[ 6 ], newOrder[ 7 ]));
4485 else if ( nbn == 9 && isQuadratic ) /////// bi-quadratic quadrangle
4487 f = aMesh->FindElement( vector<const SMDS_MeshNode*>( nodes, nodes+nbn ),
4488 SMDSAbs_Face, /*noMedium=*/false);
4490 nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
4492 const SMDS_MeshNode* newOrder[9] = { nodes[0],
4493 nodes[4 - 2*nextShift],
4495 nodes[4 + 2*nextShift],
4497 nodes[5 - 2*nextShift],
4499 nodes[5 + 2*nextShift],
4502 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4504 myLastCreatedElems.Append(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
4505 newOrder[ 2 ], newOrder[ 3 ],
4506 newOrder[ 4 ], newOrder[ 5 ],
4507 newOrder[ 6 ], newOrder[ 7 ],
4511 else //////// polygon
4513 vector<const SMDS_MeshNode*> polygon_nodes ( nodes, nodes+nbn );
4514 const SMDS_MeshFace * f = aMesh->FindFace( polygon_nodes );
4516 nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + nextShift ))
4518 if ( !vTool.IsForward() )
4519 std::reverse( polygon_nodes.begin(), polygon_nodes.end());
4521 aMesh->ChangeElementNodes( f, &polygon_nodes[0], nbn );
4523 AddElement(polygon_nodes, SMDSAbs_Face, polygon_nodes.size()>4);
4527 while ( srcElements.Length() < myLastCreatedElems.Length() )
4528 srcElements.Append( *srcEdge );
4530 } // loop on free faces
4532 // go to the next volume
4534 while ( iVol++ < nbVolumesByStep ) v++;
4537 } // loop on volumes of one step
4538 } // sweep free links into faces
4540 // Make a ceiling face with a normal external to a volume
4542 // use SMDS_VolumeTool to get a correctly ordered nodes of a ceiling face
4543 SMDS_VolumeTool lastVol( itElem->second.back(), /*ignoreCentralNodes=*/false );
4544 int iF = lastVol.GetFaceIndex( aFaceLastNodes );
4546 if ( iF < 0 && isQuadratic && nbNodes % 2 ) { // remove a central node of biquadratic
4547 aFaceLastNodes.erase( vecNewNodes.back()->second.back() );
4548 iF = lastVol.GetFaceIndex( aFaceLastNodes );
4551 lastVol.SetExternalNormal();
4552 const SMDS_MeshNode** nodes = lastVol.GetFaceNodes( iF );
4553 int nbn = lastVol.NbFaceNodes( iF );
4554 // we do not use this->AddElement() because nodes are interlaced
4555 vector<const SMDS_MeshNode*> nodeVec( nodes, nodes+nbn );
4556 if ( !hasFreeLinks ||
4557 !aMesh->FindElement( nodeVec, SMDSAbs_Face, /*noMedium=*/false) )
4560 myLastCreatedElems.Append(aMesh->AddFace( nodes[0], nodes[1], nodes[2] ));
4562 else if ( nbn == 4 )
4563 myLastCreatedElems.Append(aMesh->AddFace( nodes[0], nodes[1], nodes[2], nodes[3]));
4565 else if ( nbn == 6 && isQuadratic )
4566 myLastCreatedElems.Append(aMesh->AddFace( nodes[0], nodes[2], nodes[4],
4567 nodes[1], nodes[3], nodes[5]));
4568 else if ( nbn == 7 && isQuadratic )
4569 myLastCreatedElems.Append(aMesh->AddFace( nodes[0], nodes[2], nodes[4],
4570 nodes[1], nodes[3], nodes[5], nodes[6]));
4571 else if ( nbn == 8 && isQuadratic )
4572 myLastCreatedElems.Append(aMesh->AddFace( nodes[0], nodes[2], nodes[4], nodes[6],
4573 nodes[1], nodes[3], nodes[5], nodes[7]));
4574 else if ( nbn == 9 && isQuadratic )
4575 myLastCreatedElems.Append(aMesh->AddFace( nodes[0], nodes[2], nodes[4], nodes[6],
4576 nodes[1], nodes[3], nodes[5], nodes[7],
4579 myLastCreatedElems.Append(aMesh->AddPolygonalFace( nodeVec ));
4581 while ( srcElements.Length() < myLastCreatedElems.Length() )
4582 srcElements.Append( elem );
4585 } // loop on swept elements
4588 //=======================================================================
4589 //function : RotationSweep
4591 //=======================================================================
4593 SMESH_MeshEditor::PGroupIDs
4594 SMESH_MeshEditor::RotationSweep(TIDSortedElemSet & theElems,
4595 const gp_Ax1& theAxis,
4596 const double theAngle,
4597 const int theNbSteps,
4598 const double theTol,
4599 const bool theMakeGroups,
4600 const bool theMakeWalls)
4602 myLastCreatedElems.Clear();
4603 myLastCreatedNodes.Clear();
4605 // source elements for each generated one
4606 SMESH_SequenceOfElemPtr srcElems, srcNodes;
4608 MESSAGE( "RotationSweep()");
4610 aTrsf.SetRotation( theAxis, theAngle );
4612 aTrsf2.SetRotation( theAxis, theAngle/2. );
4614 gp_Lin aLine( theAxis );
4615 double aSqTol = theTol * theTol;
4617 SMESHDS_Mesh* aMesh = GetMeshDS();
4619 TNodeOfNodeListMap mapNewNodes;
4620 TElemOfVecOfNnlmiMap mapElemNewNodes;
4621 TElemOfElemListMap newElemsMap;
4623 const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
4624 myMesh->NbFaces(ORDER_QUADRATIC) +
4625 myMesh->NbVolumes(ORDER_QUADRATIC) );
4627 TIDSortedElemSet::iterator itElem;
4628 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
4629 const SMDS_MeshElement* elem = *itElem;
4630 if ( !elem || elem->GetType() == SMDSAbs_Volume )
4632 vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
4633 newNodesItVec.reserve( elem->NbNodes() );
4635 // loop on elem nodes
4636 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
4637 while ( itN->more() )
4639 // check if a node has been already sweeped
4640 const SMDS_MeshNode* node = cast2Node( itN->next() );
4642 gp_XYZ aXYZ( node->X(), node->Y(), node->Z() );
4644 aXYZ.Coord( coord[0], coord[1], coord[2] );
4645 bool isOnAxis = ( aLine.SquareDistance( aXYZ ) <= aSqTol );
4647 TNodeOfNodeListMapItr nIt =
4648 mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
4649 list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
4650 if ( listNewNodes.empty() )
4652 // check if we are to create medium nodes between corner ones
4653 bool needMediumNodes = false;
4654 if ( isQuadraticMesh )
4656 SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
4657 while (it->more() && !needMediumNodes )
4659 const SMDS_MeshElement* invElem = it->next();
4660 if ( invElem != elem && !theElems.count( invElem )) continue;
4661 needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
4662 if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
4663 needMediumNodes = true;
4668 const SMDS_MeshNode * newNode = node;
4669 for ( int i = 0; i < theNbSteps; i++ ) {
4671 if ( needMediumNodes ) // create a medium node
4673 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
4674 newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
4675 myLastCreatedNodes.Append(newNode);
4676 srcNodes.Append( node );
4677 listNewNodes.push_back( newNode );
4678 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
4681 aTrsf.Transforms( coord[0], coord[1], coord[2] );
4683 // create a corner node
4684 newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
4685 myLastCreatedNodes.Append(newNode);
4686 srcNodes.Append( node );
4687 listNewNodes.push_back( newNode );
4690 listNewNodes.push_back( newNode );
4691 // if ( needMediumNodes )
4692 // listNewNodes.push_back( newNode );
4696 newNodesItVec.push_back( nIt );
4698 // make new elements
4699 sweepElement( elem, newNodesItVec, newElemsMap[elem], theNbSteps, srcElems );
4703 makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElems, theNbSteps, srcElems );
4705 PGroupIDs newGroupIDs;
4706 if ( theMakeGroups )
4707 newGroupIDs = generateGroups( srcNodes, srcElems, "rotated");
4713 //=======================================================================
4714 //function : CreateNode
4716 //=======================================================================
4717 const SMDS_MeshNode* SMESH_MeshEditor::CreateNode(const double x,
4720 const double tolnode,
4721 SMESH_SequenceOfNode& aNodes)
4723 // myLastCreatedElems.Clear();
4724 // myLastCreatedNodes.Clear();
4727 SMESHDS_Mesh * aMesh = myMesh->GetMeshDS();
4729 // try to search in sequence of existing nodes
4730 // if aNodes.Length()>0 we 'nave to use given sequence
4731 // else - use all nodes of mesh
4732 if(aNodes.Length()>0) {
4734 for(i=1; i<=aNodes.Length(); i++) {
4735 gp_Pnt P2(aNodes.Value(i)->X(),aNodes.Value(i)->Y(),aNodes.Value(i)->Z());
4736 if(P1.Distance(P2)<tolnode)
4737 return aNodes.Value(i);
4741 SMDS_NodeIteratorPtr itn = aMesh->nodesIterator();
4742 while(itn->more()) {
4743 const SMDS_MeshNode* aN = static_cast<const SMDS_MeshNode*> (itn->next());
4744 gp_Pnt P2(aN->X(),aN->Y(),aN->Z());
4745 if(P1.Distance(P2)<tolnode)
4750 // create new node and return it
4751 const SMDS_MeshNode* NewNode = aMesh->AddNode(x,y,z);
4752 //myLastCreatedNodes.Append(NewNode);
4757 //=======================================================================
4758 //function : ExtrusionSweep
4760 //=======================================================================
4762 SMESH_MeshEditor::PGroupIDs
4763 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet & theElems,
4764 const gp_Vec& theStep,
4765 const int theNbSteps,
4766 TElemOfElemListMap& newElemsMap,
4767 const bool theMakeGroups,
4769 const double theTolerance)
4771 ExtrusParam aParams;
4772 aParams.myDir = gp_Dir(theStep);
4773 aParams.myNodes.Clear();
4774 aParams.mySteps = new TColStd_HSequenceOfReal;
4776 for(i=1; i<=theNbSteps; i++)
4777 aParams.mySteps->Append(theStep.Magnitude());
4780 ExtrusionSweep(theElems,aParams,newElemsMap,theMakeGroups,theFlags,theTolerance);
4784 //=======================================================================
4785 //function : ExtrusionSweep
4787 //=======================================================================
4789 SMESH_MeshEditor::PGroupIDs
4790 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet & theElems,
4791 ExtrusParam& theParams,
4792 TElemOfElemListMap& newElemsMap,
4793 const bool theMakeGroups,
4795 const double theTolerance)
4797 myLastCreatedElems.Clear();
4798 myLastCreatedNodes.Clear();
4800 // source elements for each generated one
4801 SMESH_SequenceOfElemPtr srcElems, srcNodes;
4803 SMESHDS_Mesh* aMesh = GetMeshDS();
4805 int nbsteps = theParams.mySteps->Length();
4807 TNodeOfNodeListMap mapNewNodes;
4808 //TNodeOfNodeVecMap mapNewNodes;
4809 TElemOfVecOfNnlmiMap mapElemNewNodes;
4810 //TElemOfVecOfMapNodesMap mapElemNewNodes;
4812 const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
4813 myMesh->NbFaces(ORDER_QUADRATIC) +
4814 myMesh->NbVolumes(ORDER_QUADRATIC) );
4816 TIDSortedElemSet::iterator itElem;
4817 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
4818 // check element type
4819 const SMDS_MeshElement* elem = *itElem;
4820 if ( !elem || elem->GetType() == SMDSAbs_Volume )
4823 vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
4824 newNodesItVec.reserve( elem->NbNodes() );
4826 // loop on elem nodes
4827 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
4828 while ( itN->more() )
4830 // check if a node has been already sweeped
4831 const SMDS_MeshNode* node = cast2Node( itN->next() );
4832 TNodeOfNodeListMap::iterator nIt =
4833 mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
4834 list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
4835 if ( listNewNodes.empty() )
4839 // check if we are to create medium nodes between corner ones
4840 bool needMediumNodes = false;
4841 if ( isQuadraticMesh )
4843 SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
4844 while (it->more() && !needMediumNodes )
4846 const SMDS_MeshElement* invElem = it->next();
4847 if ( invElem != elem && !theElems.count( invElem )) continue;
4848 needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
4849 if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
4850 needMediumNodes = true;
4854 double coord[] = { node->X(), node->Y(), node->Z() };
4855 for ( int i = 0; i < nbsteps; i++ )
4857 if ( needMediumNodes ) // create a medium node
4859 double x = coord[0] + theParams.myDir.X()*theParams.mySteps->Value(i+1)/2.;
4860 double y = coord[1] + theParams.myDir.Y()*theParams.mySteps->Value(i+1)/2.;
4861 double z = coord[2] + theParams.myDir.Z()*theParams.mySteps->Value(i+1)/2.;
4862 if( theFlags & EXTRUSION_FLAG_SEW ) {
4863 const SMDS_MeshNode * newNode = CreateNode(x, y, z,
4864 theTolerance, theParams.myNodes);
4865 listNewNodes.push_back( newNode );
4868 const SMDS_MeshNode * newNode = aMesh->AddNode(x, y, z);
4869 myLastCreatedNodes.Append(newNode);
4870 srcNodes.Append( node );
4871 listNewNodes.push_back( newNode );
4874 // create a corner node
4875 coord[0] = coord[0] + theParams.myDir.X()*theParams.mySteps->Value(i+1);
4876 coord[1] = coord[1] + theParams.myDir.Y()*theParams.mySteps->Value(i+1);
4877 coord[2] = coord[2] + theParams.myDir.Z()*theParams.mySteps->Value(i+1);
4878 if( theFlags & EXTRUSION_FLAG_SEW ) {
4879 const SMDS_MeshNode * newNode = CreateNode(coord[0], coord[1], coord[2],
4880 theTolerance, theParams.myNodes);
4881 listNewNodes.push_back( newNode );
4884 const SMDS_MeshNode * newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
4885 myLastCreatedNodes.Append(newNode);
4886 srcNodes.Append( node );
4887 listNewNodes.push_back( newNode );
4891 newNodesItVec.push_back( nIt );
4893 // make new elements
4894 sweepElement( elem, newNodesItVec, newElemsMap[elem], nbsteps, srcElems );
4897 if( theFlags & EXTRUSION_FLAG_BOUNDARY ) {
4898 makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElems, nbsteps, srcElems );
4900 PGroupIDs newGroupIDs;
4901 if ( theMakeGroups )
4902 newGroupIDs = generateGroups( srcNodes, srcElems, "extruded");
4907 //=======================================================================
4908 //function : ExtrusionAlongTrack
4910 //=======================================================================
4911 SMESH_MeshEditor::Extrusion_Error
4912 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet & theElements,
4913 SMESH_subMesh* theTrack,
4914 const SMDS_MeshNode* theN1,
4915 const bool theHasAngles,
4916 list<double>& theAngles,
4917 const bool theLinearVariation,
4918 const bool theHasRefPoint,
4919 const gp_Pnt& theRefPoint,
4920 const bool theMakeGroups)
4922 MESSAGE("ExtrusionAlongTrack");
4923 myLastCreatedElems.Clear();
4924 myLastCreatedNodes.Clear();
4927 std::list<double> aPrms;
4928 TIDSortedElemSet::iterator itElem;
4931 TopoDS_Edge aTrackEdge;
4932 TopoDS_Vertex aV1, aV2;
4934 SMDS_ElemIteratorPtr aItE;
4935 SMDS_NodeIteratorPtr aItN;
4936 SMDSAbs_ElementType aTypeE;
4938 TNodeOfNodeListMap mapNewNodes;
4941 aNbE = theElements.size();
4944 return EXTR_NO_ELEMENTS;
4946 // 1.1 Track Pattern
4949 SMESHDS_SubMesh* pSubMeshDS = theTrack->GetSubMeshDS();
4951 aItE = pSubMeshDS->GetElements();
4952 while ( aItE->more() ) {
4953 const SMDS_MeshElement* pE = aItE->next();
4954 aTypeE = pE->GetType();
4955 // Pattern must contain links only
4956 if ( aTypeE != SMDSAbs_Edge )
4957 return EXTR_PATH_NOT_EDGE;
4960 list<SMESH_MeshEditor_PathPoint> fullList;
4962 const TopoDS_Shape& aS = theTrack->GetSubShape();
4963 // Sub-shape for the Pattern must be an Edge or Wire
4964 if( aS.ShapeType() == TopAbs_EDGE ) {
4965 aTrackEdge = TopoDS::Edge( aS );
4966 // the Edge must not be degenerated
4967 if ( BRep_Tool::Degenerated( aTrackEdge ) )
4968 return EXTR_BAD_PATH_SHAPE;
4969 TopExp::Vertices( aTrackEdge, aV1, aV2 );
4970 aItN = theTrack->GetFather()->GetSubMesh( aV1 )->GetSubMeshDS()->GetNodes();
4971 const SMDS_MeshNode* aN1 = aItN->next();
4972 aItN = theTrack->GetFather()->GetSubMesh( aV2 )->GetSubMeshDS()->GetNodes();
4973 const SMDS_MeshNode* aN2 = aItN->next();
4974 // starting node must be aN1 or aN2
4975 if ( !( aN1 == theN1 || aN2 == theN1 ) )
4976 return EXTR_BAD_STARTING_NODE;
4977 aItN = pSubMeshDS->GetNodes();
4978 while ( aItN->more() ) {
4979 const SMDS_MeshNode* pNode = aItN->next();
4980 const SMDS_EdgePosition* pEPos =
4981 static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
4982 double aT = pEPos->GetUParameter();
4983 aPrms.push_back( aT );
4985 //Extrusion_Error err =
4986 MakeEdgePathPoints(aPrms, aTrackEdge, (aN1==theN1), fullList);
4987 } else if( aS.ShapeType() == TopAbs_WIRE ) {
4988 list< SMESH_subMesh* > LSM;
4989 TopTools_SequenceOfShape Edges;
4990 SMESH_subMeshIteratorPtr itSM = theTrack->getDependsOnIterator(false,true);
4991 while(itSM->more()) {
4992 SMESH_subMesh* SM = itSM->next();
4994 const TopoDS_Shape& aS = SM->GetSubShape();
4997 list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
4998 int startNid = theN1->GetID();
4999 TColStd_MapOfInteger UsedNums;
5001 int NbEdges = Edges.Length();
5003 for(; i<=NbEdges; i++) {
5005 list< SMESH_subMesh* >::iterator itLSM = LSM.begin();
5006 for(; itLSM!=LSM.end(); itLSM++) {
5008 if(UsedNums.Contains(k)) continue;
5009 aTrackEdge = TopoDS::Edge( Edges.Value(k) );
5010 SMESH_subMesh* locTrack = *itLSM;
5011 SMESHDS_SubMesh* locMeshDS = locTrack->GetSubMeshDS();
5012 TopExp::Vertices( aTrackEdge, aV1, aV2 );
5013 aItN = locTrack->GetFather()->GetSubMesh(aV1)->GetSubMeshDS()->GetNodes();
5014 const SMDS_MeshNode* aN1 = aItN->next();
5015 aItN = locTrack->GetFather()->GetSubMesh(aV2)->GetSubMeshDS()->GetNodes();
5016 const SMDS_MeshNode* aN2 = aItN->next();
5017 // starting node must be aN1 or aN2
5018 if ( !( aN1->GetID() == startNid || aN2->GetID() == startNid ) ) continue;
5019 // 2. Collect parameters on the track edge
5021 aItN = locMeshDS->GetNodes();
5022 while ( aItN->more() ) {
5023 const SMDS_MeshNode* pNode = aItN->next();
5024 const SMDS_EdgePosition* pEPos =
5025 static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
5026 double aT = pEPos->GetUParameter();
5027 aPrms.push_back( aT );
5029 list<SMESH_MeshEditor_PathPoint> LPP;
5030 //Extrusion_Error err =
5031 MakeEdgePathPoints(aPrms, aTrackEdge,(aN1->GetID()==startNid), LPP);
5032 LLPPs.push_back(LPP);
5034 // update startN for search following egde
5035 if( aN1->GetID() == startNid ) startNid = aN2->GetID();
5036 else startNid = aN1->GetID();
5040 list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
5041 list<SMESH_MeshEditor_PathPoint> firstList = *itLLPP;
5042 list<SMESH_MeshEditor_PathPoint>::iterator itPP = firstList.begin();
5043 for(; itPP!=firstList.end(); itPP++) {
5044 fullList.push_back( *itPP );
5046 SMESH_MeshEditor_PathPoint PP1 = fullList.back();
5047 fullList.pop_back();
5049 for(; itLLPP!=LLPPs.end(); itLLPP++) {
5050 list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
5051 itPP = currList.begin();
5052 SMESH_MeshEditor_PathPoint PP2 = currList.front();
5053 gp_Dir D1 = PP1.Tangent();
5054 gp_Dir D2 = PP2.Tangent();
5055 gp_Dir Dnew( gp_Vec( (D1.X()+D2.X())/2, (D1.Y()+D2.Y())/2,
5056 (D1.Z()+D2.Z())/2 ) );
5057 PP1.SetTangent(Dnew);
5058 fullList.push_back(PP1);
5060 for(; itPP!=firstList.end(); itPP++) {
5061 fullList.push_back( *itPP );
5063 PP1 = fullList.back();
5064 fullList.pop_back();
5066 // if wire not closed
5067 fullList.push_back(PP1);
5071 return EXTR_BAD_PATH_SHAPE;
5074 return MakeExtrElements(theElements, fullList, theHasAngles, theAngles, theLinearVariation,
5075 theHasRefPoint, theRefPoint, theMakeGroups);
5079 //=======================================================================
5080 //function : ExtrusionAlongTrack
5082 //=======================================================================
5083 SMESH_MeshEditor::Extrusion_Error
5084 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet & theElements,
5085 SMESH_Mesh* theTrack,
5086 const SMDS_MeshNode* theN1,
5087 const bool theHasAngles,
5088 list<double>& theAngles,
5089 const bool theLinearVariation,
5090 const bool theHasRefPoint,
5091 const gp_Pnt& theRefPoint,
5092 const bool theMakeGroups)
5094 myLastCreatedElems.Clear();
5095 myLastCreatedNodes.Clear();
5098 std::list<double> aPrms;
5099 TIDSortedElemSet::iterator itElem;
5102 TopoDS_Edge aTrackEdge;
5103 TopoDS_Vertex aV1, aV2;
5105 SMDS_ElemIteratorPtr aItE;
5106 SMDS_NodeIteratorPtr aItN;
5107 SMDSAbs_ElementType aTypeE;
5109 TNodeOfNodeListMap mapNewNodes;
5112 aNbE = theElements.size();
5115 return EXTR_NO_ELEMENTS;
5117 // 1.1 Track Pattern
5120 SMESHDS_Mesh* pMeshDS = theTrack->GetMeshDS();
5122 aItE = pMeshDS->elementsIterator();
5123 while ( aItE->more() ) {
5124 const SMDS_MeshElement* pE = aItE->next();
5125 aTypeE = pE->GetType();
5126 // Pattern must contain links only
5127 if ( aTypeE != SMDSAbs_Edge )
5128 return EXTR_PATH_NOT_EDGE;
5131 list<SMESH_MeshEditor_PathPoint> fullList;
5133 const TopoDS_Shape& aS = theTrack->GetShapeToMesh();
5135 if( aS == SMESH_Mesh::PseudoShape() ) {
5136 //Mesh without shape
5137 const SMDS_MeshNode* currentNode = NULL;
5138 const SMDS_MeshNode* prevNode = theN1;
5139 std::vector<const SMDS_MeshNode*> aNodesList;
5140 aNodesList.push_back(theN1);
5141 int nbEdges = 0, conn=0;
5142 const SMDS_MeshElement* prevElem = NULL;
5143 const SMDS_MeshElement* currentElem = NULL;
5144 int totalNbEdges = theTrack->NbEdges();
5145 SMDS_ElemIteratorPtr nIt;
5148 if( !theTrack->GetMeshDS()->Contains(theN1) ) {
5149 return EXTR_BAD_STARTING_NODE;
5152 conn = nbEdgeConnectivity(theN1);
5154 return EXTR_PATH_NOT_EDGE;
5156 aItE = theN1->GetInverseElementIterator();
5157 prevElem = aItE->next();
5158 currentElem = prevElem;
5160 if(totalNbEdges == 1 ) {
5161 nIt = currentElem->nodesIterator();
5162 currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
5163 if(currentNode == prevNode)
5164 currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
5165 aNodesList.push_back(currentNode);
5167 nIt = currentElem->nodesIterator();
5168 while( nIt->more() ) {
5169 currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
5170 if(currentNode == prevNode)
5171 currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
5172 aNodesList.push_back(currentNode);
5174 //case of the closed mesh
5175 if(currentNode == theN1) {
5180 conn = nbEdgeConnectivity(currentNode);
5182 return EXTR_PATH_NOT_EDGE;
5183 }else if( conn == 1 && nbEdges > 0 ) {
5188 prevNode = currentNode;
5189 aItE = currentNode->GetInverseElementIterator();
5190 currentElem = aItE->next();
5191 if( currentElem == prevElem)
5192 currentElem = aItE->next();
5193 nIt = currentElem->nodesIterator();
5194 prevElem = currentElem;
5200 if(nbEdges != totalNbEdges)
5201 return EXTR_PATH_NOT_EDGE;
5203 TopTools_SequenceOfShape Edges;
5204 double x1,x2,y1,y2,z1,z2;
5205 list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
5206 int startNid = theN1->GetID();
5207 for(int i = 1; i < aNodesList.size(); i++) {
5208 x1 = aNodesList[i-1]->X();x2 = aNodesList[i]->X();
5209 y1 = aNodesList[i-1]->Y();y2 = aNodesList[i]->Y();
5210 z1 = aNodesList[i-1]->Z();z2 = aNodesList[i]->Z();
5211 TopoDS_Edge e = BRepBuilderAPI_MakeEdge(gp_Pnt(x1,y1,z1),gp_Pnt(x2,y2,z2));
5212 list<SMESH_MeshEditor_PathPoint> LPP;
5214 MakeEdgePathPoints(aPrms, e, (aNodesList[i-1]->GetID()==startNid), LPP);
5215 LLPPs.push_back(LPP);
5216 if( aNodesList[i-1]->GetID() == startNid ) startNid = aNodesList[i]->GetID();
5217 else startNid = aNodesList[i-1]->GetID();
5221 list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
5222 list<SMESH_MeshEditor_PathPoint> firstList = *itLLPP;
5223 list<SMESH_MeshEditor_PathPoint>::iterator itPP = firstList.begin();
5224 for(; itPP!=firstList.end(); itPP++) {
5225 fullList.push_back( *itPP );
5228 SMESH_MeshEditor_PathPoint PP1 = fullList.back();
5229 SMESH_MeshEditor_PathPoint PP2;
5230 fullList.pop_back();
5232 for(; itLLPP!=LLPPs.end(); itLLPP++) {
5233 list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
5234 itPP = currList.begin();
5235 PP2 = currList.front();
5236 gp_Dir D1 = PP1.Tangent();
5237 gp_Dir D2 = PP2.Tangent();
5238 gp_Dir Dnew( gp_Vec( (D1.X()+D2.X())/2, (D1.Y()+D2.Y())/2,
5239 (D1.Z()+D2.Z())/2 ) );
5240 PP1.SetTangent(Dnew);
5241 fullList.push_back(PP1);
5243 for(; itPP!=currList.end(); itPP++) {
5244 fullList.push_back( *itPP );
5246 PP1 = fullList.back();
5247 fullList.pop_back();
5249 fullList.push_back(PP1);
5251 } // Sub-shape for the Pattern must be an Edge or Wire
5252 else if( aS.ShapeType() == TopAbs_EDGE ) {
5253 aTrackEdge = TopoDS::Edge( aS );
5254 // the Edge must not be degenerated
5255 if ( BRep_Tool::Degenerated( aTrackEdge ) )
5256 return EXTR_BAD_PATH_SHAPE;
5257 TopExp::Vertices( aTrackEdge, aV1, aV2 );
5258 const SMDS_MeshNode* aN1 = 0;
5259 const SMDS_MeshNode* aN2 = 0;
5260 if ( theTrack->GetSubMesh( aV1 ) && theTrack->GetSubMesh( aV1 )->GetSubMeshDS() ) {
5261 aItN = theTrack->GetSubMesh( aV1 )->GetSubMeshDS()->GetNodes();
5264 if ( theTrack->GetSubMesh( aV2 ) && theTrack->GetSubMesh( aV2 )->GetSubMeshDS() ) {
5265 aItN = theTrack->GetSubMesh( aV2 )->GetSubMeshDS()->GetNodes();
5268 // starting node must be aN1 or aN2
5269 if ( !( aN1 == theN1 || aN2 == theN1 ) )
5270 return EXTR_BAD_STARTING_NODE;
5271 aItN = pMeshDS->nodesIterator();
5272 while ( aItN->more() ) {
5273 const SMDS_MeshNode* pNode = aItN->next();
5274 if( pNode==aN1 || pNode==aN2 ) continue;
5275 const SMDS_EdgePosition* pEPos =
5276 static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
5277 double aT = pEPos->GetUParameter();
5278 aPrms.push_back( aT );
5280 //Extrusion_Error err =
5281 MakeEdgePathPoints(aPrms, aTrackEdge, (aN1==theN1), fullList);
5283 else if( aS.ShapeType() == TopAbs_WIRE ) {
5284 list< SMESH_subMesh* > LSM;
5285 TopTools_SequenceOfShape Edges;
5286 TopExp_Explorer eExp(aS, TopAbs_EDGE);
5287 for(; eExp.More(); eExp.Next()) {
5288 TopoDS_Edge E = TopoDS::Edge( eExp.Current() );
5289 if( BRep_Tool::Degenerated(E) ) continue;
5290 SMESH_subMesh* SM = theTrack->GetSubMesh(E);
5296 list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
5297 TopoDS_Vertex aVprev;
5298 TColStd_MapOfInteger UsedNums;
5299 int NbEdges = Edges.Length();
5301 for(; i<=NbEdges; i++) {
5303 list< SMESH_subMesh* >::iterator itLSM = LSM.begin();
5304 for(; itLSM!=LSM.end(); itLSM++) {
5306 if(UsedNums.Contains(k)) continue;
5307 aTrackEdge = TopoDS::Edge( Edges.Value(k) );
5308 SMESH_subMesh* locTrack = *itLSM;
5309 SMESHDS_SubMesh* locMeshDS = locTrack->GetSubMeshDS();
5310 TopExp::Vertices( aTrackEdge, aV1, aV2 );
5311 bool aN1isOK = false, aN2isOK = false;
5312 if ( aVprev.IsNull() ) {
5313 // if previous vertex is not yet defined, it means that we in the beginning of wire
5314 // and we have to find initial vertex corresponding to starting node theN1
5315 const SMDS_MeshNode* aN1 = 0;
5316 const SMDS_MeshNode* aN2 = 0;
5318 if ( locTrack->GetFather()->GetSubMesh(aV1) && locTrack->GetFather()->GetSubMesh(aV1)->GetSubMeshDS() ) {
5319 aItN = locTrack->GetFather()->GetSubMesh(aV1)->GetSubMeshDS()->GetNodes();
5322 if ( locTrack->GetFather()->GetSubMesh(aV2) && locTrack->GetFather()->GetSubMesh(aV2)->GetSubMeshDS() ) {
5323 aItN = locTrack->GetFather()->GetSubMesh(aV2)->GetSubMeshDS()->GetNodes();
5326 // starting node must be aN1 or aN2
5327 aN1isOK = ( aN1 && aN1 == theN1 );
5328 aN2isOK = ( aN2 && aN2 == theN1 );
5331 // we have specified ending vertex of the previous edge on the previous iteration
5332 // and we have just to check that it corresponds to any vertex in current segment
5333 aN1isOK = aVprev.IsSame( aV1 );
5334 aN2isOK = aVprev.IsSame( aV2 );
5336 if ( !aN1isOK && !aN2isOK ) continue;
5337 // 2. Collect parameters on the track edge
5339 aItN = locMeshDS->GetNodes();
5340 while ( aItN->more() ) {
5341 const SMDS_MeshNode* pNode = aItN->next();
5342 const SMDS_EdgePosition* pEPos =
5343 static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
5344 double aT = pEPos->GetUParameter();
5345 aPrms.push_back( aT );
5347 list<SMESH_MeshEditor_PathPoint> LPP;
5348 //Extrusion_Error err =
5349 MakeEdgePathPoints(aPrms, aTrackEdge, aN1isOK, LPP);
5350 LLPPs.push_back(LPP);
5352 // update startN for search following egde
5353 if ( aN1isOK ) aVprev = aV2;
5358 list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
5359 list<SMESH_MeshEditor_PathPoint> firstList = *itLLPP;
5360 list<SMESH_MeshEditor_PathPoint>::iterator itPP = firstList.begin();
5361 for(; itPP!=firstList.end(); itPP++) {
5362 fullList.push_back( *itPP );
5364 SMESH_MeshEditor_PathPoint PP1 = fullList.back();
5365 fullList.pop_back();
5367 for(; itLLPP!=LLPPs.end(); itLLPP++) {
5368 list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
5369 itPP = currList.begin();
5370 SMESH_MeshEditor_PathPoint PP2 = currList.front();
5371 gp_Dir D1 = PP1.Tangent();
5372 gp_Dir D2 = PP2.Tangent();
5373 gp_Dir Dnew( ( D1.XYZ() + D2.XYZ() ) / 2 );
5374 PP1.SetTangent(Dnew);
5375 fullList.push_back(PP1);
5377 for(; itPP!=currList.end(); itPP++) {
5378 fullList.push_back( *itPP );
5380 PP1 = fullList.back();
5381 fullList.pop_back();
5383 // if wire not closed
5384 fullList.push_back(PP1);
5388 return EXTR_BAD_PATH_SHAPE;
5391 return MakeExtrElements(theElements, fullList, theHasAngles, theAngles, theLinearVariation,
5392 theHasRefPoint, theRefPoint, theMakeGroups);
5396 //=======================================================================
5397 //function : MakeEdgePathPoints
5398 //purpose : auxilary for ExtrusionAlongTrack
5399 //=======================================================================
5400 SMESH_MeshEditor::Extrusion_Error
5401 SMESH_MeshEditor::MakeEdgePathPoints(std::list<double>& aPrms,
5402 const TopoDS_Edge& aTrackEdge,
5404 list<SMESH_MeshEditor_PathPoint>& LPP)
5406 Standard_Real aTx1, aTx2, aL2, aTolVec, aTolVec2;
5408 aTolVec2=aTolVec*aTolVec;
5410 TopoDS_Vertex aV1, aV2;
5411 TopExp::Vertices( aTrackEdge, aV1, aV2 );
5412 aT1=BRep_Tool::Parameter( aV1, aTrackEdge );
5413 aT2=BRep_Tool::Parameter( aV2, aTrackEdge );
5414 // 2. Collect parameters on the track edge
5415 aPrms.push_front( aT1 );
5416 aPrms.push_back( aT2 );
5419 if( FirstIsStart ) {
5430 SMESH_MeshEditor_PathPoint aPP;
5431 Handle(Geom_Curve) aC3D = BRep_Tool::Curve( aTrackEdge, aTx1, aTx2 );
5432 std::list<double>::iterator aItD = aPrms.begin();
5433 for(; aItD != aPrms.end(); ++aItD) {
5437 aC3D->D1( aT, aP3D, aVec );
5438 aL2 = aVec.SquareMagnitude();
5439 if ( aL2 < aTolVec2 )
5440 return EXTR_CANT_GET_TANGENT;
5441 gp_Dir aTgt( aVec );
5443 aPP.SetTangent( aTgt );
5444 aPP.SetParameter( aT );
5451 //=======================================================================
5452 //function : MakeExtrElements
5453 //purpose : auxilary for ExtrusionAlongTrack
5454 //=======================================================================
5455 SMESH_MeshEditor::Extrusion_Error
5456 SMESH_MeshEditor::MakeExtrElements(TIDSortedElemSet& theElements,
5457 list<SMESH_MeshEditor_PathPoint>& fullList,
5458 const bool theHasAngles,
5459 list<double>& theAngles,
5460 const bool theLinearVariation,
5461 const bool theHasRefPoint,
5462 const gp_Pnt& theRefPoint,
5463 const bool theMakeGroups)
5465 MESSAGE("MakeExtrElements");
5466 //cout<<"MakeExtrElements fullList.size() = "<<fullList.size()<<endl;
5467 int aNbTP = fullList.size();
5468 vector<SMESH_MeshEditor_PathPoint> aPPs(aNbTP);
5470 if( theHasAngles && theAngles.size()>0 && theLinearVariation ) {
5471 LinearAngleVariation(aNbTP-1, theAngles);
5473 vector<double> aAngles( aNbTP );
5475 for(; j<aNbTP; ++j) {
5478 if ( theHasAngles ) {
5480 std::list<double>::iterator aItD = theAngles.begin();
5481 for ( j=1; (aItD != theAngles.end()) && (j<aNbTP); ++aItD, ++j ) {
5483 aAngles[j] = anAngle;
5486 // fill vector of path points with angles
5487 //aPPs.resize(fullList.size());
5489 list<SMESH_MeshEditor_PathPoint>::iterator itPP = fullList.begin();
5490 for(; itPP!=fullList.end(); itPP++) {
5492 SMESH_MeshEditor_PathPoint PP = *itPP;
5493 PP.SetAngle(aAngles[j]);
5497 TNodeOfNodeListMap mapNewNodes;
5498 TElemOfVecOfNnlmiMap mapElemNewNodes;
5499 TElemOfElemListMap newElemsMap;
5500 TIDSortedElemSet::iterator itElem;
5503 SMDSAbs_ElementType aTypeE;
5504 // source elements for each generated one
5505 SMESH_SequenceOfElemPtr srcElems, srcNodes;
5507 // 3. Center of rotation aV0
5508 gp_Pnt aV0 = theRefPoint;
5510 if ( !theHasRefPoint ) {
5512 aGC.SetCoord( 0.,0.,0. );
5514 itElem = theElements.begin();
5515 for ( ; itElem != theElements.end(); itElem++ ) {
5516 const SMDS_MeshElement* elem = *itElem;
5518 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5519 while ( itN->more() ) {
5520 const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( itN->next() );
5525 if ( mapNewNodes.find( node ) == mapNewNodes.end() ) {
5526 list<const SMDS_MeshNode*> aLNx;
5527 mapNewNodes[node] = aLNx;
5529 gp_XYZ aXYZ( aX, aY, aZ );
5537 } // if (!theHasRefPoint) {
5538 mapNewNodes.clear();
5540 // 4. Processing the elements
5541 SMESHDS_Mesh* aMesh = GetMeshDS();
5543 for ( itElem = theElements.begin(); itElem != theElements.end(); itElem++ ) {
5544 // check element type
5545 const SMDS_MeshElement* elem = *itElem;
5546 aTypeE = elem->GetType();
5547 if ( !elem || ( aTypeE != SMDSAbs_Face && aTypeE != SMDSAbs_Edge ) )
5550 vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5551 newNodesItVec.reserve( elem->NbNodes() );
5553 // loop on elem nodes
5555 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5556 while ( itN->more() )
5559 // check if a node has been already processed
5560 const SMDS_MeshNode* node =
5561 static_cast<const SMDS_MeshNode*>( itN->next() );
5562 TNodeOfNodeListMap::iterator nIt = mapNewNodes.find( node );
5563 if ( nIt == mapNewNodes.end() ) {
5564 nIt = mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5565 list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5568 aX = node->X(); aY = node->Y(); aZ = node->Z();
5570 Standard_Real aAngle1x, aAngleT1T0, aTolAng;
5571 gp_Pnt aP0x, aP1x, aPN0, aPN1, aV0x, aV1x;
5572 gp_Ax1 anAx1, anAxT1T0;
5573 gp_Dir aDT1x, aDT0x, aDT1T0;
5578 aPN0.SetCoord(aX, aY, aZ);
5580 const SMESH_MeshEditor_PathPoint& aPP0 = aPPs[0];
5582 aDT0x= aPP0.Tangent();
5583 //cout<<"j = 0 PP: Pnt("<<aP0x.X()<<","<<aP0x.Y()<<","<<aP0x.Z()<<")"<<endl;
5585 for ( j = 1; j < aNbTP; ++j ) {
5586 const SMESH_MeshEditor_PathPoint& aPP1 = aPPs[j];
5588 aDT1x = aPP1.Tangent();
5589 aAngle1x = aPP1.Angle();
5591 gp_Trsf aTrsf, aTrsfRot, aTrsfRotT1T0;
5593 gp_Vec aV01x( aP0x, aP1x );
5594 aTrsf.SetTranslation( aV01x );
5597 aV1x = aV0x.Transformed( aTrsf );
5598 aPN1 = aPN0.Transformed( aTrsf );
5600 // rotation 1 [ T1,T0 ]
5601 aAngleT1T0=-aDT1x.Angle( aDT0x );
5602 if (fabs(aAngleT1T0) > aTolAng) {
5604 anAxT1T0.SetLocation( aV1x );
5605 anAxT1T0.SetDirection( aDT1T0 );
5606 aTrsfRotT1T0.SetRotation( anAxT1T0, aAngleT1T0 );
5608 aPN1 = aPN1.Transformed( aTrsfRotT1T0 );
5612 if ( theHasAngles ) {
5613 anAx1.SetLocation( aV1x );
5614 anAx1.SetDirection( aDT1x );
5615 aTrsfRot.SetRotation( anAx1, aAngle1x );
5617 aPN1 = aPN1.Transformed( aTrsfRot );
5621 //MESSAGE("elem->IsQuadratic " << elem->IsQuadratic() << " " << elem->IsMediumNode(node));
5622 if( elem->IsQuadratic() && !elem->IsMediumNode(node) ) {
5623 // create additional node
5624 double x = ( aPN1.X() + aPN0.X() )/2.;
5625 double y = ( aPN1.Y() + aPN0.Y() )/2.;
5626 double z = ( aPN1.Z() + aPN0.Z() )/2.;
5627 const SMDS_MeshNode* newNode = aMesh->AddNode(x,y,z);
5628 myLastCreatedNodes.Append(newNode);
5629 srcNodes.Append( node );
5630 listNewNodes.push_back( newNode );
5635 const SMDS_MeshNode* newNode = aMesh->AddNode( aX, aY, aZ );
5636 myLastCreatedNodes.Append(newNode);
5637 srcNodes.Append( node );
5638 listNewNodes.push_back( newNode );
5648 // if current elem is quadratic and current node is not medium
5649 // we have to check - may be it is needed to insert additional nodes
5650 if( elem->IsQuadratic() && !elem->IsMediumNode(node) ) {
5651 list< const SMDS_MeshNode* > & listNewNodes = nIt->second;
5652 if(listNewNodes.size()==aNbTP-1) {
5653 vector<const SMDS_MeshNode*> aNodes(2*(aNbTP-1));
5654 gp_XYZ P(node->X(), node->Y(), node->Z());
5655 list< const SMDS_MeshNode* >::iterator it = listNewNodes.begin();
5657 for(i=0; i<aNbTP-1; i++) {
5658 const SMDS_MeshNode* N = *it;
5659 double x = ( N->X() + P.X() )/2.;
5660 double y = ( N->Y() + P.Y() )/2.;
5661 double z = ( N->Z() + P.Z() )/2.;
5662 const SMDS_MeshNode* newN = aMesh->AddNode(x,y,z);
5663 srcNodes.Append( node );
5664 myLastCreatedNodes.Append(newN);
5667 P = gp_XYZ(N->X(),N->Y(),N->Z());
5669 listNewNodes.clear();
5670 for(i=0; i<2*(aNbTP-1); i++) {
5671 listNewNodes.push_back(aNodes[i]);
5677 newNodesItVec.push_back( nIt );
5679 // make new elements
5680 //sweepElement( aMesh, elem, newNodesItVec, newElemsMap[elem],
5681 // newNodesItVec[0]->second.size(), myLastCreatedElems );
5682 sweepElement( elem, newNodesItVec, newElemsMap[elem], aNbTP-1, srcElems );
5685 makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElements, aNbTP-1, srcElems );
5687 if ( theMakeGroups )
5688 generateGroups( srcNodes, srcElems, "extruded");
5694 //=======================================================================
5695 //function : LinearAngleVariation
5696 //purpose : auxilary for ExtrusionAlongTrack
5697 //=======================================================================
5698 void SMESH_MeshEditor::LinearAngleVariation(const int nbSteps,
5699 list<double>& Angles)
5701 int nbAngles = Angles.size();
5702 if( nbSteps > nbAngles ) {
5703 vector<double> theAngles(nbAngles);
5704 list<double>::iterator it = Angles.begin();
5706 for(; it!=Angles.end(); it++) {
5708 theAngles[i] = (*it);
5711 double rAn2St = double( nbAngles ) / double( nbSteps );
5712 double angPrev = 0, angle;
5713 for ( int iSt = 0; iSt < nbSteps; ++iSt ) {
5714 double angCur = rAn2St * ( iSt+1 );
5715 double angCurFloor = floor( angCur );
5716 double angPrevFloor = floor( angPrev );
5717 if ( angPrevFloor == angCurFloor )
5718 angle = rAn2St * theAngles[ int( angCurFloor ) ];
5720 int iP = int( angPrevFloor );
5721 double angPrevCeil = ceil(angPrev);
5722 angle = ( angPrevCeil - angPrev ) * theAngles[ iP ];
5724 int iC = int( angCurFloor );
5725 if ( iC < nbAngles )
5726 angle += ( angCur - angCurFloor ) * theAngles[ iC ];
5728 iP = int( angPrevCeil );
5730 angle += theAngles[ iC ];
5732 res.push_back(angle);
5737 for(; it!=res.end(); it++)
5738 Angles.push_back( *it );
5743 //================================================================================
5745 * \brief Move or copy theElements applying theTrsf to their nodes
5746 * \param theElems - elements to transform, if theElems is empty then apply to all mesh nodes
5747 * \param theTrsf - transformation to apply
5748 * \param theCopy - if true, create translated copies of theElems
5749 * \param theMakeGroups - if true and theCopy, create translated groups
5750 * \param theTargetMesh - mesh to copy translated elements into
5751 * \return SMESH_MeshEditor::PGroupIDs - list of ids of created groups
5753 //================================================================================
5755 SMESH_MeshEditor::PGroupIDs
5756 SMESH_MeshEditor::Transform (TIDSortedElemSet & theElems,
5757 const gp_Trsf& theTrsf,
5759 const bool theMakeGroups,
5760 SMESH_Mesh* theTargetMesh)
5762 myLastCreatedElems.Clear();
5763 myLastCreatedNodes.Clear();
5765 bool needReverse = false;
5766 string groupPostfix;
5767 switch ( theTrsf.Form() ) {
5769 MESSAGE("gp_PntMirror");
5771 groupPostfix = "mirrored";
5774 MESSAGE("gp_Ax1Mirror");
5775 groupPostfix = "mirrored";
5778 MESSAGE("gp_Ax2Mirror");
5780 groupPostfix = "mirrored";
5783 MESSAGE("gp_Rotation");
5784 groupPostfix = "rotated";
5786 case gp_Translation:
5787 MESSAGE("gp_Translation");
5788 groupPostfix = "translated";
5791 MESSAGE("gp_Scale");
5792 groupPostfix = "scaled";
5794 case gp_CompoundTrsf: // different scale by axis
5795 MESSAGE("gp_CompoundTrsf");
5796 groupPostfix = "scaled";
5800 needReverse = false;
5801 groupPostfix = "transformed";
5804 SMESH_MeshEditor targetMeshEditor( theTargetMesh );
5805 SMESHDS_Mesh* aTgtMesh = theTargetMesh ? theTargetMesh->GetMeshDS() : 0;
5806 SMESHDS_Mesh* aMesh = GetMeshDS();
5809 // map old node to new one
5810 TNodeNodeMap nodeMap;
5812 // elements sharing moved nodes; those of them which have all
5813 // nodes mirrored but are not in theElems are to be reversed
5814 TIDSortedElemSet inverseElemSet;
5816 // source elements for each generated one
5817 SMESH_SequenceOfElemPtr srcElems, srcNodes;
5819 // issue 021015: EDF 1578 SMESH: Free nodes are removed when translating a mesh
5820 TIDSortedElemSet orphanNode;
5822 if ( theElems.empty() ) // transform the whole mesh
5825 SMDS_ElemIteratorPtr eIt = aMesh->elementsIterator();
5826 while ( eIt->more() ) theElems.insert( eIt->next() );
5828 SMDS_NodeIteratorPtr nIt = aMesh->nodesIterator();
5829 while ( nIt->more() )
5831 const SMDS_MeshNode* node = nIt->next();
5832 if ( node->NbInverseElements() == 0)
5833 orphanNode.insert( node );
5837 // loop on elements to transform nodes : first orphan nodes then elems
5838 TIDSortedElemSet::iterator itElem;
5839 TIDSortedElemSet *elements[] = {&orphanNode, &theElems };
5840 for (int i=0; i<2; i++)
5841 for ( itElem = elements[i]->begin(); itElem != elements[i]->end(); itElem++ ) {
5842 const SMDS_MeshElement* elem = *itElem;
5846 // loop on elem nodes
5847 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5848 while ( itN->more() ) {
5850 const SMDS_MeshNode* node = cast2Node( itN->next() );
5851 // check if a node has been already transformed
5852 pair<TNodeNodeMap::iterator,bool> n2n_isnew =
5853 nodeMap.insert( make_pair ( node, node ));
5854 if ( !n2n_isnew.second )
5858 coord[0] = node->X();
5859 coord[1] = node->Y();
5860 coord[2] = node->Z();
5861 theTrsf.Transforms( coord[0], coord[1], coord[2] );
5862 if ( theTargetMesh ) {
5863 const SMDS_MeshNode * newNode = aTgtMesh->AddNode( coord[0], coord[1], coord[2] );
5864 n2n_isnew.first->second = newNode;
5865 myLastCreatedNodes.Append(newNode);
5866 srcNodes.Append( node );
5868 else if ( theCopy ) {
5869 const SMDS_MeshNode * newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5870 n2n_isnew.first->second = newNode;
5871 myLastCreatedNodes.Append(newNode);
5872 srcNodes.Append( node );
5875 aMesh->MoveNode( node, coord[0], coord[1], coord[2] );
5876 // node position on shape becomes invalid
5877 const_cast< SMDS_MeshNode* > ( node )->SetPosition
5878 ( SMDS_SpacePosition::originSpacePosition() );
5881 // keep inverse elements
5882 if ( !theCopy && !theTargetMesh && needReverse ) {
5883 SMDS_ElemIteratorPtr invElemIt = node->GetInverseElementIterator();
5884 while ( invElemIt->more() ) {
5885 const SMDS_MeshElement* iel = invElemIt->next();
5886 inverseElemSet.insert( iel );
5892 // either create new elements or reverse mirrored ones
5893 if ( !theCopy && !needReverse && !theTargetMesh )
5896 TIDSortedElemSet::iterator invElemIt = inverseElemSet.begin();
5897 for ( ; invElemIt != inverseElemSet.end(); invElemIt++ )
5898 theElems.insert( *invElemIt );
5900 // Replicate or reverse elements
5902 std::vector<int> iForw;
5903 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
5905 const SMDS_MeshElement* elem = *itElem;
5906 if ( !elem ) continue;
5908 SMDSAbs_GeometryType geomType = elem->GetGeomType();
5909 int nbNodes = elem->NbNodes();
5910 if ( geomType == SMDSGeom_NONE ) continue; // node
5912 switch ( geomType ) {
5914 case SMDSGeom_POLYGON: // ---------------------- polygon
5916 vector<const SMDS_MeshNode*> poly_nodes (nbNodes);
5918 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5919 while (itN->more()) {
5920 const SMDS_MeshNode* node =
5921 static_cast<const SMDS_MeshNode*>(itN->next());
5922 TNodeNodeMap::iterator nodeMapIt = nodeMap.find(node);
5923 if (nodeMapIt == nodeMap.end())
5924 break; // not all nodes transformed
5926 // reverse mirrored faces and volumes
5927 poly_nodes[nbNodes - iNode - 1] = (*nodeMapIt).second;
5929 poly_nodes[iNode] = (*nodeMapIt).second;
5933 if ( iNode != nbNodes )
5934 continue; // not all nodes transformed
5936 if ( theTargetMesh ) {
5937 myLastCreatedElems.Append(aTgtMesh->AddPolygonalFace(poly_nodes));
5938 srcElems.Append( elem );
5940 else if ( theCopy ) {
5941 myLastCreatedElems.Append(aMesh->AddPolygonalFace(poly_nodes));
5942 srcElems.Append( elem );
5945 aMesh->ChangePolygonNodes(elem, poly_nodes);
5950 case SMDSGeom_POLYHEDRA: // ------------------ polyhedral volume
5952 const SMDS_VtkVolume* aPolyedre =
5953 dynamic_cast<const SMDS_VtkVolume*>( elem );
5955 MESSAGE("Warning: bad volumic element");
5959 vector<const SMDS_MeshNode*> poly_nodes; poly_nodes.reserve( nbNodes );
5960 vector<int> quantities; quantities.reserve( nbNodes );
5962 bool allTransformed = true;
5963 int nbFaces = aPolyedre->NbFaces();
5964 for (int iface = 1; iface <= nbFaces && allTransformed; iface++) {
5965 int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
5966 for (int inode = 1; inode <= nbFaceNodes && allTransformed; inode++) {
5967 const SMDS_MeshNode* node = aPolyedre->GetFaceNode(iface, inode);
5968 TNodeNodeMap::iterator nodeMapIt = nodeMap.find(node);
5969 if (nodeMapIt == nodeMap.end()) {
5970 allTransformed = false; // not all nodes transformed
5972 poly_nodes.push_back((*nodeMapIt).second);
5974 if ( needReverse && allTransformed )
5975 std::reverse( poly_nodes.end() - nbFaceNodes, poly_nodes.end() );
5977 quantities.push_back(nbFaceNodes);
5979 if ( !allTransformed )
5980 continue; // not all nodes transformed
5982 if ( theTargetMesh ) {
5983 myLastCreatedElems.Append(aTgtMesh->AddPolyhedralVolume(poly_nodes, quantities));
5984 srcElems.Append( elem );
5986 else if ( theCopy ) {
5987 myLastCreatedElems.Append(aMesh->AddPolyhedralVolume(poly_nodes, quantities));
5988 srcElems.Append( elem );
5991 aMesh->ChangePolyhedronNodes(elem, poly_nodes, quantities);
5996 case SMDSGeom_BALL: // -------------------- Ball
5998 if ( !theCopy && !theTargetMesh ) continue;
6000 TNodeNodeMap::iterator nodeMapIt = nodeMap.find( elem->GetNode(0) );
6001 if (nodeMapIt == nodeMap.end())
6002 continue; // not all nodes transformed
6004 double diameter = static_cast<const SMDS_BallElement*>(elem)->GetDiameter();
6005 if ( theTargetMesh ) {
6006 myLastCreatedElems.Append(aTgtMesh->AddBall( nodeMapIt->second, diameter ));
6007 srcElems.Append( elem );
6010 myLastCreatedElems.Append(aMesh->AddBall( nodeMapIt->second, diameter ));
6011 srcElems.Append( elem );
6016 default: // ----------------------- Regular elements
6018 while ( iForw.size() < nbNodes ) iForw.push_back( iForw.size() );
6019 const std::vector<int>& iRev = SMDS_MeshCell::reverseSmdsOrder( elem->GetEntityType() );
6020 const std::vector<int>& i = needReverse ? iRev : iForw;
6022 // find transformed nodes
6023 vector<const SMDS_MeshNode*> nodes(nbNodes);
6025 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6026 while ( itN->more() ) {
6027 const SMDS_MeshNode* node =
6028 static_cast<const SMDS_MeshNode*>( itN->next() );
6029 TNodeNodeMap::iterator nodeMapIt = nodeMap.find( node );
6030 if ( nodeMapIt == nodeMap.end() )
6031 break; // not all nodes transformed
6032 nodes[ i [ iNode++ ]] = (*nodeMapIt).second;
6034 if ( iNode != nbNodes )
6035 continue; // not all nodes transformed
6037 if ( theTargetMesh ) {
6038 if ( SMDS_MeshElement* copy =
6039 targetMeshEditor.AddElement( nodes, elem->GetType(), elem->IsPoly() )) {
6040 myLastCreatedElems.Append( copy );
6041 srcElems.Append( elem );
6044 else if ( theCopy ) {
6045 if ( AddElement( nodes, elem->GetType(), elem->IsPoly() ))
6046 srcElems.Append( elem );
6049 // reverse element as it was reversed by transformation
6051 aMesh->ChangeElementNodes( elem, &nodes[0], nbNodes );
6053 } // switch ( geomType )
6055 } // loop on elements
6057 PGroupIDs newGroupIDs;
6059 if ( ( theMakeGroups && theCopy ) ||
6060 ( theMakeGroups && theTargetMesh ) )
6061 newGroupIDs = generateGroups( srcNodes, srcElems, groupPostfix, theTargetMesh );
6066 //=======================================================================
6068 * \brief Create groups of elements made during transformation
6069 * \param nodeGens - nodes making corresponding myLastCreatedNodes
6070 * \param elemGens - elements making corresponding myLastCreatedElems
6071 * \param postfix - to append to names of new groups
6073 //=======================================================================
6075 SMESH_MeshEditor::PGroupIDs
6076 SMESH_MeshEditor::generateGroups(const SMESH_SequenceOfElemPtr& nodeGens,
6077 const SMESH_SequenceOfElemPtr& elemGens,
6078 const std::string& postfix,
6079 SMESH_Mesh* targetMesh)
6081 PGroupIDs newGroupIDs( new list<int> );
6082 SMESH_Mesh* mesh = targetMesh ? targetMesh : GetMesh();
6084 // Sort existing groups by types and collect their names
6086 // to store an old group and a generated new ones
6088 using boost::make_tuple;
6089 typedef tuple< SMESHDS_GroupBase*, SMESHDS_Group*, SMESHDS_Group* > TOldNewGroup;
6090 vector< list< TOldNewGroup > > groupsByType( SMDSAbs_NbElementTypes );
6091 vector< TOldNewGroup* > orderedOldNewGroups; // in order of old groups
6093 set< string > groupNames;
6095 SMESH_Mesh::GroupIteratorPtr groupIt = GetMesh()->GetGroups();
6096 if ( !groupIt->more() ) return newGroupIDs;
6098 int newGroupID = mesh->GetGroupIds().back()+1;
6099 while ( groupIt->more() )
6101 SMESH_Group * group = groupIt->next();
6102 if ( !group ) continue;
6103 SMESHDS_GroupBase* groupDS = group->GetGroupDS();
6104 if ( !groupDS || groupDS->IsEmpty() ) continue;
6105 groupNames.insert ( group->GetName() );
6106 groupDS->SetStoreName( group->GetName() );
6107 const SMDSAbs_ElementType type = groupDS->GetType();
6108 SMESHDS_Group* newGroup = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
6109 SMESHDS_Group* newTopGroup = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
6110 groupsByType[ type ].push_back( make_tuple( groupDS, newGroup, newTopGroup ));
6111 orderedOldNewGroups.push_back( & groupsByType[ type ].back() );
6114 // Loop on nodes and elements to add them in new groups
6116 for ( int isNodes = 0; isNodes < 2; ++isNodes )
6118 const SMESH_SequenceOfElemPtr& gens = isNodes ? nodeGens : elemGens;
6119 const SMESH_SequenceOfElemPtr& elems = isNodes ? myLastCreatedNodes : myLastCreatedElems;
6120 if ( gens.Length() != elems.Length() )
6121 throw SALOME_Exception("SMESH_MeshEditor::generateGroups(): invalid args");
6123 // loop on created elements
6124 for (int iElem = 1; iElem <= elems.Length(); ++iElem )
6126 const SMDS_MeshElement* sourceElem = gens( iElem );
6127 if ( !sourceElem ) {
6128 MESSAGE("generateGroups(): NULL source element");
6131 list< TOldNewGroup > & groupsOldNew = groupsByType[ sourceElem->GetType() ];
6132 if ( groupsOldNew.empty() ) { // no groups of this type at all
6133 while ( iElem < gens.Length() && gens( iElem+1 ) == sourceElem )
6134 ++iElem; // skip all elements made by sourceElem
6137 // collect all elements made by the iElem-th sourceElem
6138 list< const SMDS_MeshElement* > resultElems;
6139 if ( const SMDS_MeshElement* resElem = elems( iElem ))
6140 if ( resElem != sourceElem )
6141 resultElems.push_back( resElem );
6142 while ( iElem < gens.Length() && gens( iElem+1 ) == sourceElem )
6143 if ( const SMDS_MeshElement* resElem = elems( ++iElem ))
6144 if ( resElem != sourceElem )
6145 resultElems.push_back( resElem );
6147 // there must be a top element
6148 const SMDS_MeshElement* topElem = 0;
6151 topElem = resultElems.back();
6152 resultElems.pop_back();
6156 list< const SMDS_MeshElement* >::reverse_iterator resElemIt = resultElems.rbegin();
6157 for ( ; resElemIt != resultElems.rend() ; ++resElemIt )
6158 if ( (*resElemIt)->GetType() == sourceElem->GetType() )
6160 topElem = *resElemIt;
6161 resultElems.erase( --(resElemIt.base()) ); // erase *resElemIt
6166 // add resultElems to groups originted from ones the sourceElem belongs to
6167 list< TOldNewGroup >::iterator gOldNew, gLast = groupsOldNew.end();
6168 for ( gOldNew = groupsOldNew.begin(); gOldNew != gLast; ++gOldNew )
6170 SMESHDS_GroupBase* oldGroup = gOldNew->get<0>();
6171 if ( oldGroup->Contains( sourceElem )) // sourceElem is in oldGroup
6173 // fill in a new group
6174 SMDS_MeshGroup & newGroup = gOldNew->get<1>()->SMDSGroup();
6175 list< const SMDS_MeshElement* >::iterator resLast = resultElems.end(), resElemIt;
6176 for ( resElemIt = resultElems.begin(); resElemIt != resLast; ++resElemIt )
6177 newGroup.Add( *resElemIt );
6179 // fill a "top" group
6182 SMDS_MeshGroup & newTopGroup = gOldNew->get<2>()->SMDSGroup();
6183 newTopGroup.Add( topElem );
6187 } // loop on created elements
6188 }// loop on nodes and elements
6190 // Create new SMESH_Groups from SMESHDS_Groups and remove empty SMESHDS_Groups
6192 list<int> topGrouIds;
6193 for ( size_t i = 0; i < orderedOldNewGroups.size(); ++i )
6195 SMESHDS_GroupBase* oldGroupDS = orderedOldNewGroups[i]->get<0>();
6196 SMESHDS_Group* newGroups[2] = { orderedOldNewGroups[i]->get<1>(),
6197 orderedOldNewGroups[i]->get<2>() };
6198 const int nbNewGroups = !newGroups[0]->IsEmpty() + !newGroups[1]->IsEmpty();
6199 for ( int is2nd = 0; is2nd < 2; ++is2nd )
6201 SMESHDS_Group* newGroupDS = newGroups[ is2nd ];
6202 if ( newGroupDS->IsEmpty() )
6204 mesh->GetMeshDS()->RemoveGroup( newGroupDS );
6209 newGroupDS->SetType( newGroupDS->GetElements()->next()->GetType() );
6212 const bool isTop = ( nbNewGroups == 2 &&
6213 newGroupDS->GetType() == oldGroupDS->GetType() &&
6216 string name = oldGroupDS->GetStoreName();
6217 if ( !targetMesh ) {
6218 string suffix = ( isTop ? "top": postfix.c_str() );
6222 while ( !groupNames.insert( name ).second ) // name exists
6223 name = SMESH_Comment( oldGroupDS->GetStoreName() ) << "_" << suffix << "_" << nb++;
6228 newGroupDS->SetStoreName( name.c_str() );
6230 // make a SMESH_Groups
6231 mesh->AddGroup( newGroupDS );
6233 topGrouIds.push_back( newGroupDS->GetID() );
6235 newGroupIDs->push_back( newGroupDS->GetID() );
6239 newGroupIDs->splice( newGroupIDs->end(), topGrouIds );
6244 //================================================================================
6246 * \brief Return list of group of nodes close to each other within theTolerance
6247 * Search among theNodes or in the whole mesh if theNodes is empty using
6248 * an Octree algorithm
6250 //================================================================================
6252 void SMESH_MeshEditor::FindCoincidentNodes (TIDSortedNodeSet & theNodes,
6253 const double theTolerance,
6254 TListOfListOfNodes & theGroupsOfNodes)
6256 myLastCreatedElems.Clear();
6257 myLastCreatedNodes.Clear();
6259 if ( theNodes.empty() )
6260 { // get all nodes in the mesh
6261 SMDS_NodeIteratorPtr nIt = GetMeshDS()->nodesIterator(/*idInceasingOrder=*/true);
6262 while ( nIt->more() )
6263 theNodes.insert( theNodes.end(),nIt->next());
6266 SMESH_OctreeNode::FindCoincidentNodes ( theNodes, &theGroupsOfNodes, theTolerance);
6269 //=======================================================================
6270 //function : SimplifyFace
6272 //=======================================================================
6274 int SMESH_MeshEditor::SimplifyFace (const vector<const SMDS_MeshNode *>& faceNodes,
6275 vector<const SMDS_MeshNode *>& poly_nodes,
6276 vector<int>& quantities) const
6278 int nbNodes = faceNodes.size();
6283 set<const SMDS_MeshNode*> nodeSet;
6285 // get simple seq of nodes
6286 //const SMDS_MeshNode* simpleNodes[ nbNodes ];
6287 vector<const SMDS_MeshNode*> simpleNodes( nbNodes );
6288 int iSimple = 0, nbUnique = 0;
6290 simpleNodes[iSimple++] = faceNodes[0];
6292 for (int iCur = 1; iCur < nbNodes; iCur++) {
6293 if (faceNodes[iCur] != simpleNodes[iSimple - 1]) {
6294 simpleNodes[iSimple++] = faceNodes[iCur];
6295 if (nodeSet.insert( faceNodes[iCur] ).second)
6299 int nbSimple = iSimple;
6300 if (simpleNodes[nbSimple - 1] == simpleNodes[0]) {
6310 bool foundLoop = (nbSimple > nbUnique);
6313 set<const SMDS_MeshNode*> loopSet;
6314 for (iSimple = 0; iSimple < nbSimple && !foundLoop; iSimple++) {
6315 const SMDS_MeshNode* n = simpleNodes[iSimple];
6316 if (!loopSet.insert( n ).second) {
6320 int iC = 0, curLast = iSimple;
6321 for (; iC < curLast; iC++) {
6322 if (simpleNodes[iC] == n) break;
6324 int loopLen = curLast - iC;
6326 // create sub-element
6328 quantities.push_back(loopLen);
6329 for (; iC < curLast; iC++) {
6330 poly_nodes.push_back(simpleNodes[iC]);
6333 // shift the rest nodes (place from the first loop position)
6334 for (iC = curLast + 1; iC < nbSimple; iC++) {
6335 simpleNodes[iC - loopLen] = simpleNodes[iC];
6337 nbSimple -= loopLen;
6340 } // for (iSimple = 0; iSimple < nbSimple; iSimple++)
6341 } // while (foundLoop)
6345 quantities.push_back(iSimple);
6346 for (int i = 0; i < iSimple; i++)
6347 poly_nodes.push_back(simpleNodes[i]);
6353 //=======================================================================
6354 //function : MergeNodes
6355 //purpose : In each group, the cdr of nodes are substituted by the first one
6357 //=======================================================================
6359 void SMESH_MeshEditor::MergeNodes (TListOfListOfNodes & theGroupsOfNodes)
6361 MESSAGE("MergeNodes");
6362 myLastCreatedElems.Clear();
6363 myLastCreatedNodes.Clear();
6365 SMESHDS_Mesh* aMesh = GetMeshDS();
6367 TNodeNodeMap nodeNodeMap; // node to replace - new node
6368 set<const SMDS_MeshElement*> elems; // all elements with changed nodes
6369 list< int > rmElemIds, rmNodeIds;
6371 // Fill nodeNodeMap and elems
6373 TListOfListOfNodes::iterator grIt = theGroupsOfNodes.begin();
6374 for ( ; grIt != theGroupsOfNodes.end(); grIt++ ) {
6375 list<const SMDS_MeshNode*>& nodes = *grIt;
6376 list<const SMDS_MeshNode*>::iterator nIt = nodes.begin();
6377 const SMDS_MeshNode* nToKeep = *nIt;
6378 //MESSAGE("node to keep " << nToKeep->GetID());
6379 for ( ++nIt; nIt != nodes.end(); nIt++ ) {
6380 const SMDS_MeshNode* nToRemove = *nIt;
6381 nodeNodeMap.insert( TNodeNodeMap::value_type( nToRemove, nToKeep ));
6382 if ( nToRemove != nToKeep ) {
6383 //MESSAGE(" node to remove " << nToRemove->GetID());
6384 rmNodeIds.push_back( nToRemove->GetID() );
6385 AddToSameGroups( nToKeep, nToRemove, aMesh );
6386 // set _alwaysComputed to a sub-mesh of VERTEX to enable mesh computing
6387 // after MergeNodes() w/o creating node in place of merged ones.
6388 const SMDS_PositionPtr& pos = nToRemove->GetPosition();
6389 if ( pos && pos->GetTypeOfPosition() == SMDS_TOP_VERTEX )
6390 if ( SMESH_subMesh* sm = myMesh->GetSubMeshContaining( nToRemove->getshapeId() ))
6391 sm->SetIsAlwaysComputed( true );
6394 SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
6395 while ( invElemIt->more() ) {
6396 const SMDS_MeshElement* elem = invElemIt->next();
6401 // Change element nodes or remove an element
6403 set<const SMDS_MeshElement*>::iterator eIt = elems.begin();
6404 for ( ; eIt != elems.end(); eIt++ ) {
6405 const SMDS_MeshElement* elem = *eIt;
6406 //MESSAGE(" ---- inverse elem on node to remove " << elem->GetID());
6407 int nbNodes = elem->NbNodes();
6408 int aShapeId = FindShape( elem );
6410 set<const SMDS_MeshNode*> nodeSet;
6411 vector< const SMDS_MeshNode*> curNodes( nbNodes ), uniqueNodes( nbNodes );
6412 int iUnique = 0, iCur = 0, nbRepl = 0;
6413 vector<int> iRepl( nbNodes );
6415 // get new seq of nodes
6416 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6417 while ( itN->more() ) {
6418 const SMDS_MeshNode* n =
6419 static_cast<const SMDS_MeshNode*>( itN->next() );
6421 TNodeNodeMap::iterator nnIt = nodeNodeMap.find( n );
6422 if ( nnIt != nodeNodeMap.end() ) { // n sticks
6424 // BUG 0020185: begin
6426 bool stopRecur = false;
6427 set<const SMDS_MeshNode*> nodesRecur;
6428 nodesRecur.insert(n);
6429 while (!stopRecur) {
6430 TNodeNodeMap::iterator nnIt_i = nodeNodeMap.find( n );
6431 if ( nnIt_i != nodeNodeMap.end() ) { // n sticks
6432 n = (*nnIt_i).second;
6433 if (!nodesRecur.insert(n).second) {
6434 // error: recursive dependancy
6444 curNodes[ iCur ] = n;
6445 bool isUnique = nodeSet.insert( n ).second;
6447 uniqueNodes[ iUnique++ ] = n;
6449 iRepl[ nbRepl++ ] = iCur;
6453 // Analyse element topology after replacement
6456 int nbUniqueNodes = nodeSet.size();
6457 //MESSAGE("nbNodes nbUniqueNodes " << nbNodes << " " << nbUniqueNodes);
6458 if ( nbNodes != nbUniqueNodes ) { // some nodes stick
6459 // Polygons and Polyhedral volumes
6460 if (elem->IsPoly()) {
6462 if (elem->GetType() == SMDSAbs_Face) {
6464 vector<const SMDS_MeshNode *> face_nodes (nbNodes);
6466 for (; inode < nbNodes; inode++) {
6467 face_nodes[inode] = curNodes[inode];
6470 vector<const SMDS_MeshNode *> polygons_nodes;
6471 vector<int> quantities;
6472 int nbNew = SimplifyFace(face_nodes, polygons_nodes, quantities);
6475 for (int iface = 0; iface < nbNew; iface++) {
6476 int nbNodes = quantities[iface];
6477 vector<const SMDS_MeshNode *> poly_nodes (nbNodes);
6478 for (int ii = 0; ii < nbNodes; ii++, inode++) {
6479 poly_nodes[ii] = polygons_nodes[inode];
6481 SMDS_MeshElement* newElem = aMesh->AddPolygonalFace(poly_nodes);
6482 myLastCreatedElems.Append(newElem);
6484 aMesh->SetMeshElementOnShape(newElem, aShapeId);
6487 MESSAGE("ChangeElementNodes MergeNodes Polygon");
6488 //aMesh->ChangeElementNodes(elem, &polygons_nodes[inode], quantities[nbNew - 1]);
6489 vector<const SMDS_MeshNode *> polynodes(polygons_nodes.begin()+inode,polygons_nodes.end());
6491 if (nbNew > 0) quid = nbNew - 1;
6492 vector<int> newquant(quantities.begin()+quid, quantities.end());
6493 const SMDS_MeshElement* newElem = 0;
6494 newElem = aMesh->AddPolyhedralVolume(polynodes, newquant);
6495 myLastCreatedElems.Append(newElem);
6496 if ( aShapeId && newElem )
6497 aMesh->SetMeshElementOnShape( newElem, aShapeId );
6498 rmElemIds.push_back(elem->GetID());
6501 rmElemIds.push_back(elem->GetID());
6505 else if (elem->GetType() == SMDSAbs_Volume) {
6506 // Polyhedral volume
6507 if (nbUniqueNodes < 4) {
6508 rmElemIds.push_back(elem->GetID());
6511 // each face has to be analyzed in order to check volume validity
6512 const SMDS_VtkVolume* aPolyedre =
6513 dynamic_cast<const SMDS_VtkVolume*>( elem );
6515 int nbFaces = aPolyedre->NbFaces();
6517 vector<const SMDS_MeshNode *> poly_nodes;
6518 vector<int> quantities;
6520 for (int iface = 1; iface <= nbFaces; iface++) {
6521 int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
6522 vector<const SMDS_MeshNode *> faceNodes (nbFaceNodes);
6524 for (int inode = 1; inode <= nbFaceNodes; inode++) {
6525 const SMDS_MeshNode * faceNode = aPolyedre->GetFaceNode(iface, inode);
6526 TNodeNodeMap::iterator nnIt = nodeNodeMap.find(faceNode);
6527 if (nnIt != nodeNodeMap.end()) { // faceNode sticks
6528 faceNode = (*nnIt).second;
6530 faceNodes[inode - 1] = faceNode;
6533 SimplifyFace(faceNodes, poly_nodes, quantities);
6536 if (quantities.size() > 3) {
6537 // to be done: remove coincident faces
6540 if (quantities.size() > 3)
6542 MESSAGE("ChangeElementNodes MergeNodes Polyhedron");
6543 //aMesh->ChangePolyhedronNodes(elem, poly_nodes, quantities);
6544 const SMDS_MeshElement* newElem = 0;
6545 newElem = aMesh->AddPolyhedralVolume(poly_nodes, quantities);
6546 myLastCreatedElems.Append(newElem);
6547 if ( aShapeId && newElem )
6548 aMesh->SetMeshElementOnShape( newElem, aShapeId );
6549 rmElemIds.push_back(elem->GetID());
6553 rmElemIds.push_back(elem->GetID());
6564 // TODO not all the possible cases are solved. Find something more generic?
6565 switch ( nbNodes ) {
6566 case 2: ///////////////////////////////////// EDGE
6567 isOk = false; break;
6568 case 3: ///////////////////////////////////// TRIANGLE
6569 isOk = false; break;
6571 if ( elem->GetType() == SMDSAbs_Volume ) // TETRAHEDRON
6573 else { //////////////////////////////////// QUADRANGLE
6574 if ( nbUniqueNodes < 3 )
6576 else if ( nbRepl == 2 && iRepl[ 1 ] - iRepl[ 0 ] == 2 )
6577 isOk = false; // opposite nodes stick
6578 //MESSAGE("isOk " << isOk);
6581 case 6: ///////////////////////////////////// PENTAHEDRON
6582 if ( nbUniqueNodes == 4 ) {
6583 // ---------------------------------> tetrahedron
6585 iRepl[ 0 ] > 2 && iRepl[ 1 ] > 2 && iRepl[ 2 ] > 2 ) {
6586 // all top nodes stick: reverse a bottom
6587 uniqueNodes[ 0 ] = curNodes [ 1 ];
6588 uniqueNodes[ 1 ] = curNodes [ 0 ];
6590 else if (nbRepl == 3 &&
6591 iRepl[ 0 ] < 3 && iRepl[ 1 ] < 3 && iRepl[ 2 ] < 3 ) {
6592 // all bottom nodes stick: set a top before
6593 uniqueNodes[ 3 ] = uniqueNodes [ 0 ];
6594 uniqueNodes[ 0 ] = curNodes [ 3 ];
6595 uniqueNodes[ 1 ] = curNodes [ 4 ];
6596 uniqueNodes[ 2 ] = curNodes [ 5 ];
6598 else if (nbRepl == 4 &&
6599 iRepl[ 2 ] - iRepl [ 0 ] == 3 && iRepl[ 3 ] - iRepl [ 1 ] == 3 ) {
6600 // a lateral face turns into a line: reverse a bottom
6601 uniqueNodes[ 0 ] = curNodes [ 1 ];
6602 uniqueNodes[ 1 ] = curNodes [ 0 ];
6607 else if ( nbUniqueNodes == 5 ) {
6608 // PENTAHEDRON --------------------> 2 tetrahedrons
6609 if ( nbRepl == 2 && iRepl[ 1 ] - iRepl [ 0 ] == 3 ) {
6610 // a bottom node sticks with a linked top one
6612 SMDS_MeshElement* newElem =
6613 aMesh->AddVolume(curNodes[ 3 ],
6616 curNodes[ iRepl[ 0 ] == 2 ? 1 : 2 ]);
6617 myLastCreatedElems.Append(newElem);
6619 aMesh->SetMeshElementOnShape( newElem, aShapeId );
6620 // 2. : reverse a bottom
6621 uniqueNodes[ 0 ] = curNodes [ 1 ];
6622 uniqueNodes[ 1 ] = curNodes [ 0 ];
6632 if(elem->IsQuadratic()) { // Quadratic quadrangle
6644 MESSAGE("nbRepl=2: " << iRepl[0] << " " << iRepl[1]);
6647 MESSAGE("nbRepl=3: " << iRepl[0] << " " << iRepl[1] << " " << iRepl[2]);
6649 if( iRepl[0]==0 && iRepl[1]==1 && iRepl[2]==4 ) {
6650 uniqueNodes[0] = curNodes[0];
6651 uniqueNodes[1] = curNodes[2];
6652 uniqueNodes[2] = curNodes[3];
6653 uniqueNodes[3] = curNodes[5];
6654 uniqueNodes[4] = curNodes[6];
6655 uniqueNodes[5] = curNodes[7];
6658 if( iRepl[0]==0 && iRepl[1]==3 && iRepl[2]==7 ) {
6659 uniqueNodes[0] = curNodes[0];
6660 uniqueNodes[1] = curNodes[1];
6661 uniqueNodes[2] = curNodes[2];
6662 uniqueNodes[3] = curNodes[4];
6663 uniqueNodes[4] = curNodes[5];
6664 uniqueNodes[5] = curNodes[6];
6667 if( iRepl[0]==0 && iRepl[1]==4 && iRepl[2]==7 ) {
6668 uniqueNodes[0] = curNodes[1];
6669 uniqueNodes[1] = curNodes[2];
6670 uniqueNodes[2] = curNodes[3];
6671 uniqueNodes[3] = curNodes[5];
6672 uniqueNodes[4] = curNodes[6];
6673 uniqueNodes[5] = curNodes[0];
6676 if( iRepl[0]==1 && iRepl[1]==2 && iRepl[2]==5 ) {
6677 uniqueNodes[0] = curNodes[0];
6678 uniqueNodes[1] = curNodes[1];
6679 uniqueNodes[2] = curNodes[3];
6680 uniqueNodes[3] = curNodes[4];
6681 uniqueNodes[4] = curNodes[6];
6682 uniqueNodes[5] = curNodes[7];
6685 if( iRepl[0]==1 && iRepl[1]==4 && iRepl[2]==5 ) {
6686 uniqueNodes[0] = curNodes[0];
6687 uniqueNodes[1] = curNodes[2];
6688 uniqueNodes[2] = curNodes[3];
6689 uniqueNodes[3] = curNodes[1];
6690 uniqueNodes[4] = curNodes[6];
6691 uniqueNodes[5] = curNodes[7];
6694 if( iRepl[0]==2 && iRepl[1]==3 && iRepl[2]==6 ) {
6695 uniqueNodes[0] = curNodes[0];
6696 uniqueNodes[1] = curNodes[1];
6697 uniqueNodes[2] = curNodes[2];
6698 uniqueNodes[3] = curNodes[4];
6699 uniqueNodes[4] = curNodes[5];
6700 uniqueNodes[5] = curNodes[7];
6703 if( iRepl[0]==2 && iRepl[1]==5 && iRepl[2]==6 ) {
6704 uniqueNodes[0] = curNodes[0];
6705 uniqueNodes[1] = curNodes[1];
6706 uniqueNodes[2] = curNodes[3];
6707 uniqueNodes[3] = curNodes[4];
6708 uniqueNodes[4] = curNodes[2];
6709 uniqueNodes[5] = curNodes[7];
6712 if( iRepl[0]==3 && iRepl[1]==6 && iRepl[2]==7 ) {
6713 uniqueNodes[0] = curNodes[0];
6714 uniqueNodes[1] = curNodes[1];
6715 uniqueNodes[2] = curNodes[2];
6716 uniqueNodes[3] = curNodes[4];
6717 uniqueNodes[4] = curNodes[5];
6718 uniqueNodes[5] = curNodes[3];
6723 MESSAGE("nbRepl=4: " << iRepl[0] << " " << iRepl[1] << " " << iRepl[2] << " " << iRepl[3]);
6726 MESSAGE("nbRepl=5: " << iRepl[0] << " " << iRepl[1] << " " << iRepl[2] << " " << iRepl[3] << " " << iRepl[4]);
6730 //////////////////////////////////// HEXAHEDRON
6732 SMDS_VolumeTool hexa (elem);
6733 hexa.SetExternalNormal();
6734 if ( nbUniqueNodes == 4 && nbRepl == 4 ) {
6735 //////////////////////// HEX ---> 1 tetrahedron
6736 for ( int iFace = 0; iFace < 6; iFace++ ) {
6737 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
6738 if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
6739 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
6740 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
6741 // one face turns into a point ...
6742 int iOppFace = hexa.GetOppFaceIndex( iFace );
6743 ind = hexa.GetFaceNodesIndices( iOppFace );
6745 for ( iCur = 0; iCur < 4 && nbStick < 2; iCur++ ) {
6746 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
6749 if ( nbStick == 1 ) {
6750 // ... and the opposite one - into a triangle.
6752 ind = hexa.GetFaceNodesIndices( iFace );
6753 uniqueNodes[ 3 ] = curNodes[ind[ 0 ]];
6760 else if ( nbUniqueNodes == 6 && nbRepl == 2 ) {
6761 //////////////////////// HEX ---> 1 prism
6762 int nbTria = 0, iTria[3];
6763 const int *ind; // indices of face nodes
6764 // look for triangular faces
6765 for ( int iFace = 0; iFace < 6 && nbTria < 3; iFace++ ) {
6766 ind = hexa.GetFaceNodesIndices( iFace );
6767 TIDSortedNodeSet faceNodes;
6768 for ( iCur = 0; iCur < 4; iCur++ )
6769 faceNodes.insert( curNodes[ind[iCur]] );
6770 if ( faceNodes.size() == 3 )
6771 iTria[ nbTria++ ] = iFace;
6773 // check if triangles are opposite
6774 if ( nbTria == 2 && iTria[0] == hexa.GetOppFaceIndex( iTria[1] ))
6777 // set nodes of the bottom triangle
6778 ind = hexa.GetFaceNodesIndices( iTria[ 0 ]);
6780 for ( iCur = 0; iCur < 4; iCur++ )
6781 if ( ind[iCur] != iRepl[0] && ind[iCur] != iRepl[1])
6782 indB.push_back( ind[iCur] );
6783 if ( !hexa.IsForward() )
6784 std::swap( indB[0], indB[2] );
6785 for ( iCur = 0; iCur < 3; iCur++ )
6786 uniqueNodes[ iCur ] = curNodes[indB[iCur]];
6787 // set nodes of the top triangle
6788 const int *indT = hexa.GetFaceNodesIndices( iTria[ 1 ]);
6789 for ( iCur = 0; iCur < 3; ++iCur )
6790 for ( int j = 0; j < 4; ++j )
6791 if ( hexa.IsLinked( indB[ iCur ], indT[ j ] ))
6793 uniqueNodes[ iCur + 3 ] = curNodes[ indT[ j ]];
6799 else if (nbUniqueNodes == 5 && nbRepl == 4 ) {
6800 //////////////////// HEXAHEDRON ---> 2 tetrahedrons
6801 for ( int iFace = 0; iFace < 6; iFace++ ) {
6802 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
6803 if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
6804 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
6805 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
6806 // one face turns into a point ...
6807 int iOppFace = hexa.GetOppFaceIndex( iFace );
6808 ind = hexa.GetFaceNodesIndices( iOppFace );
6810 iUnique = 2; // reverse a tetrahedron 1 bottom
6811 for ( iCur = 0; iCur < 4 && nbStick == 0; iCur++ ) {
6812 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
6814 else if ( iUnique >= 0 )
6815 uniqueNodes[ iUnique-- ] = curNodes[ind[ iCur ]];
6817 if ( nbStick == 0 ) {
6818 // ... and the opposite one is a quadrangle
6820 const int* indTop = hexa.GetFaceNodesIndices( iFace );
6821 uniqueNodes[ 3 ] = curNodes[indTop[ 0 ]];
6824 SMDS_MeshElement* newElem =
6825 aMesh->AddVolume(curNodes[ind[ 0 ]],
6828 curNodes[indTop[ 0 ]]);
6829 myLastCreatedElems.Append(newElem);
6831 aMesh->SetMeshElementOnShape( newElem, aShapeId );
6838 else if ( nbUniqueNodes == 6 && nbRepl == 4 ) {
6839 ////////////////// HEXAHEDRON ---> 2 tetrahedrons or 1 prism
6840 // find indices of quad and tri faces
6841 int iQuadFace[ 6 ], iTriFace[ 6 ], nbQuad = 0, nbTri = 0, iFace;
6842 for ( iFace = 0; iFace < 6; iFace++ ) {
6843 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
6845 for ( iCur = 0; iCur < 4; iCur++ )
6846 nodeSet.insert( curNodes[ind[ iCur ]] );
6847 nbUniqueNodes = nodeSet.size();
6848 if ( nbUniqueNodes == 3 )
6849 iTriFace[ nbTri++ ] = iFace;
6850 else if ( nbUniqueNodes == 4 )
6851 iQuadFace[ nbQuad++ ] = iFace;
6853 if (nbQuad == 2 && nbTri == 4 &&
6854 hexa.GetOppFaceIndex( iQuadFace[ 0 ] ) == iQuadFace[ 1 ]) {
6855 // 2 opposite quadrangles stuck with a diagonal;
6856 // sample groups of merged indices: (0-4)(2-6)
6857 // --------------------------------------------> 2 tetrahedrons
6858 const int *ind1 = hexa.GetFaceNodesIndices( iQuadFace[ 0 ]); // indices of quad1 nodes
6859 const int *ind2 = hexa.GetFaceNodesIndices( iQuadFace[ 1 ]);
6860 int i0, i1d, i2, i3d, i0t, i2t; // d-daigonal, t-top
6861 if (curNodes[ind1[ 0 ]] == curNodes[ind2[ 0 ]] &&
6862 curNodes[ind1[ 2 ]] == curNodes[ind2[ 2 ]]) {
6863 // stuck with 0-2 diagonal
6871 else if (curNodes[ind1[ 1 ]] == curNodes[ind2[ 3 ]] &&
6872 curNodes[ind1[ 3 ]] == curNodes[ind2[ 1 ]]) {
6873 // stuck with 1-3 diagonal
6885 uniqueNodes[ 0 ] = curNodes [ i0 ];
6886 uniqueNodes[ 1 ] = curNodes [ i1d ];
6887 uniqueNodes[ 2 ] = curNodes [ i3d ];
6888 uniqueNodes[ 3 ] = curNodes [ i0t ];
6891 SMDS_MeshElement* newElem = aMesh->AddVolume(curNodes[ i1d ],
6895 myLastCreatedElems.Append(newElem);
6897 aMesh->SetMeshElementOnShape( newElem, aShapeId );
6900 else if (( nbTri == 2 && nbQuad == 3 ) || // merged (0-4)(1-5)
6901 ( nbTri == 4 && nbQuad == 2 )) { // merged (7-4)(1-5)
6902 // --------------------------------------------> prism
6903 // find 2 opposite triangles
6905 for ( iFace = 0; iFace + 1 < nbTri; iFace++ ) {
6906 if ( hexa.GetOppFaceIndex( iTriFace[ iFace ] ) == iTriFace[ iFace + 1 ]) {
6907 // find indices of kept and replaced nodes
6908 // and fill unique nodes of 2 opposite triangles
6909 const int *ind1 = hexa.GetFaceNodesIndices( iTriFace[ iFace ]);
6910 const int *ind2 = hexa.GetFaceNodesIndices( iTriFace[ iFace + 1 ]);
6911 const SMDS_MeshNode** hexanodes = hexa.GetNodes();
6912 // fill unique nodes
6915 for ( iCur = 0; iCur < 4 && isOk; iCur++ ) {
6916 const SMDS_MeshNode* n = curNodes[ind1[ iCur ]];
6917 const SMDS_MeshNode* nInit = hexanodes[ind1[ iCur ]];
6919 // iCur of a linked node of the opposite face (make normals co-directed):
6920 int iCurOpp = ( iCur == 1 || iCur == 3 ) ? 4 - iCur : iCur;
6921 // check that correspondent corners of triangles are linked
6922 if ( !hexa.IsLinked( ind1[ iCur ], ind2[ iCurOpp ] ))
6925 uniqueNodes[ iUnique ] = n;
6926 uniqueNodes[ iUnique + 3 ] = curNodes[ind2[ iCurOpp ]];
6935 } // if ( nbUniqueNodes == 6 && nbRepl == 4 )
6938 MESSAGE("MergeNodes() removes hexahedron "<< elem);
6945 } // switch ( nbNodes )
6947 } // if ( nbNodes != nbUniqueNodes ) // some nodes stick
6949 if ( isOk ) { // the elem remains valid after sticking nodes
6950 if (elem->IsPoly() && elem->GetType() == SMDSAbs_Volume)
6952 // Change nodes of polyedre
6953 const SMDS_VtkVolume* aPolyedre =
6954 dynamic_cast<const SMDS_VtkVolume*>( elem );
6956 int nbFaces = aPolyedre->NbFaces();
6958 vector<const SMDS_MeshNode *> poly_nodes;
6959 vector<int> quantities (nbFaces);
6961 for (int iface = 1; iface <= nbFaces; iface++) {
6962 int inode, nbFaceNodes = aPolyedre->NbFaceNodes(iface);
6963 quantities[iface - 1] = nbFaceNodes;
6965 for (inode = 1; inode <= nbFaceNodes; inode++) {
6966 const SMDS_MeshNode* curNode = aPolyedre->GetFaceNode(iface, inode);
6968 TNodeNodeMap::iterator nnIt = nodeNodeMap.find( curNode );
6969 if (nnIt != nodeNodeMap.end()) { // curNode sticks
6970 curNode = (*nnIt).second;
6972 poly_nodes.push_back(curNode);
6975 aMesh->ChangePolyhedronNodes( elem, poly_nodes, quantities );
6978 else // replace non-polyhedron elements
6980 const SMDSAbs_ElementType etyp = elem->GetType();
6981 const int elemId = elem->GetID();
6982 const bool isPoly = (elem->GetEntityType() == SMDSEntity_Polygon);
6983 uniqueNodes.resize(nbUniqueNodes);
6985 SMESHDS_SubMesh * sm = aShapeId > 0 ? aMesh->MeshElements(aShapeId) : 0;
6987 aMesh->RemoveFreeElement(elem, sm, /*fromGroups=*/false);
6988 SMDS_MeshElement* newElem = this->AddElement(uniqueNodes, etyp, isPoly, elemId);
6989 if ( sm && newElem )
6990 sm->AddElement( newElem );
6991 if ( elem != newElem )
6992 ReplaceElemInGroups( elem, newElem, aMesh );
6996 // Remove invalid regular element or invalid polygon
6997 rmElemIds.push_back( elem->GetID() );
7000 } // loop on elements
7002 // Remove bad elements, then equal nodes (order important)
7004 Remove( rmElemIds, false );
7005 Remove( rmNodeIds, true );
7010 // ========================================================
7011 // class : SortableElement
7012 // purpose : allow sorting elements basing on their nodes
7013 // ========================================================
7014 class SortableElement : public set <const SMDS_MeshElement*>
7018 SortableElement( const SMDS_MeshElement* theElem )
7021 SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator();
7022 while ( nodeIt->more() )
7023 this->insert( nodeIt->next() );
7026 const SMDS_MeshElement* Get() const
7029 void Set(const SMDS_MeshElement* e) const
7034 mutable const SMDS_MeshElement* myElem;
7037 //=======================================================================
7038 //function : FindEqualElements
7039 //purpose : Return list of group of elements built on the same nodes.
7040 // Search among theElements or in the whole mesh if theElements is empty
7041 //=======================================================================
7043 void SMESH_MeshEditor::FindEqualElements(TIDSortedElemSet & theElements,
7044 TListOfListOfElementsID & theGroupsOfElementsID)
7046 myLastCreatedElems.Clear();
7047 myLastCreatedNodes.Clear();
7049 typedef map< SortableElement, int > TMapOfNodeSet;
7050 typedef list<int> TGroupOfElems;
7052 if ( theElements.empty() )
7053 { // get all elements in the mesh
7054 SMDS_ElemIteratorPtr eIt = GetMeshDS()->elementsIterator();
7055 while ( eIt->more() )
7056 theElements.insert( theElements.end(), eIt->next());
7059 vector< TGroupOfElems > arrayOfGroups;
7060 TGroupOfElems groupOfElems;
7061 TMapOfNodeSet mapOfNodeSet;
7063 TIDSortedElemSet::iterator elemIt = theElements.begin();
7064 for ( int i = 0, j=0; elemIt != theElements.end(); ++elemIt, ++j ) {
7065 const SMDS_MeshElement* curElem = *elemIt;
7066 SortableElement SE(curElem);
7069 pair< TMapOfNodeSet::iterator, bool> pp = mapOfNodeSet.insert(make_pair(SE, i));
7070 if( !(pp.second) ) {
7071 TMapOfNodeSet::iterator& itSE = pp.first;
7072 ind = (*itSE).second;
7073 arrayOfGroups[ind].push_back(curElem->GetID());
7076 groupOfElems.clear();
7077 groupOfElems.push_back(curElem->GetID());
7078 arrayOfGroups.push_back(groupOfElems);
7083 vector< TGroupOfElems >::iterator groupIt = arrayOfGroups.begin();
7084 for ( ; groupIt != arrayOfGroups.end(); ++groupIt ) {
7085 groupOfElems = *groupIt;
7086 if ( groupOfElems.size() > 1 ) {
7087 groupOfElems.sort();
7088 theGroupsOfElementsID.push_back(groupOfElems);
7093 //=======================================================================
7094 //function : MergeElements
7095 //purpose : In each given group, substitute all elements by the first one.
7096 //=======================================================================
7098 void SMESH_MeshEditor::MergeElements(TListOfListOfElementsID & theGroupsOfElementsID)
7100 myLastCreatedElems.Clear();
7101 myLastCreatedNodes.Clear();
7103 typedef list<int> TListOfIDs;
7104 TListOfIDs rmElemIds; // IDs of elems to remove
7106 SMESHDS_Mesh* aMesh = GetMeshDS();
7108 TListOfListOfElementsID::iterator groupsIt = theGroupsOfElementsID.begin();
7109 while ( groupsIt != theGroupsOfElementsID.end() ) {
7110 TListOfIDs& aGroupOfElemID = *groupsIt;
7111 aGroupOfElemID.sort();
7112 int elemIDToKeep = aGroupOfElemID.front();
7113 const SMDS_MeshElement* elemToKeep = aMesh->FindElement(elemIDToKeep);
7114 aGroupOfElemID.pop_front();
7115 TListOfIDs::iterator idIt = aGroupOfElemID.begin();
7116 while ( idIt != aGroupOfElemID.end() ) {
7117 int elemIDToRemove = *idIt;
7118 const SMDS_MeshElement* elemToRemove = aMesh->FindElement(elemIDToRemove);
7119 // add the kept element in groups of removed one (PAL15188)
7120 AddToSameGroups( elemToKeep, elemToRemove, aMesh );
7121 rmElemIds.push_back( elemIDToRemove );
7127 Remove( rmElemIds, false );
7130 //=======================================================================
7131 //function : MergeEqualElements
7132 //purpose : Remove all but one of elements built on the same nodes.
7133 //=======================================================================
7135 void SMESH_MeshEditor::MergeEqualElements()
7137 TIDSortedElemSet aMeshElements; /* empty input ==
7138 to merge equal elements in the whole mesh */
7139 TListOfListOfElementsID aGroupsOfElementsID;
7140 FindEqualElements(aMeshElements, aGroupsOfElementsID);
7141 MergeElements(aGroupsOfElementsID);
7144 //=======================================================================
7145 //function : findAdjacentFace
7147 //=======================================================================
7149 static const SMDS_MeshElement* findAdjacentFace(const SMDS_MeshNode* n1,
7150 const SMDS_MeshNode* n2,
7151 const SMDS_MeshElement* elem)
7153 TIDSortedElemSet elemSet, avoidSet;
7155 avoidSet.insert ( elem );
7156 return SMESH_MeshAlgos::FindFaceInSet( n1, n2, elemSet, avoidSet );
7159 //=======================================================================
7160 //function : FindFreeBorder
7162 //=======================================================================
7164 #define ControlFreeBorder SMESH::Controls::FreeEdges::IsFreeEdge
7166 bool SMESH_MeshEditor::FindFreeBorder (const SMDS_MeshNode* theFirstNode,
7167 const SMDS_MeshNode* theSecondNode,
7168 const SMDS_MeshNode* theLastNode,
7169 list< const SMDS_MeshNode* > & theNodes,
7170 list< const SMDS_MeshElement* >& theFaces)
7172 if ( !theFirstNode || !theSecondNode )
7174 // find border face between theFirstNode and theSecondNode
7175 const SMDS_MeshElement* curElem = findAdjacentFace( theFirstNode, theSecondNode, 0 );
7179 theFaces.push_back( curElem );
7180 theNodes.push_back( theFirstNode );
7181 theNodes.push_back( theSecondNode );
7183 //vector<const SMDS_MeshNode*> nodes;
7184 const SMDS_MeshNode *nIgnore = theFirstNode, *nStart = theSecondNode;
7185 TIDSortedElemSet foundElems;
7186 bool needTheLast = ( theLastNode != 0 );
7188 while ( nStart != theLastNode ) {
7189 if ( nStart == theFirstNode )
7190 return !needTheLast;
7192 // find all free border faces sharing form nStart
7194 list< const SMDS_MeshElement* > curElemList;
7195 list< const SMDS_MeshNode* > nStartList;
7196 SMDS_ElemIteratorPtr invElemIt = nStart->GetInverseElementIterator(SMDSAbs_Face);
7197 while ( invElemIt->more() ) {
7198 const SMDS_MeshElement* e = invElemIt->next();
7199 if ( e == curElem || foundElems.insert( e ).second ) {
7201 int iNode = 0, nbNodes = e->NbNodes();
7202 //const SMDS_MeshNode* nodes[nbNodes+1];
7203 vector<const SMDS_MeshNode*> nodes(nbNodes+1);
7205 if(e->IsQuadratic()) {
7206 const SMDS_VtkFace* F =
7207 dynamic_cast<const SMDS_VtkFace*>(e);
7208 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
7209 // use special nodes iterator
7210 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
7211 while( anIter->more() ) {
7212 nodes[ iNode++ ] = cast2Node(anIter->next());
7216 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
7217 while ( nIt->more() )
7218 nodes[ iNode++ ] = static_cast<const SMDS_MeshNode*>( nIt->next() );
7220 nodes[ iNode ] = nodes[ 0 ];
7222 for ( iNode = 0; iNode < nbNodes; iNode++ )
7223 if (((nodes[ iNode ] == nStart && nodes[ iNode + 1] != nIgnore ) ||
7224 (nodes[ iNode + 1] == nStart && nodes[ iNode ] != nIgnore )) &&
7225 ControlFreeBorder( &nodes[ iNode ], e->GetID() ))
7227 nStartList.push_back( nodes[ iNode + ( nodes[ iNode ] == nStart ? 1 : 0 )]);
7228 curElemList.push_back( e );
7232 // analyse the found
7234 int nbNewBorders = curElemList.size();
7235 if ( nbNewBorders == 0 ) {
7236 // no free border furthermore
7237 return !needTheLast;
7239 else if ( nbNewBorders == 1 ) {
7240 // one more element found
7242 nStart = nStartList.front();
7243 curElem = curElemList.front();
7244 theFaces.push_back( curElem );
7245 theNodes.push_back( nStart );
7248 // several continuations found
7249 list< const SMDS_MeshElement* >::iterator curElemIt;
7250 list< const SMDS_MeshNode* >::iterator nStartIt;
7251 // check if one of them reached the last node
7252 if ( needTheLast ) {
7253 for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
7254 curElemIt!= curElemList.end();
7255 curElemIt++, nStartIt++ )
7256 if ( *nStartIt == theLastNode ) {
7257 theFaces.push_back( *curElemIt );
7258 theNodes.push_back( *nStartIt );
7262 // find the best free border by the continuations
7263 list<const SMDS_MeshNode*> contNodes[ 2 ], *cNL;
7264 list<const SMDS_MeshElement*> contFaces[ 2 ], *cFL;
7265 for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
7266 curElemIt!= curElemList.end();
7267 curElemIt++, nStartIt++ )
7269 cNL = & contNodes[ contNodes[0].empty() ? 0 : 1 ];
7270 cFL = & contFaces[ contFaces[0].empty() ? 0 : 1 ];
7271 // find one more free border
7272 if ( ! SMESH_MeshEditor::FindFreeBorder( nStart, *nStartIt, theLastNode, *cNL, *cFL )) {
7276 else if ( !contNodes[0].empty() && !contNodes[1].empty() ) {
7277 // choice: clear a worse one
7278 int iLongest = ( contNodes[0].size() < contNodes[1].size() ? 1 : 0 );
7279 int iWorse = ( needTheLast ? 1 - iLongest : iLongest );
7280 contNodes[ iWorse ].clear();
7281 contFaces[ iWorse ].clear();
7284 if ( contNodes[0].empty() && contNodes[1].empty() )
7287 // append the best free border
7288 cNL = & contNodes[ contNodes[0].empty() ? 1 : 0 ];
7289 cFL = & contFaces[ contFaces[0].empty() ? 1 : 0 ];
7290 theNodes.pop_back(); // remove nIgnore
7291 theNodes.pop_back(); // remove nStart
7292 theFaces.pop_back(); // remove curElem
7293 list< const SMDS_MeshNode* >::iterator nIt = cNL->begin();
7294 list< const SMDS_MeshElement* >::iterator fIt = cFL->begin();
7295 for ( ; nIt != cNL->end(); nIt++ ) theNodes.push_back( *nIt );
7296 for ( ; fIt != cFL->end(); fIt++ ) theFaces.push_back( *fIt );
7299 } // several continuations found
7300 } // while ( nStart != theLastNode )
7305 //=======================================================================
7306 //function : CheckFreeBorderNodes
7307 //purpose : Return true if the tree nodes are on a free border
7308 //=======================================================================
7310 bool SMESH_MeshEditor::CheckFreeBorderNodes(const SMDS_MeshNode* theNode1,
7311 const SMDS_MeshNode* theNode2,
7312 const SMDS_MeshNode* theNode3)
7314 list< const SMDS_MeshNode* > nodes;
7315 list< const SMDS_MeshElement* > faces;
7316 return FindFreeBorder( theNode1, theNode2, theNode3, nodes, faces);
7319 //=======================================================================
7320 //function : SewFreeBorder
7322 //=======================================================================
7324 SMESH_MeshEditor::Sew_Error
7325 SMESH_MeshEditor::SewFreeBorder (const SMDS_MeshNode* theBordFirstNode,
7326 const SMDS_MeshNode* theBordSecondNode,
7327 const SMDS_MeshNode* theBordLastNode,
7328 const SMDS_MeshNode* theSideFirstNode,
7329 const SMDS_MeshNode* theSideSecondNode,
7330 const SMDS_MeshNode* theSideThirdNode,
7331 const bool theSideIsFreeBorder,
7332 const bool toCreatePolygons,
7333 const bool toCreatePolyedrs)
7335 myLastCreatedElems.Clear();
7336 myLastCreatedNodes.Clear();
7338 MESSAGE("::SewFreeBorder()");
7339 Sew_Error aResult = SEW_OK;
7341 // ====================================
7342 // find side nodes and elements
7343 // ====================================
7345 list< const SMDS_MeshNode* > nSide[ 2 ];
7346 list< const SMDS_MeshElement* > eSide[ 2 ];
7347 list< const SMDS_MeshNode* >::iterator nIt[ 2 ];
7348 list< const SMDS_MeshElement* >::iterator eIt[ 2 ];
7352 if (!FindFreeBorder(theBordFirstNode,theBordSecondNode,theBordLastNode,
7353 nSide[0], eSide[0])) {
7354 MESSAGE(" Free Border 1 not found " );
7355 aResult = SEW_BORDER1_NOT_FOUND;
7357 if (theSideIsFreeBorder) {
7360 if (!FindFreeBorder(theSideFirstNode, theSideSecondNode, theSideThirdNode,
7361 nSide[1], eSide[1])) {
7362 MESSAGE(" Free Border 2 not found " );
7363 aResult = ( aResult != SEW_OK ? SEW_BOTH_BORDERS_NOT_FOUND : SEW_BORDER2_NOT_FOUND );
7366 if ( aResult != SEW_OK )
7369 if (!theSideIsFreeBorder) {
7373 // -------------------------------------------------------------------------
7375 // 1. If nodes to merge are not coincident, move nodes of the free border
7376 // from the coord sys defined by the direction from the first to last
7377 // nodes of the border to the correspondent sys of the side 2
7378 // 2. On the side 2, find the links most co-directed with the correspondent
7379 // links of the free border
7380 // -------------------------------------------------------------------------
7382 // 1. Since sewing may break if there are volumes to split on the side 2,
7383 // we wont move nodes but just compute new coordinates for them
7384 typedef map<const SMDS_MeshNode*, gp_XYZ> TNodeXYZMap;
7385 TNodeXYZMap nBordXYZ;
7386 list< const SMDS_MeshNode* >& bordNodes = nSide[ 0 ];
7387 list< const SMDS_MeshNode* >::iterator nBordIt;
7389 gp_XYZ Pb1( theBordFirstNode->X(), theBordFirstNode->Y(), theBordFirstNode->Z() );
7390 gp_XYZ Pb2( theBordLastNode->X(), theBordLastNode->Y(), theBordLastNode->Z() );
7391 gp_XYZ Ps1( theSideFirstNode->X(), theSideFirstNode->Y(), theSideFirstNode->Z() );
7392 gp_XYZ Ps2( theSideSecondNode->X(), theSideSecondNode->Y(), theSideSecondNode->Z() );
7393 double tol2 = 1.e-8;
7394 gp_Vec Vbs1( Pb1 - Ps1 ),Vbs2( Pb2 - Ps2 );
7395 if ( Vbs1.SquareMagnitude() > tol2 || Vbs2.SquareMagnitude() > tol2 ) {
7396 // Need node movement.
7398 // find X and Z axes to create trsf
7399 gp_Vec Zb( Pb1 - Pb2 ), Zs( Ps1 - Ps2 );
7401 if ( X.SquareMagnitude() <= gp::Resolution() * gp::Resolution() )
7403 X = gp_Ax2( gp::Origin(), Zb ).XDirection();
7406 gp_Ax3 toBordAx( Pb1, Zb, X );
7407 gp_Ax3 fromSideAx( Ps1, Zs, X );
7408 gp_Ax3 toGlobalAx( gp::Origin(), gp::DZ(), gp::DX() );
7410 gp_Trsf toBordSys, fromSide2Sys;
7411 toBordSys.SetTransformation( toBordAx );
7412 fromSide2Sys.SetTransformation( fromSideAx, toGlobalAx );
7413 fromSide2Sys.SetScaleFactor( Zs.Magnitude() / Zb.Magnitude() );
7416 for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
7417 const SMDS_MeshNode* n = *nBordIt;
7418 gp_XYZ xyz( n->X(),n->Y(),n->Z() );
7419 toBordSys.Transforms( xyz );
7420 fromSide2Sys.Transforms( xyz );
7421 nBordXYZ.insert( TNodeXYZMap::value_type( n, xyz ));
7425 // just insert nodes XYZ in the nBordXYZ map
7426 for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
7427 const SMDS_MeshNode* n = *nBordIt;
7428 nBordXYZ.insert( TNodeXYZMap::value_type( n, gp_XYZ( n->X(),n->Y(),n->Z() )));
7432 // 2. On the side 2, find the links most co-directed with the correspondent
7433 // links of the free border
7435 list< const SMDS_MeshElement* >& sideElems = eSide[ 1 ];
7436 list< const SMDS_MeshNode* >& sideNodes = nSide[ 1 ];
7437 sideNodes.push_back( theSideFirstNode );
7439 bool hasVolumes = false;
7440 LinkID_Gen aLinkID_Gen( GetMeshDS() );
7441 set<long> foundSideLinkIDs, checkedLinkIDs;
7442 SMDS_VolumeTool volume;
7443 //const SMDS_MeshNode* faceNodes[ 4 ];
7445 const SMDS_MeshNode* sideNode;
7446 const SMDS_MeshElement* sideElem;
7447 const SMDS_MeshNode* prevSideNode = theSideFirstNode;
7448 const SMDS_MeshNode* prevBordNode = theBordFirstNode;
7449 nBordIt = bordNodes.begin();
7451 // border node position and border link direction to compare with
7452 gp_XYZ bordPos = nBordXYZ[ *nBordIt ];
7453 gp_XYZ bordDir = bordPos - nBordXYZ[ prevBordNode ];
7454 // choose next side node by link direction or by closeness to
7455 // the current border node:
7456 bool searchByDir = ( *nBordIt != theBordLastNode );
7458 // find the next node on the Side 2
7460 double maxDot = -DBL_MAX, minDist = DBL_MAX;
7462 checkedLinkIDs.clear();
7463 gp_XYZ prevXYZ( prevSideNode->X(), prevSideNode->Y(), prevSideNode->Z() );
7465 // loop on inverse elements of current node (prevSideNode) on the Side 2
7466 SMDS_ElemIteratorPtr invElemIt = prevSideNode->GetInverseElementIterator();
7467 while ( invElemIt->more() )
7469 const SMDS_MeshElement* elem = invElemIt->next();
7470 // prepare data for a loop on links coming to prevSideNode, of a face or a volume
7471 int iPrevNode, iNode = 0, nbNodes = elem->NbNodes();
7472 vector< const SMDS_MeshNode* > faceNodes( nbNodes, (const SMDS_MeshNode*)0 );
7473 bool isVolume = volume.Set( elem );
7474 const SMDS_MeshNode** nodes = isVolume ? volume.GetNodes() : & faceNodes[0];
7475 if ( isVolume ) // --volume
7477 else if ( elem->GetType()==SMDSAbs_Face ) { // --face
7478 // retrieve all face nodes and find iPrevNode - an index of the prevSideNode
7479 if(elem->IsQuadratic()) {
7480 const SMDS_VtkFace* F =
7481 dynamic_cast<const SMDS_VtkFace*>(elem);
7482 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
7483 // use special nodes iterator
7484 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
7485 while( anIter->more() ) {
7486 nodes[ iNode ] = cast2Node(anIter->next());
7487 if ( nodes[ iNode++ ] == prevSideNode )
7488 iPrevNode = iNode - 1;
7492 SMDS_ElemIteratorPtr nIt = elem->nodesIterator();
7493 while ( nIt->more() ) {
7494 nodes[ iNode ] = cast2Node( nIt->next() );
7495 if ( nodes[ iNode++ ] == prevSideNode )
7496 iPrevNode = iNode - 1;
7499 // there are 2 links to check
7504 // loop on links, to be precise, on the second node of links
7505 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
7506 const SMDS_MeshNode* n = nodes[ iNode ];
7508 if ( !volume.IsLinked( n, prevSideNode ))
7512 if ( iNode ) // a node before prevSideNode
7513 n = nodes[ iPrevNode == 0 ? elem->NbNodes() - 1 : iPrevNode - 1 ];
7514 else // a node after prevSideNode
7515 n = nodes[ iPrevNode + 1 == elem->NbNodes() ? 0 : iPrevNode + 1 ];
7517 // check if this link was already used
7518 long iLink = aLinkID_Gen.GetLinkID( prevSideNode, n );
7519 bool isJustChecked = !checkedLinkIDs.insert( iLink ).second;
7520 if (!isJustChecked &&
7521 foundSideLinkIDs.find( iLink ) == foundSideLinkIDs.end() )
7523 // test a link geometrically
7524 gp_XYZ nextXYZ ( n->X(), n->Y(), n->Z() );
7525 bool linkIsBetter = false;
7526 double dot = 0.0, dist = 0.0;
7527 if ( searchByDir ) { // choose most co-directed link
7528 dot = bordDir * ( nextXYZ - prevXYZ ).Normalized();
7529 linkIsBetter = ( dot > maxDot );
7531 else { // choose link with the node closest to bordPos
7532 dist = ( nextXYZ - bordPos ).SquareModulus();
7533 linkIsBetter = ( dist < minDist );
7535 if ( linkIsBetter ) {
7544 } // loop on inverse elements of prevSideNode
7547 MESSAGE(" Cant find path by links of the Side 2 ");
7548 return SEW_BAD_SIDE_NODES;
7550 sideNodes.push_back( sideNode );
7551 sideElems.push_back( sideElem );
7552 foundSideLinkIDs.insert ( linkID );
7553 prevSideNode = sideNode;
7555 if ( *nBordIt == theBordLastNode )
7556 searchByDir = false;
7558 // find the next border link to compare with
7559 gp_XYZ sidePos( sideNode->X(), sideNode->Y(), sideNode->Z() );
7560 searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
7561 // move to next border node if sideNode is before forward border node (bordPos)
7562 while ( *nBordIt != theBordLastNode && !searchByDir ) {
7563 prevBordNode = *nBordIt;
7565 bordPos = nBordXYZ[ *nBordIt ];
7566 bordDir = bordPos - nBordXYZ[ prevBordNode ];
7567 searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
7571 while ( sideNode != theSideSecondNode );
7573 if ( hasVolumes && sideNodes.size () != bordNodes.size() && !toCreatePolyedrs) {
7574 MESSAGE("VOLUME SPLITTING IS FORBIDDEN");
7575 return SEW_VOLUMES_TO_SPLIT; // volume splitting is forbidden
7577 } // end nodes search on the side 2
7579 // ============================
7580 // sew the border to the side 2
7581 // ============================
7583 int nbNodes[] = { nSide[0].size(), nSide[1].size() };
7584 int maxNbNodes = Max( nbNodes[0], nbNodes[1] );
7586 TListOfListOfNodes nodeGroupsToMerge;
7587 if ( nbNodes[0] == nbNodes[1] ||
7588 ( theSideIsFreeBorder && !theSideThirdNode)) {
7590 // all nodes are to be merged
7592 for (nIt[0] = nSide[0].begin(), nIt[1] = nSide[1].begin();
7593 nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end();
7594 nIt[0]++, nIt[1]++ )
7596 nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
7597 nodeGroupsToMerge.back().push_back( *nIt[1] ); // to keep
7598 nodeGroupsToMerge.back().push_back( *nIt[0] ); // to remove
7603 // insert new nodes into the border and the side to get equal nb of segments
7605 // get normalized parameters of nodes on the borders
7606 //double param[ 2 ][ maxNbNodes ];
7608 param[0] = new double [ maxNbNodes ];
7609 param[1] = new double [ maxNbNodes ];
7611 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
7612 list< const SMDS_MeshNode* >& nodes = nSide[ iBord ];
7613 list< const SMDS_MeshNode* >::iterator nIt = nodes.begin();
7614 const SMDS_MeshNode* nPrev = *nIt;
7615 double bordLength = 0;
7616 for ( iNode = 0; nIt != nodes.end(); nIt++, iNode++ ) { // loop on border nodes
7617 const SMDS_MeshNode* nCur = *nIt;
7618 gp_XYZ segment (nCur->X() - nPrev->X(),
7619 nCur->Y() - nPrev->Y(),
7620 nCur->Z() - nPrev->Z());
7621 double segmentLen = segment.Modulus();
7622 bordLength += segmentLen;
7623 param[ iBord ][ iNode ] = bordLength;
7626 // normalize within [0,1]
7627 for ( iNode = 0; iNode < nbNodes[ iBord ]; iNode++ ) {
7628 param[ iBord ][ iNode ] /= bordLength;
7632 // loop on border segments
7633 const SMDS_MeshNode *nPrev[ 2 ] = { 0, 0 };
7634 int i[ 2 ] = { 0, 0 };
7635 nIt[0] = nSide[0].begin(); eIt[0] = eSide[0].begin();
7636 nIt[1] = nSide[1].begin(); eIt[1] = eSide[1].begin();
7638 TElemOfNodeListMap insertMap;
7639 TElemOfNodeListMap::iterator insertMapIt;
7641 // key: elem to insert nodes into
7642 // value: 2 nodes to insert between + nodes to be inserted
7644 bool next[ 2 ] = { false, false };
7646 // find min adjacent segment length after sewing
7647 double nextParam = 10., prevParam = 0;
7648 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
7649 if ( i[ iBord ] + 1 < nbNodes[ iBord ])
7650 nextParam = Min( nextParam, param[iBord][ i[iBord] + 1 ]);
7651 if ( i[ iBord ] > 0 )
7652 prevParam = Max( prevParam, param[iBord][ i[iBord] - 1 ]);
7654 double minParam = Min( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
7655 double maxParam = Max( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
7656 double minSegLen = Min( nextParam - minParam, maxParam - prevParam );
7658 // choose to insert or to merge nodes
7659 double du = param[ 1 ][ i[1] ] - param[ 0 ][ i[0] ];
7660 if ( Abs( du ) <= minSegLen * 0.2 ) {
7663 nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
7664 const SMDS_MeshNode* n0 = *nIt[0];
7665 const SMDS_MeshNode* n1 = *nIt[1];
7666 nodeGroupsToMerge.back().push_back( n1 );
7667 nodeGroupsToMerge.back().push_back( n0 );
7668 // position of node of the border changes due to merge
7669 param[ 0 ][ i[0] ] += du;
7670 // move n1 for the sake of elem shape evaluation during insertion.
7671 // n1 will be removed by MergeNodes() anyway
7672 const_cast<SMDS_MeshNode*>( n0 )->setXYZ( n1->X(), n1->Y(), n1->Z() );
7673 next[0] = next[1] = true;
7678 int intoBord = ( du < 0 ) ? 0 : 1;
7679 const SMDS_MeshElement* elem = *eIt[ intoBord ];
7680 const SMDS_MeshNode* n1 = nPrev[ intoBord ];
7681 const SMDS_MeshNode* n2 = *nIt[ intoBord ];
7682 const SMDS_MeshNode* nIns = *nIt[ 1 - intoBord ];
7683 if ( intoBord == 1 ) {
7684 // move node of the border to be on a link of elem of the side
7685 gp_XYZ p1 (n1->X(), n1->Y(), n1->Z());
7686 gp_XYZ p2 (n2->X(), n2->Y(), n2->Z());
7687 double ratio = du / ( param[ 1 ][ i[1] ] - param[ 1 ][ i[1]-1 ]);
7688 gp_XYZ p = p2 * ( 1 - ratio ) + p1 * ratio;
7689 GetMeshDS()->MoveNode( nIns, p.X(), p.Y(), p.Z() );
7691 insertMapIt = insertMap.find( elem );
7692 bool notFound = ( insertMapIt == insertMap.end() );
7693 bool otherLink = ( !notFound && (*insertMapIt).second.front() != n1 );
7695 // insert into another link of the same element:
7696 // 1. perform insertion into the other link of the elem
7697 list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
7698 const SMDS_MeshNode* n12 = nodeList.front(); nodeList.pop_front();
7699 const SMDS_MeshNode* n22 = nodeList.front(); nodeList.pop_front();
7700 InsertNodesIntoLink( elem, n12, n22, nodeList, toCreatePolygons );
7701 // 2. perform insertion into the link of adjacent faces
7703 const SMDS_MeshElement* adjElem = findAdjacentFace( n12, n22, elem );
7705 InsertNodesIntoLink( adjElem, n12, n22, nodeList, toCreatePolygons );
7709 if (toCreatePolyedrs) {
7710 // perform insertion into the links of adjacent volumes
7711 UpdateVolumes(n12, n22, nodeList);
7713 // 3. find an element appeared on n1 and n2 after the insertion
7714 insertMap.erase( elem );
7715 elem = findAdjacentFace( n1, n2, 0 );
7717 if ( notFound || otherLink ) {
7718 // add element and nodes of the side into the insertMap
7719 insertMapIt = insertMap.insert
7720 ( TElemOfNodeListMap::value_type( elem, list<const SMDS_MeshNode*>() )).first;
7721 (*insertMapIt).second.push_back( n1 );
7722 (*insertMapIt).second.push_back( n2 );
7724 // add node to be inserted into elem
7725 (*insertMapIt).second.push_back( nIns );
7726 next[ 1 - intoBord ] = true;
7729 // go to the next segment
7730 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
7731 if ( next[ iBord ] ) {
7732 if ( i[ iBord ] != 0 && eIt[ iBord ] != eSide[ iBord ].end())
7734 nPrev[ iBord ] = *nIt[ iBord ];
7735 nIt[ iBord ]++; i[ iBord ]++;
7739 while ( nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end());
7741 // perform insertion of nodes into elements
7743 for (insertMapIt = insertMap.begin();
7744 insertMapIt != insertMap.end();
7747 const SMDS_MeshElement* elem = (*insertMapIt).first;
7748 list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
7749 const SMDS_MeshNode* n1 = nodeList.front(); nodeList.pop_front();
7750 const SMDS_MeshNode* n2 = nodeList.front(); nodeList.pop_front();
7752 InsertNodesIntoLink( elem, n1, n2, nodeList, toCreatePolygons );
7754 if ( !theSideIsFreeBorder ) {
7755 // look for and insert nodes into the faces adjacent to elem
7757 const SMDS_MeshElement* adjElem = findAdjacentFace( n1, n2, elem );
7759 InsertNodesIntoLink( adjElem, n1, n2, nodeList, toCreatePolygons );
7764 if (toCreatePolyedrs) {
7765 // perform insertion into the links of adjacent volumes
7766 UpdateVolumes(n1, n2, nodeList);
7772 } // end: insert new nodes
7774 MergeNodes ( nodeGroupsToMerge );
7779 //=======================================================================
7780 //function : InsertNodesIntoLink
7781 //purpose : insert theNodesToInsert into theFace between theBetweenNode1
7782 // and theBetweenNode2 and split theElement
7783 //=======================================================================
7785 void SMESH_MeshEditor::InsertNodesIntoLink(const SMDS_MeshElement* theFace,
7786 const SMDS_MeshNode* theBetweenNode1,
7787 const SMDS_MeshNode* theBetweenNode2,
7788 list<const SMDS_MeshNode*>& theNodesToInsert,
7789 const bool toCreatePoly)
7791 if ( theFace->GetType() != SMDSAbs_Face ) return;
7793 // find indices of 2 link nodes and of the rest nodes
7794 int iNode = 0, il1, il2, i3, i4;
7795 il1 = il2 = i3 = i4 = -1;
7796 //const SMDS_MeshNode* nodes[ theFace->NbNodes() ];
7797 vector<const SMDS_MeshNode*> nodes( theFace->NbNodes() );
7799 if(theFace->IsQuadratic()) {
7800 const SMDS_VtkFace* F =
7801 dynamic_cast<const SMDS_VtkFace*>(theFace);
7802 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
7803 // use special nodes iterator
7804 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
7805 while( anIter->more() ) {
7806 const SMDS_MeshNode* n = cast2Node(anIter->next());
7807 if ( n == theBetweenNode1 )
7809 else if ( n == theBetweenNode2 )
7815 nodes[ iNode++ ] = n;
7819 SMDS_ElemIteratorPtr nodeIt = theFace->nodesIterator();
7820 while ( nodeIt->more() ) {
7821 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
7822 if ( n == theBetweenNode1 )
7824 else if ( n == theBetweenNode2 )
7830 nodes[ iNode++ ] = n;
7833 if ( il1 < 0 || il2 < 0 || i3 < 0 )
7836 // arrange link nodes to go one after another regarding the face orientation
7837 bool reverse = ( Abs( il2 - il1 ) == 1 ? il2 < il1 : il1 < il2 );
7838 list<const SMDS_MeshNode *> aNodesToInsert = theNodesToInsert;
7843 aNodesToInsert.reverse();
7845 // check that not link nodes of a quadrangles are in good order
7846 int nbFaceNodes = theFace->NbNodes();
7847 if ( nbFaceNodes == 4 && i4 - i3 != 1 ) {
7853 if (toCreatePoly || theFace->IsPoly()) {
7856 vector<const SMDS_MeshNode *> poly_nodes (nbFaceNodes + aNodesToInsert.size());
7858 // add nodes of face up to first node of link
7861 if(theFace->IsQuadratic()) {
7862 const SMDS_VtkFace* F =
7863 dynamic_cast<const SMDS_VtkFace*>(theFace);
7864 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
7865 // use special nodes iterator
7866 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
7867 while( anIter->more() && !isFLN ) {
7868 const SMDS_MeshNode* n = cast2Node(anIter->next());
7869 poly_nodes[iNode++] = n;
7870 if (n == nodes[il1]) {
7874 // add nodes to insert
7875 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
7876 for (; nIt != aNodesToInsert.end(); nIt++) {
7877 poly_nodes[iNode++] = *nIt;
7879 // add nodes of face starting from last node of link
7880 while ( anIter->more() ) {
7881 poly_nodes[iNode++] = cast2Node(anIter->next());
7885 SMDS_ElemIteratorPtr nodeIt = theFace->nodesIterator();
7886 while ( nodeIt->more() && !isFLN ) {
7887 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
7888 poly_nodes[iNode++] = n;
7889 if (n == nodes[il1]) {
7893 // add nodes to insert
7894 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
7895 for (; nIt != aNodesToInsert.end(); nIt++) {
7896 poly_nodes[iNode++] = *nIt;
7898 // add nodes of face starting from last node of link
7899 while ( nodeIt->more() ) {
7900 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
7901 poly_nodes[iNode++] = n;
7905 // edit or replace the face
7906 SMESHDS_Mesh *aMesh = GetMeshDS();
7908 if (theFace->IsPoly()) {
7909 aMesh->ChangePolygonNodes(theFace, poly_nodes);
7912 int aShapeId = FindShape( theFace );
7914 SMDS_MeshElement* newElem = aMesh->AddPolygonalFace(poly_nodes);
7915 myLastCreatedElems.Append(newElem);
7916 if ( aShapeId && newElem )
7917 aMesh->SetMeshElementOnShape( newElem, aShapeId );
7919 aMesh->RemoveElement(theFace);
7924 SMESHDS_Mesh *aMesh = GetMeshDS();
7925 if( !theFace->IsQuadratic() ) {
7927 // put aNodesToInsert between theBetweenNode1 and theBetweenNode2
7928 int nbLinkNodes = 2 + aNodesToInsert.size();
7929 //const SMDS_MeshNode* linkNodes[ nbLinkNodes ];
7930 vector<const SMDS_MeshNode*> linkNodes( nbLinkNodes );
7931 linkNodes[ 0 ] = nodes[ il1 ];
7932 linkNodes[ nbLinkNodes - 1 ] = nodes[ il2 ];
7933 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
7934 for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
7935 linkNodes[ iNode++ ] = *nIt;
7937 // decide how to split a quadrangle: compare possible variants
7938 // and choose which of splits to be a quadrangle
7939 int i1, i2, iSplit, nbSplits = nbLinkNodes - 1, iBestQuad;
7940 if ( nbFaceNodes == 3 ) {
7941 iBestQuad = nbSplits;
7944 else if ( nbFaceNodes == 4 ) {
7945 SMESH::Controls::NumericalFunctorPtr aCrit( new SMESH::Controls::AspectRatio);
7946 double aBestRate = DBL_MAX;
7947 for ( int iQuad = 0; iQuad < nbSplits; iQuad++ ) {
7949 double aBadRate = 0;
7950 // evaluate elements quality
7951 for ( iSplit = 0; iSplit < nbSplits; iSplit++ ) {
7952 if ( iSplit == iQuad ) {
7953 SMDS_FaceOfNodes quad (linkNodes[ i1++ ],
7957 aBadRate += getBadRate( &quad, aCrit );
7960 SMDS_FaceOfNodes tria (linkNodes[ i1++ ],
7962 nodes[ iSplit < iQuad ? i4 : i3 ]);
7963 aBadRate += getBadRate( &tria, aCrit );
7967 if ( aBadRate < aBestRate ) {
7969 aBestRate = aBadRate;
7974 // create new elements
7975 int aShapeId = FindShape( theFace );
7978 for ( iSplit = 0; iSplit < nbSplits - 1; iSplit++ ) {
7979 SMDS_MeshElement* newElem = 0;
7980 if ( iSplit == iBestQuad )
7981 newElem = aMesh->AddFace (linkNodes[ i1++ ],
7986 newElem = aMesh->AddFace (linkNodes[ i1++ ],
7988 nodes[ iSplit < iBestQuad ? i4 : i3 ]);
7989 myLastCreatedElems.Append(newElem);
7990 if ( aShapeId && newElem )
7991 aMesh->SetMeshElementOnShape( newElem, aShapeId );
7994 // change nodes of theFace
7995 const SMDS_MeshNode* newNodes[ 4 ];
7996 newNodes[ 0 ] = linkNodes[ i1 ];
7997 newNodes[ 1 ] = linkNodes[ i2 ];
7998 newNodes[ 2 ] = nodes[ iSplit >= iBestQuad ? i3 : i4 ];
7999 newNodes[ 3 ] = nodes[ i4 ];
8000 //aMesh->ChangeElementNodes( theFace, newNodes, iSplit == iBestQuad ? 4 : 3 );
8001 const SMDS_MeshElement* newElem = 0;
8002 if (iSplit == iBestQuad)
8003 newElem = aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2], newNodes[3] );
8005 newElem = aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2] );
8006 myLastCreatedElems.Append(newElem);
8007 if ( aShapeId && newElem )
8008 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8009 } // end if(!theFace->IsQuadratic())
8010 else { // theFace is quadratic
8011 // we have to split theFace on simple triangles and one simple quadrangle
8013 int nbshift = tmp*2;
8014 // shift nodes in nodes[] by nbshift
8016 for(i=0; i<nbshift; i++) {
8017 const SMDS_MeshNode* n = nodes[0];
8018 for(j=0; j<nbFaceNodes-1; j++) {
8019 nodes[j] = nodes[j+1];
8021 nodes[nbFaceNodes-1] = n;
8023 il1 = il1 - nbshift;
8024 // now have to insert nodes between n0 and n1 or n1 and n2 (see below)
8025 // n0 n1 n2 n0 n1 n2
8026 // +-----+-----+ +-----+-----+
8035 // create new elements
8036 int aShapeId = FindShape( theFace );
8039 if(nbFaceNodes==6) { // quadratic triangle
8040 SMDS_MeshElement* newElem =
8041 aMesh->AddFace(nodes[3],nodes[4],nodes[5]);
8042 myLastCreatedElems.Append(newElem);
8043 if ( aShapeId && newElem )
8044 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8045 if(theFace->IsMediumNode(nodes[il1])) {
8046 // create quadrangle
8047 newElem = aMesh->AddFace(nodes[0],nodes[1],nodes[3],nodes[5]);
8048 myLastCreatedElems.Append(newElem);
8049 if ( aShapeId && newElem )
8050 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8056 // create quadrangle
8057 newElem = aMesh->AddFace(nodes[1],nodes[2],nodes[3],nodes[5]);
8058 myLastCreatedElems.Append(newElem);
8059 if ( aShapeId && newElem )
8060 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8066 else { // nbFaceNodes==8 - quadratic quadrangle
8067 SMDS_MeshElement* newElem =
8068 aMesh->AddFace(nodes[3],nodes[4],nodes[5]);
8069 myLastCreatedElems.Append(newElem);
8070 if ( aShapeId && newElem )
8071 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8072 newElem = aMesh->AddFace(nodes[5],nodes[6],nodes[7]);
8073 myLastCreatedElems.Append(newElem);
8074 if ( aShapeId && newElem )
8075 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8076 newElem = aMesh->AddFace(nodes[5],nodes[7],nodes[3]);
8077 myLastCreatedElems.Append(newElem);
8078 if ( aShapeId && newElem )
8079 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8080 if(theFace->IsMediumNode(nodes[il1])) {
8081 // create quadrangle
8082 newElem = aMesh->AddFace(nodes[0],nodes[1],nodes[3],nodes[7]);
8083 myLastCreatedElems.Append(newElem);
8084 if ( aShapeId && newElem )
8085 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8091 // create quadrangle
8092 newElem = aMesh->AddFace(nodes[1],nodes[2],nodes[3],nodes[7]);
8093 myLastCreatedElems.Append(newElem);
8094 if ( aShapeId && newElem )
8095 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8101 // create needed triangles using n1,n2,n3 and inserted nodes
8102 int nbn = 2 + aNodesToInsert.size();
8103 //const SMDS_MeshNode* aNodes[nbn];
8104 vector<const SMDS_MeshNode*> aNodes(nbn);
8105 aNodes[0] = nodes[n1];
8106 aNodes[nbn-1] = nodes[n2];
8107 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8108 for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
8109 aNodes[iNode++] = *nIt;
8111 for(i=1; i<nbn; i++) {
8112 SMDS_MeshElement* newElem =
8113 aMesh->AddFace(aNodes[i-1],aNodes[i],nodes[n3]);
8114 myLastCreatedElems.Append(newElem);
8115 if ( aShapeId && newElem )
8116 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8120 aMesh->RemoveElement(theFace);
8123 //=======================================================================
8124 //function : UpdateVolumes
8126 //=======================================================================
8127 void SMESH_MeshEditor::UpdateVolumes (const SMDS_MeshNode* theBetweenNode1,
8128 const SMDS_MeshNode* theBetweenNode2,
8129 list<const SMDS_MeshNode*>& theNodesToInsert)
8131 myLastCreatedElems.Clear();
8132 myLastCreatedNodes.Clear();
8134 SMDS_ElemIteratorPtr invElemIt = theBetweenNode1->GetInverseElementIterator(SMDSAbs_Volume);
8135 while (invElemIt->more()) { // loop on inverse elements of theBetweenNode1
8136 const SMDS_MeshElement* elem = invElemIt->next();
8138 // check, if current volume has link theBetweenNode1 - theBetweenNode2
8139 SMDS_VolumeTool aVolume (elem);
8140 if (!aVolume.IsLinked(theBetweenNode1, theBetweenNode2))
8143 // insert new nodes in all faces of the volume, sharing link theBetweenNode1 - theBetweenNode2
8144 int iface, nbFaces = aVolume.NbFaces();
8145 vector<const SMDS_MeshNode *> poly_nodes;
8146 vector<int> quantities (nbFaces);
8148 for (iface = 0; iface < nbFaces; iface++) {
8149 int nbFaceNodes = aVolume.NbFaceNodes(iface), nbInserted = 0;
8150 // faceNodes will contain (nbFaceNodes + 1) nodes, last = first
8151 const SMDS_MeshNode** faceNodes = aVolume.GetFaceNodes(iface);
8153 for (int inode = 0; inode < nbFaceNodes; inode++) {
8154 poly_nodes.push_back(faceNodes[inode]);
8156 if (nbInserted == 0) {
8157 if (faceNodes[inode] == theBetweenNode1) {
8158 if (faceNodes[inode + 1] == theBetweenNode2) {
8159 nbInserted = theNodesToInsert.size();
8161 // add nodes to insert
8162 list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.begin();
8163 for (; nIt != theNodesToInsert.end(); nIt++) {
8164 poly_nodes.push_back(*nIt);
8168 else if (faceNodes[inode] == theBetweenNode2) {
8169 if (faceNodes[inode + 1] == theBetweenNode1) {
8170 nbInserted = theNodesToInsert.size();
8172 // add nodes to insert in reversed order
8173 list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.end();
8175 for (; nIt != theNodesToInsert.begin(); nIt--) {
8176 poly_nodes.push_back(*nIt);
8178 poly_nodes.push_back(*nIt);
8185 quantities[iface] = nbFaceNodes + nbInserted;
8188 // Replace or update the volume
8189 SMESHDS_Mesh *aMesh = GetMeshDS();
8191 if (elem->IsPoly()) {
8192 aMesh->ChangePolyhedronNodes(elem, poly_nodes, quantities);
8196 int aShapeId = FindShape( elem );
8198 SMDS_MeshElement* newElem =
8199 aMesh->AddPolyhedralVolume(poly_nodes, quantities);
8200 myLastCreatedElems.Append(newElem);
8201 if (aShapeId && newElem)
8202 aMesh->SetMeshElementOnShape(newElem, aShapeId);
8204 aMesh->RemoveElement(elem);
8211 //================================================================================
8213 * \brief Transform any volume into data of SMDSEntity_Polyhedra
8215 //================================================================================
8217 void volumeToPolyhedron( const SMDS_MeshElement* elem,
8218 vector<const SMDS_MeshNode *> & nodes,
8219 vector<int> & nbNodeInFaces )
8222 nbNodeInFaces.clear();
8223 SMDS_VolumeTool vTool ( elem );
8224 for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
8226 const SMDS_MeshNode** fNodes = vTool.GetFaceNodes( iF );
8227 nodes.insert( nodes.end(), fNodes, fNodes + vTool.NbFaceNodes( iF ));
8228 nbNodeInFaces.push_back( vTool.NbFaceNodes( iF ));
8233 //=======================================================================
8235 * \brief Convert elements contained in a submesh to quadratic
8236 * \return int - nb of checked elements
8238 //=======================================================================
8240 int SMESH_MeshEditor::convertElemToQuadratic(SMESHDS_SubMesh * theSm,
8241 SMESH_MesherHelper& theHelper,
8242 const bool theForce3d)
8245 if( !theSm ) return nbElem;
8247 vector<int> nbNodeInFaces;
8248 vector<const SMDS_MeshNode *> nodes;
8249 SMDS_ElemIteratorPtr ElemItr = theSm->GetElements();
8250 while(ElemItr->more())
8253 const SMDS_MeshElement* elem = ElemItr->next();
8254 if( !elem ) continue;
8256 // analyse a necessity of conversion
8257 const SMDSAbs_ElementType aType = elem->GetType();
8258 if ( aType < SMDSAbs_Edge || aType > SMDSAbs_Volume )
8260 const SMDSAbs_EntityType aGeomType = elem->GetEntityType();
8261 bool hasCentralNodes = false;
8262 if ( elem->IsQuadratic() )
8265 switch ( aGeomType ) {
8266 case SMDSEntity_Quad_Triangle:
8267 case SMDSEntity_Quad_Quadrangle:
8268 case SMDSEntity_Quad_Hexa:
8269 alreadyOK = !theHelper.GetIsBiQuadratic(); break;
8271 case SMDSEntity_BiQuad_Triangle:
8272 case SMDSEntity_BiQuad_Quadrangle:
8273 case SMDSEntity_TriQuad_Hexa:
8274 alreadyOK = theHelper.GetIsBiQuadratic();
8275 hasCentralNodes = true;
8280 // take into account already present modium nodes
8282 case SMDSAbs_Volume:
8283 theHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( elem )); break;
8285 theHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( elem )); break;
8287 theHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( elem )); break;
8293 // get elem data needed to re-create it
8295 const int id = elem->GetID();
8296 const int nbNodes = elem->NbCornerNodes();
8297 nodes.assign(elem->begin_nodes(), elem->end_nodes());
8298 if ( aGeomType == SMDSEntity_Polyhedra )
8299 nbNodeInFaces = static_cast<const SMDS_VtkVolume* >( elem )->GetQuantities();
8300 else if ( aGeomType == SMDSEntity_Hexagonal_Prism )
8301 volumeToPolyhedron( elem, nodes, nbNodeInFaces );
8303 // remove a linear element
8304 GetMeshDS()->RemoveFreeElement(elem, theSm, /*fromGroups=*/false);
8306 // remove central nodes of biquadratic elements (biquad->quad convertion)
8307 if ( hasCentralNodes )
8308 for ( size_t i = nbNodes * 2; i < nodes.size(); ++i )
8309 if ( nodes[i]->NbInverseElements() == 0 )
8310 GetMeshDS()->RemoveFreeNode( nodes[i], theSm, /*fromGroups=*/true );
8312 const SMDS_MeshElement* NewElem = 0;
8318 NewElem = theHelper.AddEdge(nodes[0], nodes[1], id, theForce3d);
8326 NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
8329 NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
8332 NewElem = theHelper.AddPolygonalFace(nodes, id, theForce3d);
8336 case SMDSAbs_Volume :
8340 case SMDSEntity_Tetra:
8341 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
8343 case SMDSEntity_Pyramid:
8344 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], id, theForce3d);
8346 case SMDSEntity_Penta:
8347 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], nodes[5], id, theForce3d);
8349 case SMDSEntity_Hexa:
8350 case SMDSEntity_Quad_Hexa:
8351 case SMDSEntity_TriQuad_Hexa:
8352 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
8353 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
8355 case SMDSEntity_Hexagonal_Prism:
8357 NewElem = theHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
8364 ReplaceElemInGroups( elem, NewElem, GetMeshDS());
8365 if( NewElem && NewElem->getshapeId() < 1 )
8366 theSm->AddElement( NewElem );
8370 //=======================================================================
8371 //function : ConvertToQuadratic
8373 //=======================================================================
8375 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d, const bool theToBiQuad)
8377 SMESHDS_Mesh* meshDS = GetMeshDS();
8379 SMESH_MesherHelper aHelper(*myMesh);
8381 aHelper.SetIsQuadratic( true );
8382 aHelper.SetIsBiQuadratic( theToBiQuad );
8383 aHelper.SetElementsOnShape(true);
8385 // convert elements assigned to sub-meshes
8386 int nbCheckedElems = 0;
8387 if ( myMesh->HasShapeToMesh() )
8389 if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
8391 SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
8392 while ( smIt->more() ) {
8393 SMESH_subMesh* sm = smIt->next();
8394 if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() ) {
8395 aHelper.SetSubShape( sm->GetSubShape() );
8396 nbCheckedElems += convertElemToQuadratic(smDS, aHelper, theForce3d);
8402 // convert elements NOT assigned to sub-meshes
8403 int totalNbElems = meshDS->NbEdges() + meshDS->NbFaces() + meshDS->NbVolumes();
8404 if ( nbCheckedElems < totalNbElems ) // not all elements are in sub-meshes
8406 aHelper.SetElementsOnShape(false);
8407 SMESHDS_SubMesh *smDS = 0;
8410 SMDS_EdgeIteratorPtr aEdgeItr = meshDS->edgesIterator();
8411 while( aEdgeItr->more() )
8413 const SMDS_MeshEdge* edge = aEdgeItr->next();
8414 if ( !edge->IsQuadratic() )
8416 int id = edge->GetID();
8417 const SMDS_MeshNode* n1 = edge->GetNode(0);
8418 const SMDS_MeshNode* n2 = edge->GetNode(1);
8420 meshDS->RemoveFreeElement(edge, smDS, /*fromGroups=*/false);
8422 const SMDS_MeshEdge* NewEdge = aHelper.AddEdge(n1, n2, id, theForce3d);
8423 ReplaceElemInGroups( edge, NewEdge, GetMeshDS());
8427 aHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( edge ));
8432 SMDS_FaceIteratorPtr aFaceItr = meshDS->facesIterator();
8433 while( aFaceItr->more() )
8435 const SMDS_MeshFace* face = aFaceItr->next();
8436 if ( !face ) continue;
8438 const SMDSAbs_EntityType type = face->GetEntityType();
8442 case SMDSEntity_Quad_Triangle:
8443 case SMDSEntity_Quad_Quadrangle:
8444 alreadyOK = !theToBiQuad;
8445 aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
8447 case SMDSEntity_BiQuad_Triangle:
8448 case SMDSEntity_BiQuad_Quadrangle:
8449 alreadyOK = theToBiQuad;
8450 aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
8452 default: alreadyOK = false;
8457 const int id = face->GetID();
8458 vector<const SMDS_MeshNode *> nodes ( face->begin_nodes(), face->end_nodes());
8460 meshDS->RemoveFreeElement(face, smDS, /*fromGroups=*/false);
8462 SMDS_MeshFace * NewFace = 0;
8465 case SMDSEntity_Triangle:
8466 case SMDSEntity_Quad_Triangle:
8467 case SMDSEntity_BiQuad_Triangle:
8468 NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
8469 if ( nodes.size() == 7 && nodes[6]->NbInverseElements() == 0 ) // rm a central node
8470 GetMeshDS()->RemoveFreeNode( nodes[6], /*sm=*/0, /*fromGroups=*/true );
8473 case SMDSEntity_Quadrangle:
8474 case SMDSEntity_Quad_Quadrangle:
8475 case SMDSEntity_BiQuad_Quadrangle:
8476 NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
8477 if ( nodes.size() == 9 && nodes[8]->NbInverseElements() == 0 ) // rm a central node
8478 GetMeshDS()->RemoveFreeNode( nodes[8], /*sm=*/0, /*fromGroups=*/true );
8482 NewFace = aHelper.AddPolygonalFace(nodes, id, theForce3d);
8484 ReplaceElemInGroups( face, NewFace, GetMeshDS());
8488 vector<int> nbNodeInFaces;
8489 SMDS_VolumeIteratorPtr aVolumeItr = meshDS->volumesIterator();
8490 while(aVolumeItr->more())
8492 const SMDS_MeshVolume* volume = aVolumeItr->next();
8493 if ( !volume ) continue;
8495 const SMDSAbs_EntityType type = volume->GetEntityType();
8496 if (( theToBiQuad && type == SMDSEntity_TriQuad_Hexa ) ||
8497 ( !theToBiQuad && type == SMDSEntity_Quad_Hexa ))
8499 aHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( volume ));
8502 const int id = volume->GetID();
8503 vector<const SMDS_MeshNode *> nodes (volume->begin_nodes(), volume->end_nodes());
8504 if ( type == SMDSEntity_Polyhedra )
8505 nbNodeInFaces = static_cast<const SMDS_VtkVolume* >(volume)->GetQuantities();
8506 else if ( type == SMDSEntity_Hexagonal_Prism )
8507 volumeToPolyhedron( volume, nodes, nbNodeInFaces );
8509 meshDS->RemoveFreeElement(volume, smDS, /*fromGroups=*/false);
8511 SMDS_MeshVolume * NewVolume = 0;
8514 case SMDSEntity_Tetra:
8515 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d );
8517 case SMDSEntity_Hexa:
8518 case SMDSEntity_Quad_Hexa:
8519 case SMDSEntity_TriQuad_Hexa:
8520 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
8521 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
8522 for ( size_t i = 20; i < nodes.size(); ++i ) // rm central nodes
8523 if ( nodes[i]->NbInverseElements() == 0 )
8524 GetMeshDS()->RemoveFreeNode( nodes[i], /*sm=*/0, /*fromGroups=*/true );
8526 case SMDSEntity_Pyramid:
8527 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
8528 nodes[3], nodes[4], id, theForce3d);
8530 case SMDSEntity_Penta:
8531 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
8532 nodes[3], nodes[4], nodes[5], id, theForce3d);
8534 case SMDSEntity_Hexagonal_Prism:
8536 NewVolume = aHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
8538 ReplaceElemInGroups(volume, NewVolume, meshDS);
8543 { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
8544 // aHelper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
8545 // aHelper.FixQuadraticElements(myError);
8546 SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
8550 //================================================================================
8552 * \brief Makes given elements quadratic
8553 * \param theForce3d - if true, the medium nodes will be placed in the middle of link
8554 * \param theElements - elements to make quadratic
8556 //================================================================================
8558 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d,
8559 TIDSortedElemSet& theElements,
8560 const bool theToBiQuad)
8562 if ( theElements.empty() ) return;
8564 // we believe that all theElements are of the same type
8565 const SMDSAbs_ElementType elemType = (*theElements.begin())->GetType();
8567 // get all nodes shared by theElements
8568 TIDSortedNodeSet allNodes;
8569 TIDSortedElemSet::iterator eIt = theElements.begin();
8570 for ( ; eIt != theElements.end(); ++eIt )
8571 allNodes.insert( (*eIt)->begin_nodes(), (*eIt)->end_nodes() );
8573 // complete theElements with elements of lower dim whose all nodes are in allNodes
8575 TIDSortedElemSet quadAdjacentElems [ SMDSAbs_NbElementTypes ]; // quadratic adjacent elements
8576 TIDSortedElemSet checkedAdjacentElems [ SMDSAbs_NbElementTypes ];
8577 TIDSortedNodeSet::iterator nIt = allNodes.begin();
8578 for ( ; nIt != allNodes.end(); ++nIt )
8580 const SMDS_MeshNode* n = *nIt;
8581 SMDS_ElemIteratorPtr invIt = n->GetInverseElementIterator();
8582 while ( invIt->more() )
8584 const SMDS_MeshElement* e = invIt->next();
8585 const SMDSAbs_ElementType type = e->GetType();
8586 if ( e->IsQuadratic() )
8588 quadAdjacentElems[ type ].insert( e );
8591 switch ( e->GetEntityType() ) {
8592 case SMDSEntity_Quad_Triangle:
8593 case SMDSEntity_Quad_Quadrangle:
8594 case SMDSEntity_Quad_Hexa: alreadyOK = !theToBiQuad; break;
8595 case SMDSEntity_BiQuad_Triangle:
8596 case SMDSEntity_BiQuad_Quadrangle:
8597 case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; break;
8598 default: alreadyOK = true;
8603 if ( type >= elemType )
8604 continue; // same type or more complex linear element
8606 if ( !checkedAdjacentElems[ type ].insert( e ).second )
8607 continue; // e is already checked
8611 SMDS_NodeIteratorPtr nodeIt = e->nodeIterator();
8612 while ( nodeIt->more() && allIn )
8613 allIn = allNodes.count( nodeIt->next() );
8615 theElements.insert(e );
8619 SMESH_MesherHelper helper(*myMesh);
8620 helper.SetIsQuadratic( true );
8621 helper.SetIsBiQuadratic( theToBiQuad );
8623 // add links of quadratic adjacent elements to the helper
8625 if ( !quadAdjacentElems[SMDSAbs_Edge].empty() )
8626 for ( eIt = quadAdjacentElems[SMDSAbs_Edge].begin();
8627 eIt != quadAdjacentElems[SMDSAbs_Edge].end(); ++eIt )
8629 helper.AddTLinks( static_cast< const SMDS_MeshEdge*> (*eIt) );
8631 if ( !quadAdjacentElems[SMDSAbs_Face].empty() )
8632 for ( eIt = quadAdjacentElems[SMDSAbs_Face].begin();
8633 eIt != quadAdjacentElems[SMDSAbs_Face].end(); ++eIt )
8635 helper.AddTLinks( static_cast< const SMDS_MeshFace*> (*eIt) );
8637 if ( !quadAdjacentElems[SMDSAbs_Volume].empty() )
8638 for ( eIt = quadAdjacentElems[SMDSAbs_Volume].begin();
8639 eIt != quadAdjacentElems[SMDSAbs_Volume].end(); ++eIt )
8641 helper.AddTLinks( static_cast< const SMDS_MeshVolume*> (*eIt) );
8644 // make quadratic (or bi-tri-quadratic) elements instead of linear ones
8646 SMESHDS_Mesh* meshDS = GetMeshDS();
8647 SMESHDS_SubMesh* smDS = 0;
8648 for ( eIt = theElements.begin(); eIt != theElements.end(); ++eIt )
8650 const SMDS_MeshElement* elem = *eIt;
8653 int nbCentralNodes = 0;
8654 switch ( elem->GetEntityType() ) {
8655 // linear convertible
8656 case SMDSEntity_Edge:
8657 case SMDSEntity_Triangle:
8658 case SMDSEntity_Quadrangle:
8659 case SMDSEntity_Tetra:
8660 case SMDSEntity_Pyramid:
8661 case SMDSEntity_Hexa:
8662 case SMDSEntity_Penta: alreadyOK = false; nbCentralNodes = 0; break;
8663 // quadratic that can become bi-quadratic
8664 case SMDSEntity_Quad_Triangle:
8665 case SMDSEntity_Quad_Quadrangle:
8666 case SMDSEntity_Quad_Hexa: alreadyOK =!theToBiQuad; nbCentralNodes = 0; break;
8668 case SMDSEntity_BiQuad_Triangle:
8669 case SMDSEntity_BiQuad_Quadrangle: alreadyOK = theToBiQuad; nbCentralNodes = 1; break;
8670 case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; nbCentralNodes = 7; break;
8672 default: alreadyOK = true;
8674 if ( alreadyOK ) continue;
8676 const SMDSAbs_ElementType type = elem->GetType();
8677 const int id = elem->GetID();
8678 const int nbNodes = elem->NbCornerNodes();
8679 vector<const SMDS_MeshNode *> nodes ( elem->begin_nodes(), elem->end_nodes());
8681 helper.SetSubShape( elem->getshapeId() );
8683 if ( !smDS || !smDS->Contains( elem ))
8684 smDS = meshDS->MeshElements( elem->getshapeId() );
8685 meshDS->RemoveFreeElement(elem, smDS, /*fromGroups=*/false);
8687 SMDS_MeshElement * newElem = 0;
8690 case 4: // cases for most frequently used element types go first (for optimization)
8691 if ( type == SMDSAbs_Volume )
8692 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
8694 newElem = helper.AddFace (nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
8697 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
8698 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
8701 newElem = helper.AddFace (nodes[0], nodes[1], nodes[2], id, theForce3d);
8704 newElem = helper.AddEdge(nodes[0], nodes[1], id, theForce3d);
8707 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
8708 nodes[4], id, theForce3d);
8711 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
8712 nodes[4], nodes[5], id, theForce3d);
8716 ReplaceElemInGroups( elem, newElem, meshDS);
8717 if( newElem && smDS )
8718 smDS->AddElement( newElem );
8720 // remove central nodes
8721 for ( size_t i = nodes.size() - nbCentralNodes; i < nodes.size(); ++i )
8722 if ( nodes[i]->NbInverseElements() == 0 )
8723 meshDS->RemoveFreeNode( nodes[i], smDS, /*fromGroups=*/true );
8725 } // loop on theElements
8728 { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
8729 // helper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
8730 // helper.FixQuadraticElements( myError );
8731 SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
8735 //=======================================================================
8737 * \brief Convert quadratic elements to linear ones and remove quadratic nodes
8738 * \return int - nb of checked elements
8740 //=======================================================================
8742 int SMESH_MeshEditor::removeQuadElem(SMESHDS_SubMesh * theSm,
8743 SMDS_ElemIteratorPtr theItr,
8744 const int theShapeID)
8747 SMESHDS_Mesh* meshDS = GetMeshDS();
8749 while( theItr->more() )
8751 const SMDS_MeshElement* elem = theItr->next();
8753 if( elem && elem->IsQuadratic())
8755 int id = elem->GetID();
8756 int nbCornerNodes = elem->NbCornerNodes();
8757 SMDSAbs_ElementType aType = elem->GetType();
8759 vector<const SMDS_MeshNode *> nodes( elem->begin_nodes(), elem->end_nodes() );
8761 //remove a quadratic element
8762 if ( !theSm || !theSm->Contains( elem ))
8763 theSm = meshDS->MeshElements( elem->getshapeId() );
8764 meshDS->RemoveFreeElement( elem, theSm, /*fromGroups=*/false );
8766 // remove medium nodes
8767 for ( unsigned i = nbCornerNodes; i < nodes.size(); ++i )
8768 if ( nodes[i]->NbInverseElements() == 0 )
8769 meshDS->RemoveFreeNode( nodes[i], theSm );
8771 // add a linear element
8772 nodes.resize( nbCornerNodes );
8773 SMDS_MeshElement * newElem = AddElement( nodes, aType, false, id );
8774 ReplaceElemInGroups(elem, newElem, meshDS);
8775 if( theSm && newElem )
8776 theSm->AddElement( newElem );
8782 //=======================================================================
8783 //function : ConvertFromQuadratic
8785 //=======================================================================
8787 bool SMESH_MeshEditor::ConvertFromQuadratic()
8789 int nbCheckedElems = 0;
8790 if ( myMesh->HasShapeToMesh() )
8792 if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
8794 SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
8795 while ( smIt->more() ) {
8796 SMESH_subMesh* sm = smIt->next();
8797 if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() )
8798 nbCheckedElems += removeQuadElem( smDS, smDS->GetElements(), sm->GetId() );
8804 GetMeshDS()->NbEdges() + GetMeshDS()->NbFaces() + GetMeshDS()->NbVolumes();
8805 if ( nbCheckedElems < totalNbElems ) // not all elements are in submeshes
8807 SMESHDS_SubMesh *aSM = 0;
8808 removeQuadElem( aSM, GetMeshDS()->elementsIterator(), 0 );
8816 //================================================================================
8818 * \brief Return true if all medium nodes of the element are in the node set
8820 //================================================================================
8822 bool allMediumNodesIn(const SMDS_MeshElement* elem, TIDSortedNodeSet& nodeSet )
8824 for ( int i = elem->NbCornerNodes(); i < elem->NbNodes(); ++i )
8825 if ( !nodeSet.count( elem->GetNode(i) ))
8831 //================================================================================
8833 * \brief Makes given elements linear
8835 //================================================================================
8837 void SMESH_MeshEditor::ConvertFromQuadratic(TIDSortedElemSet& theElements)
8839 if ( theElements.empty() ) return;
8841 // collect IDs of medium nodes of theElements; some of these nodes will be removed
8842 set<int> mediumNodeIDs;
8843 TIDSortedElemSet::iterator eIt = theElements.begin();
8844 for ( ; eIt != theElements.end(); ++eIt )
8846 const SMDS_MeshElement* e = *eIt;
8847 for ( int i = e->NbCornerNodes(); i < e->NbNodes(); ++i )
8848 mediumNodeIDs.insert( e->GetNode(i)->GetID() );
8851 // replace given elements by linear ones
8852 SMDS_ElemIteratorPtr elemIt = elemSetIterator( theElements );
8853 removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
8855 // we need to convert remaining elements whose all medium nodes are in mediumNodeIDs
8856 // except those elements sharing medium nodes of quadratic element whose medium nodes
8857 // are not all in mediumNodeIDs
8859 // get remaining medium nodes
8860 TIDSortedNodeSet mediumNodes;
8861 set<int>::iterator nIdsIt = mediumNodeIDs.begin();
8862 for ( ; nIdsIt != mediumNodeIDs.end(); ++nIdsIt )
8863 if ( const SMDS_MeshNode* n = GetMeshDS()->FindNode( *nIdsIt ))
8864 mediumNodes.insert( mediumNodes.end(), n );
8866 // find more quadratic elements to convert
8867 TIDSortedElemSet moreElemsToConvert;
8868 TIDSortedNodeSet::iterator nIt = mediumNodes.begin();
8869 for ( ; nIt != mediumNodes.end(); ++nIt )
8871 SMDS_ElemIteratorPtr invIt = (*nIt)->GetInverseElementIterator();
8872 while ( invIt->more() )
8874 const SMDS_MeshElement* e = invIt->next();
8875 if ( e->IsQuadratic() && allMediumNodesIn( e, mediumNodes ))
8877 // find a more complex element including e and
8878 // whose medium nodes are not in mediumNodes
8879 bool complexFound = false;
8880 for ( int type = e->GetType() + 1; type < SMDSAbs_0DElement; ++type )
8882 SMDS_ElemIteratorPtr invIt2 =
8883 (*nIt)->GetInverseElementIterator( SMDSAbs_ElementType( type ));
8884 while ( invIt2->more() )
8886 const SMDS_MeshElement* eComplex = invIt2->next();
8887 if ( eComplex->IsQuadratic() && !allMediumNodesIn( eComplex, mediumNodes))
8889 int nbCommonNodes = SMESH_MeshAlgos::GetCommonNodes( e, eComplex ).size();
8890 if ( nbCommonNodes == e->NbNodes())
8892 complexFound = true;
8893 type = SMDSAbs_NbElementTypes; // to quit from the outer loop
8899 if ( !complexFound )
8900 moreElemsToConvert.insert( e );
8904 elemIt = elemSetIterator( moreElemsToConvert );
8905 removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
8908 //=======================================================================
8909 //function : SewSideElements
8911 //=======================================================================
8913 SMESH_MeshEditor::Sew_Error
8914 SMESH_MeshEditor::SewSideElements (TIDSortedElemSet& theSide1,
8915 TIDSortedElemSet& theSide2,
8916 const SMDS_MeshNode* theFirstNode1,
8917 const SMDS_MeshNode* theFirstNode2,
8918 const SMDS_MeshNode* theSecondNode1,
8919 const SMDS_MeshNode* theSecondNode2)
8921 myLastCreatedElems.Clear();
8922 myLastCreatedNodes.Clear();
8924 MESSAGE ("::::SewSideElements()");
8925 if ( theSide1.size() != theSide2.size() )
8926 return SEW_DIFF_NB_OF_ELEMENTS;
8928 Sew_Error aResult = SEW_OK;
8930 // 1. Build set of faces representing each side
8931 // 2. Find which nodes of the side 1 to merge with ones on the side 2
8932 // 3. Replace nodes in elements of the side 1 and remove replaced nodes
8934 // =======================================================================
8935 // 1. Build set of faces representing each side:
8936 // =======================================================================
8937 // a. build set of nodes belonging to faces
8938 // b. complete set of faces: find missing faces whose nodes are in set of nodes
8939 // c. create temporary faces representing side of volumes if correspondent
8940 // face does not exist
8942 SMESHDS_Mesh* aMesh = GetMeshDS();
8943 // TODO algoritm not OK with vtkUnstructuredGrid: 2 meshes can't share nodes
8944 //SMDS_Mesh aTmpFacesMesh; // try to use the same mesh
8945 TIDSortedElemSet faceSet1, faceSet2;
8946 set<const SMDS_MeshElement*> volSet1, volSet2;
8947 set<const SMDS_MeshNode*> nodeSet1, nodeSet2;
8948 TIDSortedElemSet * faceSetPtr[] = { &faceSet1, &faceSet2 };
8949 set<const SMDS_MeshElement*> * volSetPtr[] = { &volSet1, &volSet2 };
8950 set<const SMDS_MeshNode*> * nodeSetPtr[] = { &nodeSet1, &nodeSet2 };
8951 TIDSortedElemSet * elemSetPtr[] = { &theSide1, &theSide2 };
8952 int iSide, iFace, iNode;
8954 list<const SMDS_MeshElement* > tempFaceList;
8955 for ( iSide = 0; iSide < 2; iSide++ ) {
8956 set<const SMDS_MeshNode*> * nodeSet = nodeSetPtr[ iSide ];
8957 TIDSortedElemSet * elemSet = elemSetPtr[ iSide ];
8958 TIDSortedElemSet * faceSet = faceSetPtr[ iSide ];
8959 set<const SMDS_MeshElement*> * volSet = volSetPtr [ iSide ];
8960 set<const SMDS_MeshElement*>::iterator vIt;
8961 TIDSortedElemSet::iterator eIt;
8962 set<const SMDS_MeshNode*>::iterator nIt;
8964 // check that given nodes belong to given elements
8965 const SMDS_MeshNode* n1 = ( iSide == 0 ) ? theFirstNode1 : theFirstNode2;
8966 const SMDS_MeshNode* n2 = ( iSide == 0 ) ? theSecondNode1 : theSecondNode2;
8967 int firstIndex = -1, secondIndex = -1;
8968 for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
8969 const SMDS_MeshElement* elem = *eIt;
8970 if ( firstIndex < 0 ) firstIndex = elem->GetNodeIndex( n1 );
8971 if ( secondIndex < 0 ) secondIndex = elem->GetNodeIndex( n2 );
8972 if ( firstIndex > -1 && secondIndex > -1 ) break;
8974 if ( firstIndex < 0 || secondIndex < 0 ) {
8975 // we can simply return until temporary faces created
8976 return (iSide == 0 ) ? SEW_BAD_SIDE1_NODES : SEW_BAD_SIDE2_NODES;
8979 // -----------------------------------------------------------
8980 // 1a. Collect nodes of existing faces
8981 // and build set of face nodes in order to detect missing
8982 // faces corresponding to sides of volumes
8983 // -----------------------------------------------------------
8985 set< set <const SMDS_MeshNode*> > setOfFaceNodeSet;
8987 // loop on the given element of a side
8988 for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
8989 //const SMDS_MeshElement* elem = *eIt;
8990 const SMDS_MeshElement* elem = *eIt;
8991 if ( elem->GetType() == SMDSAbs_Face ) {
8992 faceSet->insert( elem );
8993 set <const SMDS_MeshNode*> faceNodeSet;
8994 SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
8995 while ( nodeIt->more() ) {
8996 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8997 nodeSet->insert( n );
8998 faceNodeSet.insert( n );
9000 setOfFaceNodeSet.insert( faceNodeSet );
9002 else if ( elem->GetType() == SMDSAbs_Volume )
9003 volSet->insert( elem );
9005 // ------------------------------------------------------------------------------
9006 // 1b. Complete set of faces: find missing faces whose nodes are in set of nodes
9007 // ------------------------------------------------------------------------------
9009 for ( nIt = nodeSet->begin(); nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
9010 SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
9011 while ( fIt->more() ) { // loop on faces sharing a node
9012 const SMDS_MeshElement* f = fIt->next();
9013 if ( faceSet->find( f ) == faceSet->end() ) {
9014 // check if all nodes are in nodeSet and
9015 // complete setOfFaceNodeSet if they are
9016 set <const SMDS_MeshNode*> faceNodeSet;
9017 SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
9018 bool allInSet = true;
9019 while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
9020 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9021 if ( nodeSet->find( n ) == nodeSet->end() )
9024 faceNodeSet.insert( n );
9027 faceSet->insert( f );
9028 setOfFaceNodeSet.insert( faceNodeSet );
9034 // -------------------------------------------------------------------------
9035 // 1c. Create temporary faces representing sides of volumes if correspondent
9036 // face does not exist
9037 // -------------------------------------------------------------------------
9039 if ( !volSet->empty() ) {
9040 //int nodeSetSize = nodeSet->size();
9042 // loop on given volumes
9043 for ( vIt = volSet->begin(); vIt != volSet->end(); vIt++ ) {
9044 SMDS_VolumeTool vol (*vIt);
9045 // loop on volume faces: find free faces
9046 // --------------------------------------
9047 list<const SMDS_MeshElement* > freeFaceList;
9048 for ( iFace = 0; iFace < vol.NbFaces(); iFace++ ) {
9049 if ( !vol.IsFreeFace( iFace ))
9051 // check if there is already a face with same nodes in a face set
9052 const SMDS_MeshElement* aFreeFace = 0;
9053 const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iFace );
9054 int nbNodes = vol.NbFaceNodes( iFace );
9055 set <const SMDS_MeshNode*> faceNodeSet;
9056 vol.GetFaceNodes( iFace, faceNodeSet );
9057 bool isNewFace = setOfFaceNodeSet.insert( faceNodeSet ).second;
9059 // no such a face is given but it still can exist, check it
9060 vector<const SMDS_MeshNode *> nodes ( fNodes, fNodes + nbNodes);
9061 aFreeFace = aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false );
9064 // create a temporary face
9065 if ( nbNodes == 3 ) {
9066 //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2] );
9067 aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2] );
9069 else if ( nbNodes == 4 ) {
9070 //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
9071 aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
9074 vector<const SMDS_MeshNode *> poly_nodes ( fNodes, & fNodes[nbNodes]);
9075 //aFreeFace = aTmpFacesMesh.AddPolygonalFace(poly_nodes);
9076 aFreeFace = aMesh->AddPolygonalFace(poly_nodes);
9079 tempFaceList.push_back( aFreeFace );
9083 freeFaceList.push_back( aFreeFace );
9085 } // loop on faces of a volume
9087 // choose one of several free faces of a volume
9088 // --------------------------------------------
9089 if ( freeFaceList.size() > 1 ) {
9090 // choose a face having max nb of nodes shared by other elems of a side
9091 int maxNbNodes = -1;
9092 list<const SMDS_MeshElement* >::iterator fIt = freeFaceList.begin();
9093 while ( fIt != freeFaceList.end() ) { // loop on free faces
9094 int nbSharedNodes = 0;
9095 SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
9096 while ( nodeIt->more() ) { // loop on free face nodes
9097 const SMDS_MeshNode* n =
9098 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9099 SMDS_ElemIteratorPtr invElemIt = n->GetInverseElementIterator();
9100 while ( invElemIt->more() ) {
9101 const SMDS_MeshElement* e = invElemIt->next();
9102 nbSharedNodes += faceSet->count( e );
9103 nbSharedNodes += elemSet->count( e );
9106 if ( nbSharedNodes > maxNbNodes ) {
9107 maxNbNodes = nbSharedNodes;
9108 freeFaceList.erase( freeFaceList.begin(), fIt++ );
9110 else if ( nbSharedNodes == maxNbNodes ) {
9114 freeFaceList.erase( fIt++ ); // here fIt++ occurs before erase
9117 if ( freeFaceList.size() > 1 )
9119 // could not choose one face, use another way
9120 // choose a face most close to the bary center of the opposite side
9121 gp_XYZ aBC( 0., 0., 0. );
9122 set <const SMDS_MeshNode*> addedNodes;
9123 TIDSortedElemSet * elemSet2 = elemSetPtr[ 1 - iSide ];
9124 eIt = elemSet2->begin();
9125 for ( eIt = elemSet2->begin(); eIt != elemSet2->end(); eIt++ ) {
9126 SMDS_ElemIteratorPtr nodeIt = (*eIt)->nodesIterator();
9127 while ( nodeIt->more() ) { // loop on free face nodes
9128 const SMDS_MeshNode* n =
9129 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9130 if ( addedNodes.insert( n ).second )
9131 aBC += gp_XYZ( n->X(),n->Y(),n->Z() );
9134 aBC /= addedNodes.size();
9135 double minDist = DBL_MAX;
9136 fIt = freeFaceList.begin();
9137 while ( fIt != freeFaceList.end() ) { // loop on free faces
9139 SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
9140 while ( nodeIt->more() ) { // loop on free face nodes
9141 const SMDS_MeshNode* n =
9142 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9143 gp_XYZ p( n->X(),n->Y(),n->Z() );
9144 dist += ( aBC - p ).SquareModulus();
9146 if ( dist < minDist ) {
9148 freeFaceList.erase( freeFaceList.begin(), fIt++ );
9151 fIt = freeFaceList.erase( fIt++ );
9154 } // choose one of several free faces of a volume
9156 if ( freeFaceList.size() == 1 ) {
9157 const SMDS_MeshElement* aFreeFace = freeFaceList.front();
9158 faceSet->insert( aFreeFace );
9159 // complete a node set with nodes of a found free face
9160 // for ( iNode = 0; iNode < ; iNode++ )
9161 // nodeSet->insert( fNodes[ iNode ] );
9164 } // loop on volumes of a side
9166 // // complete a set of faces if new nodes in a nodeSet appeared
9167 // // ----------------------------------------------------------
9168 // if ( nodeSetSize != nodeSet->size() ) {
9169 // for ( ; nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
9170 // SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
9171 // while ( fIt->more() ) { // loop on faces sharing a node
9172 // const SMDS_MeshElement* f = fIt->next();
9173 // if ( faceSet->find( f ) == faceSet->end() ) {
9174 // // check if all nodes are in nodeSet and
9175 // // complete setOfFaceNodeSet if they are
9176 // set <const SMDS_MeshNode*> faceNodeSet;
9177 // SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
9178 // bool allInSet = true;
9179 // while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
9180 // const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9181 // if ( nodeSet->find( n ) == nodeSet->end() )
9182 // allInSet = false;
9184 // faceNodeSet.insert( n );
9186 // if ( allInSet ) {
9187 // faceSet->insert( f );
9188 // setOfFaceNodeSet.insert( faceNodeSet );
9194 } // Create temporary faces, if there are volumes given
9197 if ( faceSet1.size() != faceSet2.size() ) {
9198 // delete temporary faces: they are in reverseElements of actual nodes
9199 // SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
9200 // while ( tmpFaceIt->more() )
9201 // aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
9202 // list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
9203 // for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
9204 // aMesh->RemoveElement(*tmpFaceIt);
9205 MESSAGE("Diff nb of faces");
9206 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9209 // ============================================================
9210 // 2. Find nodes to merge:
9211 // bind a node to remove to a node to put instead
9212 // ============================================================
9214 TNodeNodeMap nReplaceMap; // bind a node to remove to a node to put instead
9215 if ( theFirstNode1 != theFirstNode2 )
9216 nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
9217 if ( theSecondNode1 != theSecondNode2 )
9218 nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
9220 LinkID_Gen aLinkID_Gen( GetMeshDS() );
9221 set< long > linkIdSet; // links to process
9222 linkIdSet.insert( aLinkID_Gen.GetLinkID( theFirstNode1, theSecondNode1 ));
9224 typedef pair< const SMDS_MeshNode*, const SMDS_MeshNode* > NLink;
9225 list< NLink > linkList[2];
9226 linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
9227 linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
9228 // loop on links in linkList; find faces by links and append links
9229 // of the found faces to linkList
9230 list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
9231 for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ )
9233 NLink link[] = { *linkIt[0], *linkIt[1] };
9234 long linkID = aLinkID_Gen.GetLinkID( link[0].first, link[0].second );
9235 if ( !linkIdSet.count( linkID ) )
9238 // by links, find faces in the face sets,
9239 // and find indices of link nodes in the found faces;
9240 // in a face set, there is only one or no face sharing a link
9241 // ---------------------------------------------------------------
9243 const SMDS_MeshElement* face[] = { 0, 0 };
9244 vector<const SMDS_MeshNode*> fnodes[2];
9245 int iLinkNode[2][2];
9246 TIDSortedElemSet avoidSet;
9247 for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
9248 const SMDS_MeshNode* n1 = link[iSide].first;
9249 const SMDS_MeshNode* n2 = link[iSide].second;
9250 //cout << "Side " << iSide << " ";
9251 //cout << "L( " << n1->GetID() << ", " << n2->GetID() << " ) " << endl;
9252 // find a face by two link nodes
9253 face[ iSide ] = SMESH_MeshAlgos::FindFaceInSet( n1, n2,
9254 *faceSetPtr[ iSide ], avoidSet,
9255 &iLinkNode[iSide][0],
9256 &iLinkNode[iSide][1] );
9259 //cout << " F " << face[ iSide]->GetID() <<endl;
9260 faceSetPtr[ iSide ]->erase( face[ iSide ]);
9261 // put face nodes to fnodes
9262 if ( face[ iSide ]->IsQuadratic() )
9264 // use interlaced nodes iterator
9265 const SMDS_VtkFace* F = dynamic_cast<const SMDS_VtkFace*>( face[ iSide ]);
9266 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
9267 SMDS_ElemIteratorPtr nIter = F->interlacedNodesElemIterator();
9268 while ( nIter->more() )
9269 fnodes[ iSide ].push_back( cast2Node( nIter->next() ));
9273 fnodes[ iSide ].assign( face[ iSide ]->begin_nodes(),
9274 face[ iSide ]->end_nodes() );
9276 fnodes[ iSide ].push_back( fnodes[ iSide ].front());
9280 // check similarity of elements of the sides
9281 if (aResult == SEW_OK && (( face[0] && !face[1] ) || ( !face[0] && face[1] ))) {
9282 MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
9283 if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
9284 aResult = ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
9287 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9289 break; // do not return because it's necessary to remove tmp faces
9292 // set nodes to merge
9293 // -------------------
9295 if ( face[0] && face[1] ) {
9296 const int nbNodes = face[0]->NbNodes();
9297 if ( nbNodes != face[1]->NbNodes() ) {
9298 MESSAGE("Diff nb of face nodes");
9299 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9300 break; // do not return because it s necessary to remove tmp faces
9302 bool reverse[] = { false, false }; // order of nodes in the link
9303 for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
9304 // analyse link orientation in faces
9305 int i1 = iLinkNode[ iSide ][ 0 ];
9306 int i2 = iLinkNode[ iSide ][ 1 ];
9307 reverse[ iSide ] = Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1;
9309 int di1 = reverse[0] ? -1 : +1, i1 = iLinkNode[0][1] + di1;
9310 int di2 = reverse[1] ? -1 : +1, i2 = iLinkNode[1][1] + di2;
9311 for ( int i = nbNodes - 2; i > 0; --i, i1 += di1, i2 += di2 )
9313 nReplaceMap.insert ( make_pair ( fnodes[0][ ( i1 + nbNodes ) % nbNodes ],
9314 fnodes[1][ ( i2 + nbNodes ) % nbNodes ]));
9317 // add other links of the faces to linkList
9318 // -----------------------------------------
9320 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
9321 linkID = aLinkID_Gen.GetLinkID( fnodes[0][iNode], fnodes[0][iNode+1] );
9322 pair< set<long>::iterator, bool > iter_isnew = linkIdSet.insert( linkID );
9323 if ( !iter_isnew.second ) { // already in a set: no need to process
9324 linkIdSet.erase( iter_isnew.first );
9326 else // new in set == encountered for the first time: add
9328 const SMDS_MeshNode* n1 = fnodes[0][ iNode ];
9329 const SMDS_MeshNode* n2 = fnodes[0][ iNode + 1];
9330 linkList[0].push_back ( NLink( n1, n2 ));
9331 linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
9336 if ( faceSetPtr[0]->empty() || faceSetPtr[1]->empty() )
9339 } // loop on link lists
9341 if ( aResult == SEW_OK &&
9342 ( //linkIt[0] != linkList[0].end() ||
9343 !faceSetPtr[0]->empty() || !faceSetPtr[1]->empty() )) {
9344 MESSAGE( (linkIt[0] != linkList[0].end()) <<" "<< (faceSetPtr[0]->empty()) <<
9345 " " << (faceSetPtr[1]->empty()));
9346 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9349 // ====================================================================
9350 // 3. Replace nodes in elements of the side 1 and remove replaced nodes
9351 // ====================================================================
9353 // delete temporary faces
9354 // SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
9355 // while ( tmpFaceIt->more() )
9356 // aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
9357 list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
9358 for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
9359 aMesh->RemoveElement(*tmpFaceIt);
9361 if ( aResult != SEW_OK)
9364 list< int > nodeIDsToRemove/*, elemIDsToRemove*/;
9365 // loop on nodes replacement map
9366 TNodeNodeMap::iterator nReplaceMapIt = nReplaceMap.begin(), nnIt;
9367 for ( ; nReplaceMapIt != nReplaceMap.end(); nReplaceMapIt++ )
9368 if ( (*nReplaceMapIt).first != (*nReplaceMapIt).second ) {
9369 const SMDS_MeshNode* nToRemove = (*nReplaceMapIt).first;
9370 nodeIDsToRemove.push_back( nToRemove->GetID() );
9371 // loop on elements sharing nToRemove
9372 SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
9373 while ( invElemIt->more() ) {
9374 const SMDS_MeshElement* e = invElemIt->next();
9375 // get a new suite of nodes: make replacement
9376 int nbReplaced = 0, i = 0, nbNodes = e->NbNodes();
9377 vector< const SMDS_MeshNode*> nodes( nbNodes );
9378 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
9379 while ( nIt->more() ) {
9380 const SMDS_MeshNode* n =
9381 static_cast<const SMDS_MeshNode*>( nIt->next() );
9382 nnIt = nReplaceMap.find( n );
9383 if ( nnIt != nReplaceMap.end() ) {
9389 // if ( nbReplaced == nbNodes && e->GetType() == SMDSAbs_Face )
9390 // elemIDsToRemove.push_back( e->GetID() );
9394 SMDSAbs_ElementType etyp = e->GetType();
9395 SMDS_MeshElement* newElem = this->AddElement(nodes, etyp, false);
9398 myLastCreatedElems.Append(newElem);
9399 AddToSameGroups(newElem, e, aMesh);
9400 int aShapeId = e->getshapeId();
9403 aMesh->SetMeshElementOnShape( newElem, aShapeId );
9406 aMesh->RemoveElement(e);
9411 Remove( nodeIDsToRemove, true );
9416 //================================================================================
9418 * \brief Find corresponding nodes in two sets of faces
9419 * \param theSide1 - first face set
9420 * \param theSide2 - second first face
9421 * \param theFirstNode1 - a boundary node of set 1
9422 * \param theFirstNode2 - a node of set 2 corresponding to theFirstNode1
9423 * \param theSecondNode1 - a boundary node of set 1 linked with theFirstNode1
9424 * \param theSecondNode2 - a node of set 2 corresponding to theSecondNode1
9425 * \param nReplaceMap - output map of corresponding nodes
9426 * \return bool - is a success or not
9428 //================================================================================
9431 //#define DEBUG_MATCHING_NODES
9434 SMESH_MeshEditor::Sew_Error
9435 SMESH_MeshEditor::FindMatchingNodes(set<const SMDS_MeshElement*>& theSide1,
9436 set<const SMDS_MeshElement*>& theSide2,
9437 const SMDS_MeshNode* theFirstNode1,
9438 const SMDS_MeshNode* theFirstNode2,
9439 const SMDS_MeshNode* theSecondNode1,
9440 const SMDS_MeshNode* theSecondNode2,
9441 TNodeNodeMap & nReplaceMap)
9443 set<const SMDS_MeshElement*> * faceSetPtr[] = { &theSide1, &theSide2 };
9445 nReplaceMap.clear();
9446 if ( theFirstNode1 != theFirstNode2 )
9447 nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
9448 if ( theSecondNode1 != theSecondNode2 )
9449 nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
9451 set< SMESH_TLink > linkSet; // set of nodes where order of nodes is ignored
9452 linkSet.insert( SMESH_TLink( theFirstNode1, theSecondNode1 ));
9454 list< NLink > linkList[2];
9455 linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
9456 linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
9458 // loop on links in linkList; find faces by links and append links
9459 // of the found faces to linkList
9460 list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
9461 for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ ) {
9462 NLink link[] = { *linkIt[0], *linkIt[1] };
9463 if ( linkSet.find( link[0] ) == linkSet.end() )
9466 // by links, find faces in the face sets,
9467 // and find indices of link nodes in the found faces;
9468 // in a face set, there is only one or no face sharing a link
9469 // ---------------------------------------------------------------
9471 const SMDS_MeshElement* face[] = { 0, 0 };
9472 list<const SMDS_MeshNode*> notLinkNodes[2];
9473 //bool reverse[] = { false, false }; // order of notLinkNodes
9475 for ( int iSide = 0; iSide < 2; iSide++ ) // loop on 2 sides
9477 const SMDS_MeshNode* n1 = link[iSide].first;
9478 const SMDS_MeshNode* n2 = link[iSide].second;
9479 set<const SMDS_MeshElement*> * faceSet = faceSetPtr[ iSide ];
9480 set< const SMDS_MeshElement* > facesOfNode1;
9481 for ( int iNode = 0; iNode < 2; iNode++ ) // loop on 2 nodes of a link
9483 // during a loop of the first node, we find all faces around n1,
9484 // during a loop of the second node, we find one face sharing both n1 and n2
9485 const SMDS_MeshNode* n = iNode ? n1 : n2; // a node of a link
9486 SMDS_ElemIteratorPtr fIt = n->GetInverseElementIterator(SMDSAbs_Face);
9487 while ( fIt->more() ) { // loop on faces sharing a node
9488 const SMDS_MeshElement* f = fIt->next();
9489 if (faceSet->find( f ) != faceSet->end() && // f is in face set
9490 ! facesOfNode1.insert( f ).second ) // f encounters twice
9492 if ( face[ iSide ] ) {
9493 MESSAGE( "2 faces per link " );
9494 return ( iSide ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
9497 faceSet->erase( f );
9499 // get not link nodes
9500 int nbN = f->NbNodes();
9501 if ( f->IsQuadratic() )
9503 nbNodes[ iSide ] = nbN;
9504 list< const SMDS_MeshNode* > & nodes = notLinkNodes[ iSide ];
9505 int i1 = f->GetNodeIndex( n1 );
9506 int i2 = f->GetNodeIndex( n2 );
9507 int iEnd = nbN, iBeg = -1, iDelta = 1;
9508 bool reverse = ( Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1 );
9510 std::swap( iEnd, iBeg ); iDelta = -1;
9515 if ( i == iEnd ) i = iBeg + iDelta;
9516 if ( i == i1 ) break;
9517 nodes.push_back ( f->GetNode( i ) );
9523 // check similarity of elements of the sides
9524 if (( face[0] && !face[1] ) || ( !face[0] && face[1] )) {
9525 MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
9526 if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
9527 return ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
9530 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9534 // set nodes to merge
9535 // -------------------
9537 if ( face[0] && face[1] ) {
9538 if ( nbNodes[0] != nbNodes[1] ) {
9539 MESSAGE("Diff nb of face nodes");
9540 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9542 #ifdef DEBUG_MATCHING_NODES
9543 MESSAGE ( " Link 1: " << link[0].first->GetID() <<" "<< link[0].second->GetID()
9544 << " F 1: " << face[0] << "| Link 2: " << link[1].first->GetID() <<" "
9545 << link[1].second->GetID() << " F 2: " << face[1] << " | Bind: " ) ;
9547 int nbN = nbNodes[0];
9549 list<const SMDS_MeshNode*>::iterator n1 = notLinkNodes[0].begin();
9550 list<const SMDS_MeshNode*>::iterator n2 = notLinkNodes[1].begin();
9551 for ( int i = 0 ; i < nbN - 2; ++i ) {
9552 #ifdef DEBUG_MATCHING_NODES
9553 MESSAGE ( (*n1)->GetID() << " to " << (*n2)->GetID() );
9555 nReplaceMap.insert( make_pair( *(n1++), *(n2++) ));
9559 // add other links of the face 1 to linkList
9560 // -----------------------------------------
9562 const SMDS_MeshElement* f0 = face[0];
9563 const SMDS_MeshNode* n1 = f0->GetNode( nbN - 1 );
9564 for ( int i = 0; i < nbN; i++ )
9566 const SMDS_MeshNode* n2 = f0->GetNode( i );
9567 pair< set< SMESH_TLink >::iterator, bool > iter_isnew =
9568 linkSet.insert( SMESH_TLink( n1, n2 ));
9569 if ( !iter_isnew.second ) { // already in a set: no need to process
9570 linkSet.erase( iter_isnew.first );
9572 else // new in set == encountered for the first time: add
9574 #ifdef DEBUG_MATCHING_NODES
9575 MESSAGE ( "Add link 1: " << n1->GetID() << " " << n2->GetID() << " "
9576 << " | link 2: " << nReplaceMap[n1]->GetID() << " " << nReplaceMap[n2]->GetID() << " " );
9578 linkList[0].push_back ( NLink( n1, n2 ));
9579 linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
9584 } // loop on link lists
9589 //================================================================================
9591 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
9592 \param theElems - the list of elements (edges or faces) to be replicated
9593 The nodes for duplication could be found from these elements
9594 \param theNodesNot - list of nodes to NOT replicate
9595 \param theAffectedElems - the list of elements (cells and edges) to which the
9596 replicated nodes should be associated to.
9597 \return TRUE if operation has been completed successfully, FALSE otherwise
9599 //================================================================================
9601 bool SMESH_MeshEditor::DoubleNodes( const TIDSortedElemSet& theElems,
9602 const TIDSortedElemSet& theNodesNot,
9603 const TIDSortedElemSet& theAffectedElems )
9605 myLastCreatedElems.Clear();
9606 myLastCreatedNodes.Clear();
9608 if ( theElems.size() == 0 )
9611 SMESHDS_Mesh* aMeshDS = GetMeshDS();
9616 std::map< const SMDS_MeshNode*, const SMDS_MeshNode* > anOldNodeToNewNode;
9617 // duplicate elements and nodes
9618 res = doubleNodes( aMeshDS, theElems, theNodesNot, anOldNodeToNewNode, true );
9619 // replce nodes by duplications
9620 res = doubleNodes( aMeshDS, theAffectedElems, theNodesNot, anOldNodeToNewNode, false );
9624 //================================================================================
9626 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
9627 \param theMeshDS - mesh instance
9628 \param theElems - the elements replicated or modified (nodes should be changed)
9629 \param theNodesNot - nodes to NOT replicate
9630 \param theNodeNodeMap - relation of old node to new created node
9631 \param theIsDoubleElem - flag os to replicate element or modify
9632 \return TRUE if operation has been completed successfully, FALSE otherwise
9634 //================================================================================
9636 bool SMESH_MeshEditor::doubleNodes( SMESHDS_Mesh* theMeshDS,
9637 const TIDSortedElemSet& theElems,
9638 const TIDSortedElemSet& theNodesNot,
9639 std::map< const SMDS_MeshNode*,
9640 const SMDS_MeshNode* >& theNodeNodeMap,
9641 const bool theIsDoubleElem )
9643 MESSAGE("doubleNodes");
9644 // iterate on through element and duplicate them (by nodes duplication)
9646 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
9647 for ( ; elemItr != theElems.end(); ++elemItr )
9649 const SMDS_MeshElement* anElem = *elemItr;
9653 bool isDuplicate = false;
9654 // duplicate nodes to duplicate element
9655 std::vector<const SMDS_MeshNode*> newNodes( anElem->NbNodes() );
9656 SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
9658 while ( anIter->more() )
9661 SMDS_MeshNode* aCurrNode = (SMDS_MeshNode*)anIter->next();
9662 SMDS_MeshNode* aNewNode = aCurrNode;
9663 if ( theNodeNodeMap.find( aCurrNode ) != theNodeNodeMap.end() )
9664 aNewNode = (SMDS_MeshNode*)theNodeNodeMap[ aCurrNode ];
9665 else if ( theIsDoubleElem && theNodesNot.find( aCurrNode ) == theNodesNot.end() )
9668 aNewNode = theMeshDS->AddNode( aCurrNode->X(), aCurrNode->Y(), aCurrNode->Z() );
9669 theNodeNodeMap[ aCurrNode ] = aNewNode;
9670 myLastCreatedNodes.Append( aNewNode );
9672 isDuplicate |= (aCurrNode != aNewNode);
9673 newNodes[ ind++ ] = aNewNode;
9678 if ( theIsDoubleElem )
9679 AddElement(newNodes, anElem->GetType(), anElem->IsPoly());
9682 MESSAGE("ChangeElementNodes");
9683 theMeshDS->ChangeElementNodes( anElem, &newNodes[ 0 ], anElem->NbNodes() );
9690 //================================================================================
9692 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
9693 \param theNodes - identifiers of nodes to be doubled
9694 \param theModifiedElems - identifiers of elements to be updated by the new (doubled)
9695 nodes. If list of element identifiers is empty then nodes are doubled but
9696 they not assigned to elements
9697 \return TRUE if operation has been completed successfully, FALSE otherwise
9699 //================================================================================
9701 bool SMESH_MeshEditor::DoubleNodes( const std::list< int >& theListOfNodes,
9702 const std::list< int >& theListOfModifiedElems )
9704 MESSAGE("DoubleNodes");
9705 myLastCreatedElems.Clear();
9706 myLastCreatedNodes.Clear();
9708 if ( theListOfNodes.size() == 0 )
9711 SMESHDS_Mesh* aMeshDS = GetMeshDS();
9715 // iterate through nodes and duplicate them
9717 std::map< const SMDS_MeshNode*, const SMDS_MeshNode* > anOldNodeToNewNode;
9719 std::list< int >::const_iterator aNodeIter;
9720 for ( aNodeIter = theListOfNodes.begin(); aNodeIter != theListOfNodes.end(); ++aNodeIter )
9722 int aCurr = *aNodeIter;
9723 SMDS_MeshNode* aNode = (SMDS_MeshNode*)aMeshDS->FindNode( aCurr );
9729 const SMDS_MeshNode* aNewNode = aMeshDS->AddNode( aNode->X(), aNode->Y(), aNode->Z() );
9732 anOldNodeToNewNode[ aNode ] = aNewNode;
9733 myLastCreatedNodes.Append( aNewNode );
9737 // Create map of new nodes for modified elements
9739 std::map< SMDS_MeshElement*, vector<const SMDS_MeshNode*> > anElemToNodes;
9741 std::list< int >::const_iterator anElemIter;
9742 for ( anElemIter = theListOfModifiedElems.begin();
9743 anElemIter != theListOfModifiedElems.end(); ++anElemIter )
9745 int aCurr = *anElemIter;
9746 SMDS_MeshElement* anElem = (SMDS_MeshElement*)aMeshDS->FindElement( aCurr );
9750 vector<const SMDS_MeshNode*> aNodeArr( anElem->NbNodes() );
9752 SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
9754 while ( anIter->more() )
9756 SMDS_MeshNode* aCurrNode = (SMDS_MeshNode*)anIter->next();
9757 if ( aCurr && anOldNodeToNewNode.find( aCurrNode ) != anOldNodeToNewNode.end() )
9759 const SMDS_MeshNode* aNewNode = anOldNodeToNewNode[ aCurrNode ];
9760 aNodeArr[ ind++ ] = aNewNode;
9763 aNodeArr[ ind++ ] = aCurrNode;
9765 anElemToNodes[ anElem ] = aNodeArr;
9768 // Change nodes of elements
9770 std::map< SMDS_MeshElement*, vector<const SMDS_MeshNode*> >::iterator
9771 anElemToNodesIter = anElemToNodes.begin();
9772 for ( ; anElemToNodesIter != anElemToNodes.end(); ++anElemToNodesIter )
9774 const SMDS_MeshElement* anElem = anElemToNodesIter->first;
9775 vector<const SMDS_MeshNode*> aNodeArr = anElemToNodesIter->second;
9778 MESSAGE("ChangeElementNodes");
9779 aMeshDS->ChangeElementNodes( anElem, &aNodeArr[ 0 ], anElem->NbNodes() );
9788 //================================================================================
9790 \brief Check if element located inside shape
9791 \return TRUE if IN or ON shape, FALSE otherwise
9793 //================================================================================
9795 template<class Classifier>
9796 bool isInside(const SMDS_MeshElement* theElem,
9797 Classifier& theClassifier,
9798 const double theTol)
9800 gp_XYZ centerXYZ (0, 0, 0);
9801 SMDS_ElemIteratorPtr aNodeItr = theElem->nodesIterator();
9802 while (aNodeItr->more())
9803 centerXYZ += SMESH_TNodeXYZ(cast2Node( aNodeItr->next()));
9805 gp_Pnt aPnt = centerXYZ / theElem->NbNodes();
9806 theClassifier.Perform(aPnt, theTol);
9807 TopAbs_State aState = theClassifier.State();
9808 return (aState == TopAbs_IN || aState == TopAbs_ON );
9811 //================================================================================
9813 * \brief Classifier of the 3D point on the TopoDS_Face
9814 * with interaface suitable for isInside()
9816 //================================================================================
9818 struct _FaceClassifier
9820 Extrema_ExtPS _extremum;
9821 BRepAdaptor_Surface _surface;
9822 TopAbs_State _state;
9824 _FaceClassifier(const TopoDS_Face& face):_extremum(),_surface(face),_state(TopAbs_OUT)
9826 _extremum.Initialize( _surface,
9827 _surface.FirstUParameter(), _surface.LastUParameter(),
9828 _surface.FirstVParameter(), _surface.LastVParameter(),
9829 _surface.Tolerance(), _surface.Tolerance() );
9831 void Perform(const gp_Pnt& aPnt, double theTol)
9833 _state = TopAbs_OUT;
9834 _extremum.Perform(aPnt);
9835 if ( _extremum.IsDone() )
9836 for ( int iSol = 1; iSol <= _extremum.NbExt() && _state == TopAbs_OUT; ++iSol)
9837 #if OCC_VERSION_LARGE > 0x06040000 // Porting to OCCT6.5.1
9838 _state = ( _extremum.SquareDistance(iSol) <= theTol ? TopAbs_IN : TopAbs_OUT );
9840 _state = ( _extremum.Value(iSol) <= theTol ? TopAbs_IN : TopAbs_OUT );
9843 TopAbs_State State() const
9850 //================================================================================
9852 \brief Identify the elements that will be affected by node duplication (actual duplication is not performed.
9853 This method is the first step of DoubleNodeElemGroupsInRegion.
9854 \param theElems - list of groups of elements (edges or faces) to be replicated
9855 \param theNodesNot - list of groups of nodes not to replicated
9856 \param theShape - shape to detect affected elements (element which geometric center
9857 located on or inside shape).
9858 The replicated nodes should be associated to affected elements.
9859 \return groups of affected elements
9860 \sa DoubleNodeElemGroupsInRegion()
9862 //================================================================================
9864 bool SMESH_MeshEditor::AffectedElemGroupsInRegion( const TIDSortedElemSet& theElems,
9865 const TIDSortedElemSet& theNodesNot,
9866 const TopoDS_Shape& theShape,
9867 TIDSortedElemSet& theAffectedElems)
9869 if ( theShape.IsNull() )
9872 const double aTol = Precision::Confusion();
9873 auto_ptr< BRepClass3d_SolidClassifier> bsc3d;
9874 auto_ptr<_FaceClassifier> aFaceClassifier;
9875 if ( theShape.ShapeType() == TopAbs_SOLID )
9877 bsc3d.reset( new BRepClass3d_SolidClassifier(theShape));;
9878 bsc3d->PerformInfinitePoint(aTol);
9880 else if (theShape.ShapeType() == TopAbs_FACE )
9882 aFaceClassifier.reset( new _FaceClassifier(TopoDS::Face(theShape)));
9885 // iterates on indicated elements and get elements by back references from their nodes
9886 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
9887 for ( ; elemItr != theElems.end(); ++elemItr )
9889 SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
9893 SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
9894 while ( nodeItr->more() )
9896 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
9897 if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
9899 SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
9900 while ( backElemItr->more() )
9902 const SMDS_MeshElement* curElem = backElemItr->next();
9903 if ( curElem && theElems.find(curElem) == theElems.end() &&
9905 isInside( curElem, *bsc3d, aTol ) :
9906 isInside( curElem, *aFaceClassifier, aTol )))
9907 theAffectedElems.insert( curElem );
9914 //================================================================================
9916 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
9917 \param theElems - group of of elements (edges or faces) to be replicated
9918 \param theNodesNot - group of nodes not to replicate
9919 \param theShape - shape to detect affected elements (element which geometric center
9920 located on or inside shape).
9921 The replicated nodes should be associated to affected elements.
9922 \return TRUE if operation has been completed successfully, FALSE otherwise
9924 //================================================================================
9926 bool SMESH_MeshEditor::DoubleNodesInRegion( const TIDSortedElemSet& theElems,
9927 const TIDSortedElemSet& theNodesNot,
9928 const TopoDS_Shape& theShape )
9930 if ( theShape.IsNull() )
9933 const double aTol = Precision::Confusion();
9934 auto_ptr< BRepClass3d_SolidClassifier> bsc3d;
9935 auto_ptr<_FaceClassifier> aFaceClassifier;
9936 if ( theShape.ShapeType() == TopAbs_SOLID )
9938 bsc3d.reset( new BRepClass3d_SolidClassifier(theShape));;
9939 bsc3d->PerformInfinitePoint(aTol);
9941 else if (theShape.ShapeType() == TopAbs_FACE )
9943 aFaceClassifier.reset( new _FaceClassifier(TopoDS::Face(theShape)));
9946 // iterates on indicated elements and get elements by back references from their nodes
9947 TIDSortedElemSet anAffected;
9948 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
9949 for ( ; elemItr != theElems.end(); ++elemItr )
9951 SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
9955 SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
9956 while ( nodeItr->more() )
9958 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
9959 if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
9961 SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
9962 while ( backElemItr->more() )
9964 const SMDS_MeshElement* curElem = backElemItr->next();
9965 if ( curElem && theElems.find(curElem) == theElems.end() &&
9967 isInside( curElem, *bsc3d, aTol ) :
9968 isInside( curElem, *aFaceClassifier, aTol )))
9969 anAffected.insert( curElem );
9973 return DoubleNodes( theElems, theNodesNot, anAffected );
9977 * \brief compute an oriented angle between two planes defined by four points.
9978 * The vector (p0,p1) defines the intersection of the 2 planes (p0,p1,g1) and (p0,p1,g2)
9979 * @param p0 base of the rotation axe
9980 * @param p1 extremity of the rotation axe
9981 * @param g1 belongs to the first plane
9982 * @param g2 belongs to the second plane
9984 double SMESH_MeshEditor::OrientedAngle(const gp_Pnt& p0, const gp_Pnt& p1, const gp_Pnt& g1, const gp_Pnt& g2)
9986 // MESSAGE(" p0: " << p0.X() << " " << p0.Y() << " " << p0.Z());
9987 // MESSAGE(" p1: " << p1.X() << " " << p1.Y() << " " << p1.Z());
9988 // MESSAGE(" g1: " << g1.X() << " " << g1.Y() << " " << g1.Z());
9989 // MESSAGE(" g2: " << g2.X() << " " << g2.Y() << " " << g2.Z());
9990 gp_Vec vref(p0, p1);
9993 gp_Vec n1 = vref.Crossed(v1);
9994 gp_Vec n2 = vref.Crossed(v2);
9995 return n2.AngleWithRef(n1, vref);
9999 * \brief Double nodes on shared faces between groups of volumes and create flat elements on demand.
10000 * The list of groups must contain at least two groups. The groups have to be disjoint: no common element into two different groups.
10001 * The nodes of the internal faces at the boundaries of the groups are doubled. Optionally, the internal faces are replaced by flat elements.
10002 * Triangles are transformed into prisms, and quadrangles into hexahedrons.
10003 * The flat elements are stored in groups of volumes. These groups are named according to the position of the group in the list:
10004 * 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.
10005 * 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.
10006 * All the flat elements are gathered into the group named "joints3D" (or "joints2D" in 2D situation).
10007 * The flat element of the multiple junctions between the simple junction are stored in a group named "jointsMultiples".
10008 * @param theElems - list of groups of volumes, where a group of volume is a set of
10009 * SMDS_MeshElements sorted by Id.
10010 * @param createJointElems - if TRUE, create the elements
10011 * @return TRUE if operation has been completed successfully, FALSE otherwise
10013 bool SMESH_MeshEditor::DoubleNodesOnGroupBoundaries( const std::vector<TIDSortedElemSet>& theElems,
10014 bool createJointElems)
10016 MESSAGE("----------------------------------------------");
10017 MESSAGE("SMESH_MeshEditor::doubleNodesOnGroupBoundaries");
10018 MESSAGE("----------------------------------------------");
10020 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
10021 meshDS->BuildDownWardConnectivity(true);
10023 SMDS_UnstructuredGrid *grid = meshDS->getGrid();
10025 // --- build the list of faces shared by 2 domains (group of elements), with their domain and volume indexes
10026 // build the list of cells with only a node or an edge on the border, with their domain and volume indexes
10027 // build the list of nodes shared by 2 or more domains, with their domain indexes
10029 std::map<DownIdType, std::map<int,int>, DownIdCompare> faceDomains; // face --> (id domain --> id volume)
10030 std::map<int,int>celldom; // cell vtkId --> domain
10031 std::map<DownIdType, std::map<int,int>, DownIdCompare> cellDomains; // oldNode --> (id domain --> id cell)
10032 std::map<int, std::map<int,int> > nodeDomains; // oldId --> (domainId --> newId)
10033 faceDomains.clear();
10035 cellDomains.clear();
10036 nodeDomains.clear();
10037 std::map<int,int> emptyMap;
10038 std::set<int> emptySet;
10041 MESSAGE(".. Number of domains :"<<theElems.size());
10043 // Check if the domains do not share an element
10044 for (int idom = 0; idom < theElems.size()-1; idom++)
10046 // MESSAGE("... Check of domain #" << idom);
10047 const TIDSortedElemSet& domain = theElems[idom];
10048 TIDSortedElemSet::const_iterator elemItr = domain.begin();
10049 for (; elemItr != domain.end(); ++elemItr)
10051 SMDS_MeshElement* anElem = (SMDS_MeshElement*) *elemItr;
10052 int idombisdeb = idom + 1 ;
10053 for (int idombis = idombisdeb; idombis < theElems.size(); idombis++) // check if the element belongs to a domain further in the list
10055 const TIDSortedElemSet& domainbis = theElems[idombis];
10056 if ( domainbis.count(anElem) )
10058 MESSAGE(".... Domain #" << idom);
10059 MESSAGE(".... Domain #" << idombis);
10060 throw SALOME_Exception("The domains are not disjoint.");
10067 for (int idom = 0; idom < theElems.size(); idom++)
10070 // --- build a map (face to duplicate --> volume to modify)
10071 // with all the faces shared by 2 domains (group of elements)
10072 // and corresponding volume of this domain, for each shared face.
10073 // a volume has a face shared by 2 domains if it has a neighbor which is not in his domain.
10075 MESSAGE("... Neighbors of domain #" << idom);
10076 const TIDSortedElemSet& domain = theElems[idom];
10077 TIDSortedElemSet::const_iterator elemItr = domain.begin();
10078 for (; elemItr != domain.end(); ++elemItr)
10080 SMDS_MeshElement* anElem = (SMDS_MeshElement*) *elemItr;
10083 int vtkId = anElem->getVtkId();
10084 //MESSAGE(" vtkId " << vtkId << " smdsId " << anElem->GetID());
10085 int neighborsVtkIds[NBMAXNEIGHBORS];
10086 int downIds[NBMAXNEIGHBORS];
10087 unsigned char downTypes[NBMAXNEIGHBORS];
10088 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
10089 for (int n = 0; n < nbNeighbors; n++)
10091 int smdsId = meshDS->fromVtkToSmds(neighborsVtkIds[n]);
10092 const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
10093 if (! domain.count(elem)) // neighbor is in another domain : face is shared
10096 for (int idombis = 0; idombis < theElems.size(); idombis++) // check if the neighbor belongs to another domain of the list
10098 // MESSAGE("Domain " << idombis);
10099 const TIDSortedElemSet& domainbis = theElems[idombis];
10100 if ( domainbis.count(elem)) ok = true ; // neighbor is in a correct domain : face is kept
10102 if ( ok ) // the characteristics of the face is stored
10104 DownIdType face(downIds[n], downTypes[n]);
10105 if (!faceDomains.count(face))
10106 faceDomains[face] = emptyMap; // create an empty entry for face
10107 if (!faceDomains[face].count(idom))
10109 faceDomains[face][idom] = vtkId; // volume associated to face in this domain
10110 celldom[vtkId] = idom;
10111 //MESSAGE(" cell with a border " << vtkId << " domain " << idom);
10119 //MESSAGE("Number of shared faces " << faceDomains.size());
10120 std::map<DownIdType, std::map<int, int>, DownIdCompare>::iterator itface;
10122 // --- explore the shared faces domain by domain,
10123 // explore the nodes of the face and see if they belong to a cell in the domain,
10124 // which has only a node or an edge on the border (not a shared face)
10126 for (int idomain = 0; idomain < theElems.size(); idomain++)
10128 //MESSAGE("Domain " << idomain);
10129 const TIDSortedElemSet& domain = theElems[idomain];
10130 itface = faceDomains.begin();
10131 for (; itface != faceDomains.end(); ++itface)
10133 std::map<int, int> domvol = itface->second;
10134 if (!domvol.count(idomain))
10136 DownIdType face = itface->first;
10137 //MESSAGE(" --- face " << face.cellId);
10138 std::set<int> oldNodes;
10140 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
10141 std::set<int>::iterator itn = oldNodes.begin();
10142 for (; itn != oldNodes.end(); ++itn)
10145 //MESSAGE(" node " << oldId);
10146 vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
10147 for (int i=0; i<l.ncells; i++)
10149 int vtkId = l.cells[i];
10150 const SMDS_MeshElement* anElem = GetMeshDS()->FindElement(GetMeshDS()->fromVtkToSmds(vtkId));
10151 if (!domain.count(anElem))
10153 int vtkType = grid->GetCellType(vtkId);
10154 int downId = grid->CellIdToDownId(vtkId);
10157 MESSAGE("doubleNodesOnGroupBoundaries: internal algorithm problem");
10158 continue; // not OK at this stage of the algorithm:
10159 //no cells created after BuildDownWardConnectivity
10161 DownIdType aCell(downId, vtkType);
10162 if (!cellDomains.count(aCell))
10163 cellDomains[aCell] = emptyMap; // create an empty entry for cell
10164 cellDomains[aCell][idomain] = vtkId;
10165 celldom[vtkId] = idomain;
10166 //MESSAGE(" cell " << vtkId << " domain " << idomain);
10172 // --- explore the shared faces domain by domain, to duplicate the nodes in a coherent way
10173 // for each shared face, get the nodes
10174 // for each node, for each domain of the face, create a clone of the node
10176 // --- edges at the intersection of 3 or 4 domains, with the order of domains to build
10177 // junction elements of type prism or hexa. the key is the pair of nodesId (lower first)
10178 // the value is the ordered domain ids. (more than 4 domains not taken into account)
10180 std::map<std::vector<int>, std::vector<int> > edgesMultiDomains; // nodes of edge --> ordered domains
10181 std::map<int, std::vector<int> > mutipleNodes; // nodes multi domains with domain order
10182 std::map<int, std::vector<int> > mutipleNodesToFace; // nodes multi domains with domain order to transform in Face (junction between 3 or more 2D domains)
10184 MESSAGE(".. Duplication of the nodes");
10185 for (int idomain = 0; idomain < theElems.size(); idomain++)
10187 itface = faceDomains.begin();
10188 for (; itface != faceDomains.end(); ++itface)
10190 std::map<int, int> domvol = itface->second;
10191 if (!domvol.count(idomain))
10193 DownIdType face = itface->first;
10194 //MESSAGE(" --- face " << face.cellId);
10195 std::set<int> oldNodes;
10197 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
10198 std::set<int>::iterator itn = oldNodes.begin();
10199 for (; itn != oldNodes.end(); ++itn)
10202 //MESSAGE("-+-+-a node " << oldId);
10203 if (!nodeDomains.count(oldId))
10204 nodeDomains[oldId] = emptyMap; // create an empty entry for node
10205 if (nodeDomains[oldId].empty())
10207 nodeDomains[oldId][idomain] = oldId; // keep the old node in the first domain
10208 //MESSAGE("-+-+-b oldNode " << oldId << " domain " << idomain);
10210 std::map<int, int>::iterator itdom = domvol.begin();
10211 for (; itdom != domvol.end(); ++itdom)
10213 int idom = itdom->first;
10214 //MESSAGE(" domain " << idom);
10215 if (!nodeDomains[oldId].count(idom)) // --- node to clone
10217 if (nodeDomains[oldId].size() >= 2) // a multiple node
10219 vector<int> orderedDoms;
10220 //MESSAGE("multiple node " << oldId);
10221 if (mutipleNodes.count(oldId))
10222 orderedDoms = mutipleNodes[oldId];
10225 map<int,int>::iterator it = nodeDomains[oldId].begin();
10226 for (; it != nodeDomains[oldId].end(); ++it)
10227 orderedDoms.push_back(it->first);
10229 orderedDoms.push_back(idom); // TODO order ==> push_front or back
10230 //stringstream txt;
10231 //for (int i=0; i<orderedDoms.size(); i++)
10232 // txt << orderedDoms[i] << " ";
10233 //MESSAGE("orderedDoms " << txt.str());
10234 mutipleNodes[oldId] = orderedDoms;
10236 double *coords = grid->GetPoint(oldId);
10237 SMDS_MeshNode *newNode = meshDS->AddNode(coords[0], coords[1], coords[2]);
10238 int newId = newNode->getVtkId();
10239 nodeDomains[oldId][idom] = newId; // cloned node for other domains
10240 //MESSAGE("-+-+-c oldNode " << oldId << " domain " << idomain << " newNode " << newId << " domain " << idom << " size=" <<nodeDomains[oldId].size());
10247 MESSAGE(".. Creation of elements");
10248 for (int idomain = 0; idomain < theElems.size(); idomain++)
10250 itface = faceDomains.begin();
10251 for (; itface != faceDomains.end(); ++itface)
10253 std::map<int, int> domvol = itface->second;
10254 if (!domvol.count(idomain))
10256 DownIdType face = itface->first;
10257 //MESSAGE(" --- face " << face.cellId);
10258 std::set<int> oldNodes;
10260 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
10261 int nbMultipleNodes = 0;
10262 std::set<int>::iterator itn = oldNodes.begin();
10263 for (; itn != oldNodes.end(); ++itn)
10266 if (mutipleNodes.count(oldId))
10269 if (nbMultipleNodes > 1) // check if an edge of the face is shared between 3 or more domains
10271 //MESSAGE("multiple Nodes detected on a shared face");
10272 int downId = itface->first.cellId;
10273 unsigned char cellType = itface->first.cellType;
10274 // --- shared edge or shared face ?
10275 if ((cellType == VTK_LINE) || (cellType == VTK_QUADRATIC_EDGE)) // shared edge (between two faces)
10278 int nbNodes = grid->getDownArray(cellType)->getNodes(downId, nodes);
10279 for (int i=0; i< nbNodes; i=i+nbNodes-1) // i=0 , i=nbNodes-1
10280 if (mutipleNodes.count(nodes[i]))
10281 if (!mutipleNodesToFace.count(nodes[i]))
10282 mutipleNodesToFace[nodes[i]] = mutipleNodes[nodes[i]];
10284 else // shared face (between two volumes)
10286 int nbEdges = grid->getDownArray(cellType)->getNumberOfDownCells(downId);
10287 const int* downEdgeIds = grid->getDownArray(cellType)->getDownCells(downId);
10288 const unsigned char* edgeType = grid->getDownArray(cellType)->getDownTypes(downId);
10289 for (int ie =0; ie < nbEdges; ie++)
10292 int nbNodes = grid->getDownArray(edgeType[ie])->getNodes(downEdgeIds[ie], nodes);
10293 if (mutipleNodes.count(nodes[0]) && mutipleNodes.count(nodes[nbNodes-1]))
10295 vector<int> vn0 = mutipleNodes[nodes[0]];
10296 vector<int> vn1 = mutipleNodes[nodes[nbNodes - 1]];
10298 for (int i0 = 0; i0 < vn0.size(); i0++)
10299 for (int i1 = 0; i1 < vn1.size(); i1++)
10300 if (vn0[i0] == vn1[i1])
10301 doms.push_back(vn0[i0]);
10302 if (doms.size() >2)
10304 //MESSAGE(" detect edgesMultiDomains " << nodes[0] << " " << nodes[nbNodes - 1]);
10305 double *coords = grid->GetPoint(nodes[0]);
10306 gp_Pnt p0(coords[0], coords[1], coords[2]);
10307 coords = grid->GetPoint(nodes[nbNodes - 1]);
10308 gp_Pnt p1(coords[0], coords[1], coords[2]);
10310 int vtkVolIds[1000]; // an edge can belong to a lot of volumes
10311 map<int, SMDS_VtkVolume*> domvol; // domain --> a volume with the edge
10312 map<int, double> angleDom; // oriented angles between planes defined by edge and volume centers
10313 int nbvol = grid->GetParentVolumes(vtkVolIds, downEdgeIds[ie], edgeType[ie]);
10314 for (int id=0; id < doms.size(); id++)
10316 int idom = doms[id];
10317 for (int ivol=0; ivol<nbvol; ivol++)
10319 int smdsId = meshDS->fromVtkToSmds(vtkVolIds[ivol]);
10320 SMDS_MeshElement* elem = (SMDS_MeshElement*)meshDS->FindElement(smdsId);
10321 if (theElems[idom].count(elem))
10323 SMDS_VtkVolume* svol = dynamic_cast<SMDS_VtkVolume*>(elem);
10324 domvol[idom] = svol;
10325 //MESSAGE(" domain " << idom << " volume " << elem->GetID());
10327 vtkIdType npts = 0;
10328 vtkIdType* pts = 0;
10329 grid->GetCellPoints(vtkVolIds[ivol], npts, pts);
10330 SMDS_VtkVolume::gravityCenter(grid, pts, npts, values);
10333 gref.SetXYZ(gp_XYZ(values[0], values[1], values[2]));
10334 angleDom[idom] = 0;
10338 gp_Pnt g(values[0], values[1], values[2]);
10339 angleDom[idom] = OrientedAngle(p0, p1, gref, g); // -pi<angle<+pi
10340 //MESSAGE(" angle=" << angleDom[idom]);
10346 map<double, int> sortedDom; // sort domains by angle
10347 for (map<int, double>::iterator ia = angleDom.begin(); ia != angleDom.end(); ++ia)
10348 sortedDom[ia->second] = ia->first;
10349 vector<int> vnodes;
10351 for (map<double, int>::iterator ib = sortedDom.begin(); ib != sortedDom.end(); ++ib)
10353 vdom.push_back(ib->second);
10354 //MESSAGE(" ordered domain " << ib->second << " angle " << ib->first);
10356 for (int ino = 0; ino < nbNodes; ino++)
10357 vnodes.push_back(nodes[ino]);
10358 edgesMultiDomains[vnodes] = vdom; // nodes vector --> ordered domains
10367 // --- iterate on shared faces (volumes to modify, face to extrude)
10368 // get node id's of the face (id SMDS = id VTK)
10369 // create flat element with old and new nodes if requested
10371 // --- new quad nodes on flat quad elements: oldId --> ((domain1 X domain2) --> newId)
10372 // (domain1 X domain2) = domain1 + MAXINT*domain2
10374 std::map<int, std::map<long,int> > nodeQuadDomains;
10375 std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
10377 MESSAGE(".. Creation of elements: simple junction");
10378 if (createJointElems)
10381 string joints2DName = "joints2D";
10382 mapOfJunctionGroups[joints2DName] = this->myMesh->AddGroup(SMDSAbs_Face, joints2DName.c_str(), idg);
10383 SMESHDS_Group *joints2DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints2DName]->GetGroupDS());
10384 string joints3DName = "joints3D";
10385 mapOfJunctionGroups[joints3DName] = this->myMesh->AddGroup(SMDSAbs_Volume, joints3DName.c_str(), idg);
10386 SMESHDS_Group *joints3DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints3DName]->GetGroupDS());
10388 itface = faceDomains.begin();
10389 for (; itface != faceDomains.end(); ++itface)
10391 DownIdType face = itface->first;
10392 std::set<int> oldNodes;
10393 std::set<int>::iterator itn;
10395 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
10397 std::map<int, int> domvol = itface->second;
10398 std::map<int, int>::iterator itdom = domvol.begin();
10399 int dom1 = itdom->first;
10400 int vtkVolId = itdom->second;
10402 int dom2 = itdom->first;
10403 SMDS_MeshCell *vol = grid->extrudeVolumeFromFace(vtkVolId, dom1, dom2, oldNodes, nodeDomains,
10405 stringstream grpname;
10408 grpname << dom1 << "_" << dom2;
10410 grpname << dom2 << "_" << dom1;
10411 string namegrp = grpname.str();
10412 if (!mapOfJunctionGroups.count(namegrp))
10413 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(vol->GetType(), namegrp.c_str(), idg);
10414 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
10416 sgrp->Add(vol->GetID());
10417 if (vol->GetType() == SMDSAbs_Volume)
10418 joints3DGrp->Add(vol->GetID());
10419 else if (vol->GetType() == SMDSAbs_Face)
10420 joints2DGrp->Add(vol->GetID());
10424 // --- create volumes on multiple domain intersection if requested
10425 // iterate on mutipleNodesToFace
10426 // iterate on edgesMultiDomains
10428 MESSAGE(".. Creation of elements: multiple junction");
10429 if (createJointElems)
10431 // --- iterate on mutipleNodesToFace
10433 std::map<int, std::vector<int> >::iterator itn = mutipleNodesToFace.begin();
10434 for (; itn != mutipleNodesToFace.end(); ++itn)
10436 int node = itn->first;
10437 vector<int> orderDom = itn->second;
10438 vector<vtkIdType> orderedNodes;
10439 for (int idom = 0; idom <orderDom.size(); idom++)
10440 orderedNodes.push_back( nodeDomains[node][orderDom[idom]] );
10441 SMDS_MeshFace* face = this->GetMeshDS()->AddFaceFromVtkIds(orderedNodes);
10443 stringstream grpname;
10445 grpname << 0 << "_" << 0;
10447 string namegrp = grpname.str();
10448 if (!mapOfJunctionGroups.count(namegrp))
10449 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Face, namegrp.c_str(), idg);
10450 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
10452 sgrp->Add(face->GetID());
10455 // --- iterate on edgesMultiDomains
10457 std::map<std::vector<int>, std::vector<int> >::iterator ite = edgesMultiDomains.begin();
10458 for (; ite != edgesMultiDomains.end(); ++ite)
10460 vector<int> nodes = ite->first;
10461 vector<int> orderDom = ite->second;
10462 vector<vtkIdType> orderedNodes;
10463 if (nodes.size() == 2)
10465 //MESSAGE(" use edgesMultiDomains " << nodes[0] << " " << nodes[1]);
10466 for (int ino=0; ino < nodes.size(); ino++)
10467 if (orderDom.size() == 3)
10468 for (int idom = 0; idom <orderDom.size(); idom++)
10469 orderedNodes.push_back( nodeDomains[nodes[ino]][orderDom[idom]] );
10471 for (int idom = orderDom.size()-1; idom >=0; idom--)
10472 orderedNodes.push_back( nodeDomains[nodes[ino]][orderDom[idom]] );
10473 SMDS_MeshVolume* vol = this->GetMeshDS()->AddVolumeFromVtkIds(orderedNodes);
10476 string namegrp = "jointsMultiples";
10477 if (!mapOfJunctionGroups.count(namegrp))
10478 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
10479 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
10481 sgrp->Add(vol->GetID());
10485 INFOS("Quadratic multiple joints not implemented");
10486 // TODO quadratic nodes
10491 // --- list the explicit faces and edges of the mesh that need to be modified,
10492 // i.e. faces and edges built with one or more duplicated nodes.
10493 // associate these faces or edges to their corresponding domain.
10494 // only the first domain found is kept when a face or edge is shared
10496 std::map<DownIdType, std::map<int,int>, DownIdCompare> faceOrEdgeDom; // cellToModify --> (id domain --> id cell)
10497 std::map<int,int> feDom; // vtk id of cell to modify --> id domain
10498 faceOrEdgeDom.clear();
10501 MESSAGE(".. Modification of elements");
10502 for (int idomain = 0; idomain < theElems.size(); idomain++)
10504 std::map<int, std::map<int, int> >::const_iterator itnod = nodeDomains.begin();
10505 for (; itnod != nodeDomains.end(); ++itnod)
10507 int oldId = itnod->first;
10508 //MESSAGE(" node " << oldId);
10509 vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
10510 for (int i = 0; i < l.ncells; i++)
10512 int vtkId = l.cells[i];
10513 int vtkType = grid->GetCellType(vtkId);
10514 int downId = grid->CellIdToDownId(vtkId);
10516 continue; // new cells: not to be modified
10517 DownIdType aCell(downId, vtkType);
10518 int volParents[1000];
10519 int nbvol = grid->GetParentVolumes(volParents, vtkId);
10520 for (int j = 0; j < nbvol; j++)
10521 if (celldom.count(volParents[j]) && (celldom[volParents[j]] == idomain))
10522 if (!feDom.count(vtkId))
10524 feDom[vtkId] = idomain;
10525 faceOrEdgeDom[aCell] = emptyMap;
10526 faceOrEdgeDom[aCell][idomain] = vtkId; // affect face or edge to the first domain only
10527 //MESSAGE("affect cell " << this->GetMeshDS()->fromVtkToSmds(vtkId) << " domain " << idomain
10528 // << " type " << vtkType << " downId " << downId);
10534 // --- iterate on shared faces (volumes to modify, face to extrude)
10535 // get node id's of the face
10536 // replace old nodes by new nodes in volumes, and update inverse connectivity
10538 std::map<DownIdType, std::map<int,int>, DownIdCompare>* maps[3] = {&faceDomains, &cellDomains, &faceOrEdgeDom};
10539 for (int m=0; m<3; m++)
10541 std::map<DownIdType, std::map<int,int>, DownIdCompare>* amap = maps[m];
10542 itface = (*amap).begin();
10543 for (; itface != (*amap).end(); ++itface)
10545 DownIdType face = itface->first;
10546 std::set<int> oldNodes;
10547 std::set<int>::iterator itn;
10549 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
10550 //MESSAGE("examine cell, downId " << face.cellId << " type " << int(face.cellType));
10551 std::map<int, int> localClonedNodeIds;
10553 std::map<int, int> domvol = itface->second;
10554 std::map<int, int>::iterator itdom = domvol.begin();
10555 for (; itdom != domvol.end(); ++itdom)
10557 int idom = itdom->first;
10558 int vtkVolId = itdom->second;
10559 //MESSAGE("modify nodes of cell " << this->GetMeshDS()->fromVtkToSmds(vtkVolId) << " domain " << idom);
10560 localClonedNodeIds.clear();
10561 for (itn = oldNodes.begin(); itn != oldNodes.end(); ++itn)
10564 if (nodeDomains[oldId].count(idom))
10566 localClonedNodeIds[oldId] = nodeDomains[oldId][idom];
10567 //MESSAGE(" node " << oldId << " --> " << localClonedNodeIds[oldId]);
10570 meshDS->ModifyCellNodes(vtkVolId, localClonedNodeIds);
10575 meshDS->CleanDownWardConnectivity(); // Mesh has been modified, downward connectivity is no more usable, free memory
10576 grid->BuildLinks();
10584 * \brief Double nodes on some external faces and create flat elements.
10585 * Flat elements are mainly used by some types of mechanic calculations.
10587 * Each group of the list must be constituted of faces.
10588 * Triangles are transformed in prisms, and quadrangles in hexahedrons.
10589 * @param theElems - list of groups of faces, where a group of faces is a set of
10590 * SMDS_MeshElements sorted by Id.
10591 * @return TRUE if operation has been completed successfully, FALSE otherwise
10593 bool SMESH_MeshEditor::CreateFlatElementsOnFacesGroups(const std::vector<TIDSortedElemSet>& theElems)
10595 MESSAGE("-------------------------------------------------");
10596 MESSAGE("SMESH_MeshEditor::CreateFlatElementsOnFacesGroups");
10597 MESSAGE("-------------------------------------------------");
10599 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
10601 // --- For each group of faces
10602 // duplicate the nodes, create a flat element based on the face
10603 // replace the nodes of the faces by their clones
10605 std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> clonedNodes;
10606 std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> intermediateNodes;
10607 clonedNodes.clear();
10608 intermediateNodes.clear();
10609 std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
10610 mapOfJunctionGroups.clear();
10612 for (int idom = 0; idom < theElems.size(); idom++)
10614 const TIDSortedElemSet& domain = theElems[idom];
10615 TIDSortedElemSet::const_iterator elemItr = domain.begin();
10616 for (; elemItr != domain.end(); ++elemItr)
10618 SMDS_MeshElement* anElem = (SMDS_MeshElement*) *elemItr;
10619 SMDS_MeshFace* aFace = dynamic_cast<SMDS_MeshFace*> (anElem);
10622 // MESSAGE("aFace=" << aFace->GetID());
10623 bool isQuad = aFace->IsQuadratic();
10624 vector<const SMDS_MeshNode*> ln0, ln1, ln2, ln3, ln4;
10626 // --- clone the nodes, create intermediate nodes for non medium nodes of a quad face
10628 SMDS_ElemIteratorPtr nodeIt = aFace->nodesIterator();
10629 while (nodeIt->more())
10631 const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*> (nodeIt->next());
10632 bool isMedium = isQuad && (aFace->IsMediumNode(node));
10634 ln2.push_back(node);
10636 ln0.push_back(node);
10638 const SMDS_MeshNode* clone = 0;
10639 if (!clonedNodes.count(node))
10641 clone = meshDS->AddNode(node->X(), node->Y(), node->Z());
10642 clonedNodes[node] = clone;
10645 clone = clonedNodes[node];
10648 ln3.push_back(clone);
10650 ln1.push_back(clone);
10652 const SMDS_MeshNode* inter = 0;
10653 if (isQuad && (!isMedium))
10655 if (!intermediateNodes.count(node))
10657 inter = meshDS->AddNode(node->X(), node->Y(), node->Z());
10658 intermediateNodes[node] = inter;
10661 inter = intermediateNodes[node];
10662 ln4.push_back(inter);
10666 // --- extrude the face
10668 vector<const SMDS_MeshNode*> ln;
10669 SMDS_MeshVolume* vol = 0;
10670 vtkIdType aType = aFace->GetVtkType();
10674 vol = meshDS->AddVolume(ln0[2], ln0[1], ln0[0], ln1[2], ln1[1], ln1[0]);
10675 // MESSAGE("vol prism " << vol->GetID());
10676 ln.push_back(ln1[0]);
10677 ln.push_back(ln1[1]);
10678 ln.push_back(ln1[2]);
10681 vol = meshDS->AddVolume(ln0[3], ln0[2], ln0[1], ln0[0], ln1[3], ln1[2], ln1[1], ln1[0]);
10682 // MESSAGE("vol hexa " << vol->GetID());
10683 ln.push_back(ln1[0]);
10684 ln.push_back(ln1[1]);
10685 ln.push_back(ln1[2]);
10686 ln.push_back(ln1[3]);
10688 case VTK_QUADRATIC_TRIANGLE:
10689 vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln0[0], ln0[1], ln0[2], ln3[0], ln3[1], ln3[2],
10690 ln2[0], ln2[1], ln2[2], ln4[0], ln4[1], ln4[2]);
10691 // MESSAGE("vol quad prism " << vol->GetID());
10692 ln.push_back(ln1[0]);
10693 ln.push_back(ln1[1]);
10694 ln.push_back(ln1[2]);
10695 ln.push_back(ln3[0]);
10696 ln.push_back(ln3[1]);
10697 ln.push_back(ln3[2]);
10699 case VTK_QUADRATIC_QUAD:
10700 // vol = meshDS->AddVolume(ln0[0], ln0[1], ln0[2], ln0[3], ln1[0], ln1[1], ln1[2], ln1[3],
10701 // ln2[0], ln2[1], ln2[2], ln2[3], ln3[0], ln3[1], ln3[2], ln3[3],
10702 // ln4[0], ln4[1], ln4[2], ln4[3]);
10703 vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln1[3], ln0[0], ln0[1], ln0[2], ln0[3],
10704 ln3[0], ln3[1], ln3[2], ln3[3], ln2[0], ln2[1], ln2[2], ln2[3],
10705 ln4[0], ln4[1], ln4[2], ln4[3]);
10706 // MESSAGE("vol quad hexa " << vol->GetID());
10707 ln.push_back(ln1[0]);
10708 ln.push_back(ln1[1]);
10709 ln.push_back(ln1[2]);
10710 ln.push_back(ln1[3]);
10711 ln.push_back(ln3[0]);
10712 ln.push_back(ln3[1]);
10713 ln.push_back(ln3[2]);
10714 ln.push_back(ln3[3]);
10724 stringstream grpname;
10728 string namegrp = grpname.str();
10729 if (!mapOfJunctionGroups.count(namegrp))
10730 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
10731 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
10733 sgrp->Add(vol->GetID());
10736 // --- modify the face
10738 aFace->ChangeNodes(&ln[0], ln.size());
10745 * \brief identify all the elements around a geom shape, get the faces delimiting the hole
10746 * Build groups of volume to remove, groups of faces to replace on the skin of the object,
10747 * groups of faces to remove inside the object, (idem edges).
10748 * Build ordered list of nodes at the border of each group of faces to replace (to be used to build a geom subshape)
10750 void SMESH_MeshEditor::CreateHoleSkin(double radius,
10751 const TopoDS_Shape& theShape,
10752 SMESH_NodeSearcher* theNodeSearcher,
10753 const char* groupName,
10754 std::vector<double>& nodesCoords,
10755 std::vector<std::vector<int> >& listOfListOfNodes)
10757 MESSAGE("--------------------------------");
10758 MESSAGE("SMESH_MeshEditor::CreateHoleSkin");
10759 MESSAGE("--------------------------------");
10761 // --- zone of volumes to remove is given :
10762 // 1 either by a geom shape (one or more vertices) and a radius,
10763 // 2 either by a group of nodes (representative of the shape)to use with the radius,
10764 // 3 either by a group of nodes where all the elements build on one of this nodes are to remove,
10765 // In the case 2, the group of nodes is an external group of nodes from another mesh,
10766 // In the case 3, the group of nodes is an internal group of the mesh (obtained for instance by a filter),
10767 // defined by it's name.
10769 SMESHDS_GroupBase* groupDS = 0;
10770 SMESH_Mesh::GroupIteratorPtr groupIt = this->myMesh->GetGroups();
10771 while ( groupIt->more() )
10774 SMESH_Group * group = groupIt->next();
10775 if ( !group ) continue;
10776 groupDS = group->GetGroupDS();
10777 if ( !groupDS || groupDS->IsEmpty() ) continue;
10778 std::string grpName = group->GetName();
10779 //MESSAGE("grpName=" << grpName);
10780 if (grpName == groupName)
10786 bool isNodeGroup = false;
10787 bool isNodeCoords = false;
10790 if (groupDS->GetType() != SMDSAbs_Node)
10792 isNodeGroup = true; // a group of nodes exists and it is in this mesh
10795 if (nodesCoords.size() > 0)
10796 isNodeCoords = true; // a list o nodes given by their coordinates
10797 //MESSAGE("---" << isNodeGroup << " " << isNodeCoords);
10799 // --- define groups to build
10801 int idg; // --- group of SMDS volumes
10802 string grpvName = groupName;
10803 grpvName += "_vol";
10804 SMESH_Group *grp = this->myMesh->AddGroup(SMDSAbs_Volume, grpvName.c_str(), idg);
10807 MESSAGE("group not created " << grpvName);
10810 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(grp->GetGroupDS());
10812 int idgs; // --- group of SMDS faces on the skin
10813 string grpsName = groupName;
10814 grpsName += "_skin";
10815 SMESH_Group *grps = this->myMesh->AddGroup(SMDSAbs_Face, grpsName.c_str(), idgs);
10818 MESSAGE("group not created " << grpsName);
10821 SMESHDS_Group *sgrps = dynamic_cast<SMESHDS_Group*>(grps->GetGroupDS());
10823 int idgi; // --- group of SMDS faces internal (several shapes)
10824 string grpiName = groupName;
10825 grpiName += "_internalFaces";
10826 SMESH_Group *grpi = this->myMesh->AddGroup(SMDSAbs_Face, grpiName.c_str(), idgi);
10829 MESSAGE("group not created " << grpiName);
10832 SMESHDS_Group *sgrpi = dynamic_cast<SMESHDS_Group*>(grpi->GetGroupDS());
10834 int idgei; // --- group of SMDS faces internal (several shapes)
10835 string grpeiName = groupName;
10836 grpeiName += "_internalEdges";
10837 SMESH_Group *grpei = this->myMesh->AddGroup(SMDSAbs_Edge, grpeiName.c_str(), idgei);
10840 MESSAGE("group not created " << grpeiName);
10843 SMESHDS_Group *sgrpei = dynamic_cast<SMESHDS_Group*>(grpei->GetGroupDS());
10845 // --- build downward connectivity
10847 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
10848 meshDS->BuildDownWardConnectivity(true);
10849 SMDS_UnstructuredGrid* grid = meshDS->getGrid();
10851 // --- set of volumes detected inside
10853 std::set<int> setOfInsideVol;
10854 std::set<int> setOfVolToCheck;
10856 std::vector<gp_Pnt> gpnts;
10859 if (isNodeGroup) // --- a group of nodes is provided : find all the volumes using one or more of this nodes
10861 MESSAGE("group of nodes provided");
10862 SMDS_ElemIteratorPtr elemIt = groupDS->GetElements();
10863 while ( elemIt->more() )
10865 const SMDS_MeshElement* elem = elemIt->next();
10868 const SMDS_MeshNode* node = dynamic_cast<const SMDS_MeshNode*>(elem);
10871 SMDS_MeshElement* vol = 0;
10872 SMDS_ElemIteratorPtr volItr = node->GetInverseElementIterator(SMDSAbs_Volume);
10873 while (volItr->more())
10875 vol = (SMDS_MeshElement*)volItr->next();
10876 setOfInsideVol.insert(vol->getVtkId());
10877 sgrp->Add(vol->GetID());
10881 else if (isNodeCoords)
10883 MESSAGE("list of nodes coordinates provided");
10886 while (i < nodesCoords.size()-2)
10888 double x = nodesCoords[i++];
10889 double y = nodesCoords[i++];
10890 double z = nodesCoords[i++];
10891 gp_Pnt p = gp_Pnt(x, y ,z);
10892 gpnts.push_back(p);
10893 MESSAGE("TopoDS_Vertex " << k++ << " " << p.X() << " " << p.Y() << " " << p.Z());
10896 else // --- no group, no coordinates : use the vertices of the geom shape provided, and radius
10898 MESSAGE("no group of nodes provided, using vertices from geom shape, and radius");
10899 TopTools_IndexedMapOfShape vertexMap;
10900 TopExp::MapShapes( theShape, TopAbs_VERTEX, vertexMap );
10901 gp_Pnt p = gp_Pnt(0,0,0);
10902 if (vertexMap.Extent() < 1)
10905 for ( int i = 1; i <= vertexMap.Extent(); ++i )
10907 const TopoDS_Vertex& vertex = TopoDS::Vertex( vertexMap( i ));
10908 p = BRep_Tool::Pnt(vertex);
10909 gpnts.push_back(p);
10910 MESSAGE("TopoDS_Vertex " << i << " " << p.X() << " " << p.Y() << " " << p.Z());
10914 if (gpnts.size() > 0)
10917 const SMDS_MeshNode* startNode = theNodeSearcher->FindClosestTo(gpnts[0]);
10919 nodeId = startNode->GetID();
10920 MESSAGE("nodeId " << nodeId);
10922 double radius2 = radius*radius;
10923 MESSAGE("radius2 " << radius2);
10925 // --- volumes on start node
10927 setOfVolToCheck.clear();
10928 SMDS_MeshElement* startVol = 0;
10929 SMDS_ElemIteratorPtr volItr = startNode->GetInverseElementIterator(SMDSAbs_Volume);
10930 while (volItr->more())
10932 startVol = (SMDS_MeshElement*)volItr->next();
10933 setOfVolToCheck.insert(startVol->getVtkId());
10935 if (setOfVolToCheck.empty())
10937 MESSAGE("No volumes found");
10941 // --- starting with central volumes then their neighbors, check if they are inside
10942 // or outside the domain, until no more new neighbor volume is inside.
10943 // Fill the group of inside volumes
10945 std::map<int, double> mapOfNodeDistance2;
10946 mapOfNodeDistance2.clear();
10947 std::set<int> setOfOutsideVol;
10948 while (!setOfVolToCheck.empty())
10950 std::set<int>::iterator it = setOfVolToCheck.begin();
10952 MESSAGE("volume to check, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
10953 bool volInside = false;
10954 vtkIdType npts = 0;
10955 vtkIdType* pts = 0;
10956 grid->GetCellPoints(vtkId, npts, pts);
10957 for (int i=0; i<npts; i++)
10959 double distance2 = 0;
10960 if (mapOfNodeDistance2.count(pts[i]))
10962 distance2 = mapOfNodeDistance2[pts[i]];
10963 MESSAGE("point " << pts[i] << " distance2 " << distance2);
10967 double *coords = grid->GetPoint(pts[i]);
10968 gp_Pnt aPoint = gp_Pnt(coords[0], coords[1], coords[2]);
10970 for (int j=0; j<gpnts.size(); j++)
10972 double d2 = aPoint.SquareDistance(gpnts[j]);
10973 if (d2 < distance2)
10976 if (distance2 < radius2)
10980 mapOfNodeDistance2[pts[i]] = distance2;
10981 MESSAGE(" point " << pts[i] << " distance2 " << distance2 << " coords " << coords[0] << " " << coords[1] << " " << coords[2]);
10983 if (distance2 < radius2)
10985 volInside = true; // one or more nodes inside the domain
10986 sgrp->Add(meshDS->fromVtkToSmds(vtkId));
10992 setOfInsideVol.insert(vtkId);
10993 MESSAGE(" volume inside, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
10994 int neighborsVtkIds[NBMAXNEIGHBORS];
10995 int downIds[NBMAXNEIGHBORS];
10996 unsigned char downTypes[NBMAXNEIGHBORS];
10997 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
10998 for (int n = 0; n < nbNeighbors; n++)
10999 if (!setOfInsideVol.count(neighborsVtkIds[n]) ||setOfOutsideVol.count(neighborsVtkIds[n]))
11000 setOfVolToCheck.insert(neighborsVtkIds[n]);
11004 setOfOutsideVol.insert(vtkId);
11005 MESSAGE(" volume outside, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
11007 setOfVolToCheck.erase(vtkId);
11011 // --- for outside hexahedrons, check if they have more than one neighbor volume inside
11012 // If yes, add the volume to the inside set
11014 bool addedInside = true;
11015 std::set<int> setOfVolToReCheck;
11016 while (addedInside)
11018 MESSAGE(" --------------------------- re check");
11019 addedInside = false;
11020 std::set<int>::iterator itv = setOfInsideVol.begin();
11021 for (; itv != setOfInsideVol.end(); ++itv)
11024 int neighborsVtkIds[NBMAXNEIGHBORS];
11025 int downIds[NBMAXNEIGHBORS];
11026 unsigned char downTypes[NBMAXNEIGHBORS];
11027 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
11028 for (int n = 0; n < nbNeighbors; n++)
11029 if (!setOfInsideVol.count(neighborsVtkIds[n]))
11030 setOfVolToReCheck.insert(neighborsVtkIds[n]);
11032 setOfVolToCheck = setOfVolToReCheck;
11033 setOfVolToReCheck.clear();
11034 while (!setOfVolToCheck.empty())
11036 std::set<int>::iterator it = setOfVolToCheck.begin();
11038 if (grid->GetCellType(vtkId) == VTK_HEXAHEDRON)
11040 MESSAGE("volume to recheck, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
11041 int countInside = 0;
11042 int neighborsVtkIds[NBMAXNEIGHBORS];
11043 int downIds[NBMAXNEIGHBORS];
11044 unsigned char downTypes[NBMAXNEIGHBORS];
11045 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
11046 for (int n = 0; n < nbNeighbors; n++)
11047 if (setOfInsideVol.count(neighborsVtkIds[n]))
11049 MESSAGE("countInside " << countInside);
11050 if (countInside > 1)
11052 MESSAGE(" volume inside, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
11053 setOfInsideVol.insert(vtkId);
11054 sgrp->Add(meshDS->fromVtkToSmds(vtkId));
11055 addedInside = true;
11058 setOfVolToReCheck.insert(vtkId);
11060 setOfVolToCheck.erase(vtkId);
11064 // --- map of Downward faces at the boundary, inside the global volume
11065 // map of Downward faces on the skin of the global volume (equivalent to SMDS faces on the skin)
11066 // fill group of SMDS faces inside the volume (when several volume shapes)
11067 // fill group of SMDS faces on the skin of the global volume (if skin)
11069 std::map<DownIdType, int, DownIdCompare> boundaryFaces; // boundary faces inside the volume --> corresponding cell
11070 std::map<DownIdType, int, DownIdCompare> skinFaces; // faces on the skin of the global volume --> corresponding cell
11071 std::set<int>::iterator it = setOfInsideVol.begin();
11072 for (; it != setOfInsideVol.end(); ++it)
11075 //MESSAGE(" vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
11076 int neighborsVtkIds[NBMAXNEIGHBORS];
11077 int downIds[NBMAXNEIGHBORS];
11078 unsigned char downTypes[NBMAXNEIGHBORS];
11079 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId, true);
11080 for (int n = 0; n < nbNeighbors; n++)
11082 int neighborDim = SMDS_Downward::getCellDimension(grid->GetCellType(neighborsVtkIds[n]));
11083 if (neighborDim == 3)
11085 if (! setOfInsideVol.count(neighborsVtkIds[n])) // neighbor volume is not inside : face is boundary
11087 DownIdType face(downIds[n], downTypes[n]);
11088 boundaryFaces[face] = vtkId;
11090 // if the face between to volumes is in the mesh, get it (internal face between shapes)
11091 int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
11092 if (vtkFaceId >= 0)
11094 sgrpi->Add(meshDS->fromVtkToSmds(vtkFaceId));
11095 // find also the smds edges on this face
11096 int nbEdges = grid->getDownArray(downTypes[n])->getNumberOfDownCells(downIds[n]);
11097 const int* dEdges = grid->getDownArray(downTypes[n])->getDownCells(downIds[n]);
11098 const unsigned char* dTypes = grid->getDownArray(downTypes[n])->getDownTypes(downIds[n]);
11099 for (int i = 0; i < nbEdges; i++)
11101 int vtkEdgeId = grid->getDownArray(dTypes[i])->getVtkCellId(dEdges[i]);
11102 if (vtkEdgeId >= 0)
11103 sgrpei->Add(meshDS->fromVtkToSmds(vtkEdgeId));
11107 else if (neighborDim == 2) // skin of the volume
11109 DownIdType face(downIds[n], downTypes[n]);
11110 skinFaces[face] = vtkId;
11111 int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
11112 if (vtkFaceId >= 0)
11113 sgrps->Add(meshDS->fromVtkToSmds(vtkFaceId));
11118 // --- identify the edges constituting the wire of each subshape on the skin
11119 // define polylines with the nodes of edges, equivalent to wires
11120 // project polylines on subshapes, and partition, to get geom faces
11122 std::map<int, std::set<int> > shapeIdToVtkIdSet; // shapeId --> set of vtkId on skin
11123 std::set<int> emptySet;
11125 std::set<int> shapeIds;
11127 SMDS_ElemIteratorPtr itelem = sgrps->GetElements();
11128 while (itelem->more())
11130 const SMDS_MeshElement *elem = itelem->next();
11131 int shapeId = elem->getshapeId();
11132 int vtkId = elem->getVtkId();
11133 if (!shapeIdToVtkIdSet.count(shapeId))
11135 shapeIdToVtkIdSet[shapeId] = emptySet;
11136 shapeIds.insert(shapeId);
11138 shapeIdToVtkIdSet[shapeId].insert(vtkId);
11141 std::map<int, std::set<DownIdType, DownIdCompare> > shapeIdToEdges; // shapeId --> set of downward edges
11142 std::set<DownIdType, DownIdCompare> emptyEdges;
11143 emptyEdges.clear();
11145 std::map<int, std::set<int> >::iterator itShape = shapeIdToVtkIdSet.begin();
11146 for (; itShape != shapeIdToVtkIdSet.end(); ++itShape)
11148 int shapeId = itShape->first;
11149 MESSAGE(" --- Shape ID --- "<< shapeId);
11150 shapeIdToEdges[shapeId] = emptyEdges;
11152 std::vector<int> nodesEdges;
11154 std::set<int>::iterator its = itShape->second.begin();
11155 for (; its != itShape->second.end(); ++its)
11158 MESSAGE(" " << vtkId);
11159 int neighborsVtkIds[NBMAXNEIGHBORS];
11160 int downIds[NBMAXNEIGHBORS];
11161 unsigned char downTypes[NBMAXNEIGHBORS];
11162 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
11163 for (int n = 0; n < nbNeighbors; n++)
11165 if (neighborsVtkIds[n]<0) // only smds faces are considered as neighbors here
11167 int smdsId = meshDS->fromVtkToSmds(neighborsVtkIds[n]);
11168 const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
11169 if ( shapeIds.count(elem->getshapeId()) && !sgrps->Contains(elem)) // edge : neighbor in the set of shape, not in the group
11171 DownIdType edge(downIds[n], downTypes[n]);
11172 if (!shapeIdToEdges[shapeId].count(edge))
11174 shapeIdToEdges[shapeId].insert(edge);
11176 int nbNodes = grid->getDownArray(downTypes[n])->getNodes(downIds[n],vtkNodeId);
11177 nodesEdges.push_back(vtkNodeId[0]);
11178 nodesEdges.push_back(vtkNodeId[nbNodes-1]);
11179 MESSAGE(" --- nodes " << vtkNodeId[0]+1 << " " << vtkNodeId[nbNodes-1]+1);
11185 std::list<int> order;
11187 if (nodesEdges.size() > 0)
11189 order.push_back(nodesEdges[0]); MESSAGE(" --- back " << order.back()+1); // SMDS id = VTK id + 1;
11190 nodesEdges[0] = -1;
11191 order.push_back(nodesEdges[1]); MESSAGE(" --- back " << order.back()+1);
11192 nodesEdges[1] = -1; // do not reuse this edge
11196 int nodeTofind = order.back(); // try first to push back
11198 for (i = 0; i<nodesEdges.size(); i++)
11199 if (nodesEdges[i] == nodeTofind)
11201 if (i == nodesEdges.size())
11202 found = false; // no follower found on back
11205 if (i%2) // odd ==> use the previous one
11206 if (nodesEdges[i-1] < 0)
11210 order.push_back(nodesEdges[i-1]); MESSAGE(" --- back " << order.back()+1);
11211 nodesEdges[i-1] = -1;
11213 else // even ==> use the next one
11214 if (nodesEdges[i+1] < 0)
11218 order.push_back(nodesEdges[i+1]); MESSAGE(" --- back " << order.back()+1);
11219 nodesEdges[i+1] = -1;
11224 // try to push front
11226 nodeTofind = order.front(); // try to push front
11227 for (i = 0; i<nodesEdges.size(); i++)
11228 if (nodesEdges[i] == nodeTofind)
11230 if (i == nodesEdges.size())
11232 found = false; // no predecessor found on front
11235 if (i%2) // odd ==> use the previous one
11236 if (nodesEdges[i-1] < 0)
11240 order.push_front(nodesEdges[i-1]); MESSAGE(" --- front " << order.front()+1);
11241 nodesEdges[i-1] = -1;
11243 else // even ==> use the next one
11244 if (nodesEdges[i+1] < 0)
11248 order.push_front(nodesEdges[i+1]); MESSAGE(" --- front " << order.front()+1);
11249 nodesEdges[i+1] = -1;
11255 std::vector<int> nodes;
11256 nodes.push_back(shapeId);
11257 std::list<int>::iterator itl = order.begin();
11258 for (; itl != order.end(); itl++)
11260 nodes.push_back((*itl) + 1); // SMDS id = VTK id + 1;
11261 MESSAGE(" ordered node " << nodes[nodes.size()-1]);
11263 listOfListOfNodes.push_back(nodes);
11266 // partition geom faces with blocFissure
11267 // mesh blocFissure and geom faces of the skin (external wires given, triangle algo to choose)
11268 // mesh volume around blocFissure (skin triangles and quadrangle given, tetra algo to choose)
11274 //================================================================================
11276 * \brief Generates skin mesh (containing 2D cells) from 3D mesh
11277 * The created 2D mesh elements based on nodes of free faces of boundary volumes
11278 * \return TRUE if operation has been completed successfully, FALSE otherwise
11280 //================================================================================
11282 bool SMESH_MeshEditor::Make2DMeshFrom3D()
11284 // iterates on volume elements and detect all free faces on them
11285 SMESHDS_Mesh* aMesh = GetMeshDS();
11288 //bool res = false;
11289 int nbFree = 0, nbExisted = 0, nbCreated = 0;
11290 SMDS_VolumeIteratorPtr vIt = aMesh->volumesIterator();
11293 const SMDS_MeshVolume* volume = vIt->next();
11294 SMDS_VolumeTool vTool( volume, /*ignoreCentralNodes=*/false );
11295 vTool.SetExternalNormal();
11296 //const bool isPoly = volume->IsPoly();
11297 const int iQuad = volume->IsQuadratic();
11298 for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
11300 if (!vTool.IsFreeFace(iface))
11303 vector<const SMDS_MeshNode *> nodes;
11304 int nbFaceNodes = vTool.NbFaceNodes(iface);
11305 const SMDS_MeshNode** faceNodes = vTool.GetFaceNodes(iface);
11307 for ( ; inode < nbFaceNodes; inode += iQuad+1)
11308 nodes.push_back(faceNodes[inode]);
11309 if (iQuad) { // add medium nodes
11310 for ( inode = 1; inode < nbFaceNodes; inode += 2)
11311 nodes.push_back(faceNodes[inode]);
11312 if ( nbFaceNodes == 9 ) // bi-quadratic quad
11313 nodes.push_back(faceNodes[8]);
11315 // add new face based on volume nodes
11316 if (aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false) ) {
11318 continue; // face already exsist
11320 AddElement(nodes, SMDSAbs_Face, ( !iQuad && nbFaceNodes/(iQuad+1) > 4 ));
11324 return ( nbFree==(nbExisted+nbCreated) );
11329 inline const SMDS_MeshNode* getNodeWithSameID(SMESHDS_Mesh* mesh, const SMDS_MeshNode* node)
11331 if ( const SMDS_MeshNode* n = mesh->FindNode( node->GetID() ))
11333 return mesh->AddNodeWithID( node->X(),node->Y(),node->Z(), node->GetID() );
11336 //================================================================================
11338 * \brief Creates missing boundary elements
11339 * \param elements - elements whose boundary is to be checked
11340 * \param dimension - defines type of boundary elements to create
11341 * \param group - a group to store created boundary elements in
11342 * \param targetMesh - a mesh to store created boundary elements in
11343 * \param toCopyElements - if true, the checked elements will be copied into the targetMesh
11344 * \param toCopyExistingBoundary - if true, not only new but also pre-existing
11345 * boundary elements will be copied into the targetMesh
11346 * \param toAddExistingBondary - if true, not only new but also pre-existing
11347 * boundary elements will be added into the new group
11348 * \param aroundElements - if true, elements will be created on boundary of given
11349 * elements else, on boundary of the whole mesh.
11350 * \return nb of added boundary elements
11352 //================================================================================
11354 int SMESH_MeshEditor::MakeBoundaryMesh(const TIDSortedElemSet& elements,
11355 Bnd_Dimension dimension,
11356 SMESH_Group* group/*=0*/,
11357 SMESH_Mesh* targetMesh/*=0*/,
11358 bool toCopyElements/*=false*/,
11359 bool toCopyExistingBoundary/*=false*/,
11360 bool toAddExistingBondary/*= false*/,
11361 bool aroundElements/*= false*/)
11363 SMDSAbs_ElementType missType = (dimension == BND_2DFROM3D) ? SMDSAbs_Face : SMDSAbs_Edge;
11364 SMDSAbs_ElementType elemType = (dimension == BND_1DFROM2D) ? SMDSAbs_Face : SMDSAbs_Volume;
11365 // hope that all elements are of the same type, do not check them all
11366 if ( !elements.empty() && (*elements.begin())->GetType() != elemType )
11367 throw SALOME_Exception(LOCALIZED("wrong element type"));
11370 toCopyElements = toCopyExistingBoundary = false;
11372 SMESH_MeshEditor tgtEditor( targetMesh ? targetMesh : myMesh );
11373 SMESHDS_Mesh* aMesh = GetMeshDS(), *tgtMeshDS = tgtEditor.GetMeshDS();
11374 int nbAddedBnd = 0;
11376 // editor adding present bnd elements and optionally holding elements to add to the group
11377 SMESH_MeshEditor* presentEditor;
11378 SMESH_MeshEditor tgtEditor2( tgtEditor.GetMesh() );
11379 presentEditor = toAddExistingBondary ? &tgtEditor : &tgtEditor2;
11381 SMESH_MesherHelper helper( *myMesh );
11382 const TopAbs_ShapeEnum missShapeType = ( missType==SMDSAbs_Face ? TopAbs_FACE : TopAbs_EDGE );
11383 SMDS_VolumeTool vTool;
11384 TIDSortedElemSet avoidSet;
11385 const TIDSortedElemSet emptySet, *elemSet = aroundElements ? &elements : &emptySet;
11388 typedef vector<const SMDS_MeshNode*> TConnectivity;
11390 SMDS_ElemIteratorPtr eIt;
11391 if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
11392 else eIt = elemSetIterator( elements );
11394 while (eIt->more())
11396 const SMDS_MeshElement* elem = eIt->next();
11397 const int iQuad = elem->IsQuadratic();
11399 // ------------------------------------------------------------------------------------
11400 // 1. For an elem, get present bnd elements and connectivities of missing bnd elements
11401 // ------------------------------------------------------------------------------------
11402 vector<const SMDS_MeshElement*> presentBndElems;
11403 vector<TConnectivity> missingBndElems;
11404 TConnectivity nodes, elemNodes;
11405 if ( vTool.Set(elem, /*ignoreCentralNodes=*/true) ) // elem is a volume --------------
11407 vTool.SetExternalNormal();
11408 const SMDS_MeshElement* otherVol = 0;
11409 for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
11411 if ( !vTool.IsFreeFace(iface, &otherVol) &&
11412 ( !aroundElements || elements.count( otherVol )))
11414 const SMDS_MeshNode** nn = vTool.GetFaceNodes(iface);
11415 const int nbFaceNodes = vTool.NbFaceNodes (iface);
11416 if ( missType == SMDSAbs_Edge ) // boundary edges
11418 nodes.resize( 2+iQuad );
11419 for ( int i = 0; i < nbFaceNodes; i += 1+iQuad)
11421 for ( int j = 0; j < nodes.size(); ++j )
11423 if ( const SMDS_MeshElement* edge =
11424 aMesh->FindElement(nodes,SMDSAbs_Edge,/*noMedium=*/false))
11425 presentBndElems.push_back( edge );
11427 missingBndElems.push_back( nodes );
11430 else // boundary face
11433 for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
11434 nodes.push_back( nn[inode] ); // add corner nodes
11436 for ( inode = 1; inode < nbFaceNodes; inode += 2)
11437 nodes.push_back( nn[inode] ); // add medium nodes
11438 int iCenter = vTool.GetCenterNodeIndex(iface); // for HEX27
11440 nodes.push_back( vTool.GetNodes()[ iCenter ] );
11442 if (const SMDS_MeshElement * f = aMesh->FindElement( nodes,
11443 SMDSAbs_Face, /*noMedium=*/false ))
11444 presentBndElems.push_back( f );
11446 missingBndElems.push_back( nodes );
11448 if ( targetMesh != myMesh )
11450 // add 1D elements on face boundary to be added to a new mesh
11451 const SMDS_MeshElement* edge;
11452 for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
11455 edge = aMesh->FindEdge( nn[inode], nn[inode+1], nn[inode+2]);
11457 edge = aMesh->FindEdge( nn[inode], nn[inode+1]);
11458 if ( edge && avoidSet.insert( edge ).second )
11459 presentBndElems.push_back( edge );
11465 else if ( elem->GetType() == SMDSAbs_Face ) // elem is a face ------------------------
11467 avoidSet.clear(), avoidSet.insert( elem );
11468 elemNodes.assign( SMDS_MeshElement::iterator( elem->interlacedNodesElemIterator() ),
11469 SMDS_MeshElement::iterator() );
11470 elemNodes.push_back( elemNodes[0] );
11471 nodes.resize( 2 + iQuad );
11472 const int nbLinks = elem->NbCornerNodes();
11473 for ( int i = 0, iN = 0; i < nbLinks; i++, iN += 1+iQuad )
11475 nodes[0] = elemNodes[iN];
11476 nodes[1] = elemNodes[iN+1+iQuad];
11477 if ( SMESH_MeshAlgos::FindFaceInSet( nodes[0], nodes[1], *elemSet, avoidSet))
11478 continue; // not free link
11480 if ( iQuad ) nodes[2] = elemNodes[iN+1];
11481 if ( const SMDS_MeshElement* edge =
11482 aMesh->FindElement(nodes,SMDSAbs_Edge,/*noMedium=*/false))
11483 presentBndElems.push_back( edge );
11485 missingBndElems.push_back( nodes );
11489 // ---------------------------------
11490 // 2. Add missing boundary elements
11491 // ---------------------------------
11492 if ( targetMesh != myMesh )
11493 // instead of making a map of nodes in this mesh and targetMesh,
11494 // we create nodes with same IDs.
11495 for ( int i = 0; i < missingBndElems.size(); ++i )
11497 TConnectivity& srcNodes = missingBndElems[i];
11498 TConnectivity nodes( srcNodes.size() );
11499 for ( inode = 0; inode < nodes.size(); ++inode )
11500 nodes[inode] = getNodeWithSameID( tgtMeshDS, srcNodes[inode] );
11501 if ( aroundElements && tgtEditor.GetMeshDS()->FindElement( nodes,
11503 /*noMedium=*/false))
11505 tgtEditor.AddElement(nodes, missType, !iQuad && nodes.size()/(iQuad+1)>4);
11509 for ( int i = 0; i < missingBndElems.size(); ++i )
11511 TConnectivity& nodes = missingBndElems[i];
11512 if ( aroundElements && tgtEditor.GetMeshDS()->FindElement( nodes,
11514 /*noMedium=*/false))
11516 SMDS_MeshElement* elem =
11517 tgtEditor.AddElement(nodes, missType, !iQuad && nodes.size()/(iQuad+1)>4);
11520 // try to set a new element to a shape
11521 if ( myMesh->HasShapeToMesh() )
11524 set< pair<TopAbs_ShapeEnum, int > > mediumShapes;
11525 const int nbN = nodes.size() / (iQuad+1 );
11526 for ( inode = 0; inode < nbN && ok; ++inode )
11528 pair<int, TopAbs_ShapeEnum> i_stype =
11529 helper.GetMediumPos( nodes[inode], nodes[(inode+1)%nbN]);
11530 if (( ok = ( i_stype.first > 0 && i_stype.second >= TopAbs_FACE )))
11531 mediumShapes.insert( make_pair ( i_stype.second, i_stype.first ));
11533 if ( ok && mediumShapes.size() > 1 )
11535 set< pair<TopAbs_ShapeEnum, int > >::iterator stype_i = mediumShapes.begin();
11536 pair<TopAbs_ShapeEnum, int> stype_i_0 = *stype_i;
11537 for ( ++stype_i; stype_i != mediumShapes.end() && ok; ++stype_i )
11539 if (( ok = ( stype_i->first != stype_i_0.first )))
11540 ok = helper.IsSubShape( aMesh->IndexToShape( stype_i->second ),
11541 aMesh->IndexToShape( stype_i_0.second ));
11544 if ( ok && mediumShapes.begin()->first == missShapeType )
11545 aMesh->SetMeshElementOnShape( elem, mediumShapes.begin()->second );
11549 // ----------------------------------
11550 // 3. Copy present boundary elements
11551 // ----------------------------------
11552 if ( toCopyExistingBoundary )
11553 for ( int i = 0 ; i < presentBndElems.size(); ++i )
11555 const SMDS_MeshElement* e = presentBndElems[i];
11556 TConnectivity nodes( e->NbNodes() );
11557 for ( inode = 0; inode < nodes.size(); ++inode )
11558 nodes[inode] = getNodeWithSameID( tgtMeshDS, e->GetNode(inode) );
11559 presentEditor->AddElement(nodes, e->GetType(), e->IsPoly());
11561 else // store present elements to add them to a group
11562 for ( int i = 0 ; i < presentBndElems.size(); ++i )
11564 presentEditor->myLastCreatedElems.Append(presentBndElems[i]);
11567 } // loop on given elements
11569 // ---------------------------------------------
11570 // 4. Fill group with boundary elements
11571 // ---------------------------------------------
11574 if ( SMESHDS_Group* g = dynamic_cast<SMESHDS_Group*>( group->GetGroupDS() ))
11575 for ( int i = 0; i < tgtEditor.myLastCreatedElems.Size(); ++i )
11576 g->SMDSGroup().Add( tgtEditor.myLastCreatedElems( i+1 ));
11578 tgtEditor.myLastCreatedElems.Clear();
11579 tgtEditor2.myLastCreatedElems.Clear();
11581 // -----------------------
11582 // 5. Copy given elements
11583 // -----------------------
11584 if ( toCopyElements && targetMesh != myMesh )
11586 if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
11587 else eIt = elemSetIterator( elements );
11588 while (eIt->more())
11590 const SMDS_MeshElement* elem = eIt->next();
11591 TConnectivity nodes( elem->NbNodes() );
11592 for ( inode = 0; inode < nodes.size(); ++inode )
11593 nodes[inode] = getNodeWithSameID( tgtMeshDS, elem->GetNode(inode) );
11594 tgtEditor.AddElement(nodes, elemType, elem->IsPoly());
11596 tgtEditor.myLastCreatedElems.Clear();