1 // Copyright (C) 2007-2014 CEA/DEN, EDF R&D, OPEN CASCADE
3 // Copyright (C) 2003-2007 OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN,
4 // CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS
6 // This library is free software; you can redistribute it and/or
7 // modify it under the terms of the GNU Lesser General Public
8 // License as published by the Free Software Foundation; either
9 // version 2.1 of the License, or (at your option) any later version.
11 // This library is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 // Lesser General Public License for more details.
16 // You should have received a copy of the GNU Lesser General Public
17 // License along with this library; if not, write to the Free Software
18 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
23 // File : SMESH_MeshEditor.cxx
24 // Created : Mon Apr 12 16:10:22 2004
25 // Author : Edward AGAPOV (eap)
27 #include "SMESH_MeshEditor.hxx"
29 #include "SMDS_FaceOfNodes.hxx"
30 #include "SMDS_VolumeTool.hxx"
31 #include "SMDS_EdgePosition.hxx"
32 #include "SMDS_FacePosition.hxx"
33 #include "SMDS_SpacePosition.hxx"
34 #include "SMDS_MeshGroup.hxx"
35 #include "SMDS_LinearEdge.hxx"
36 #include "SMDS_Downward.hxx"
37 #include "SMDS_SetIterator.hxx"
39 #include "SMESHDS_Group.hxx"
40 #include "SMESHDS_Mesh.hxx"
42 #include "SMESH_Algo.hxx"
43 #include "SMESH_ControlsDef.hxx"
44 #include "SMESH_Group.hxx"
45 #include "SMESH_MeshAlgos.hxx"
46 #include "SMESH_MesherHelper.hxx"
47 #include "SMESH_OctreeNode.hxx"
48 #include "SMESH_subMesh.hxx"
50 #include <Basics_OCCTVersion.hxx>
52 #include "utilities.h"
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 template < class ELEM_SET >
109 SMDS_ElemIteratorPtr elemSetIterator( const ELEM_SET& elements )
111 typedef SMDS_SetIterator
112 < SMDS_pElement, typename ELEM_SET::const_iterator> TSetIterator;
113 return SMDS_ElemIteratorPtr( new TSetIterator( elements.begin(), elements.end() ));
117 //=======================================================================
118 //function : SMESH_MeshEditor
120 //=======================================================================
122 SMESH_MeshEditor::SMESH_MeshEditor( SMESH_Mesh* theMesh )
123 :myMesh( theMesh ) // theMesh may be NULL
127 //================================================================================
129 * \brief Clears myLastCreatedNodes and myLastCreatedElems
131 //================================================================================
133 void SMESH_MeshEditor::CrearLastCreated()
135 myLastCreatedNodes.Clear();
136 myLastCreatedElems.Clear();
140 //=======================================================================
144 //=======================================================================
147 SMESH_MeshEditor::AddElement(const vector<const SMDS_MeshNode*> & node,
148 const SMDSAbs_ElementType type,
151 const double ballDiameter)
153 //MESSAGE("AddElement " <<node.size() << " " << type << " " << isPoly << " " << ID);
154 SMDS_MeshElement* e = 0;
155 int nbnode = node.size();
156 SMESHDS_Mesh* mesh = GetMeshDS();
161 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], ID);
162 else e = mesh->AddFace (node[0], node[1], node[2] );
164 else if (nbnode == 4) {
165 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3], ID);
166 else e = mesh->AddFace (node[0], node[1], node[2], node[3] );
168 else if (nbnode == 6) {
169 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
170 node[4], node[5], ID);
171 else e = mesh->AddFace (node[0], node[1], node[2], node[3],
174 else if (nbnode == 7) {
175 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
176 node[4], node[5], node[6], ID);
177 else e = mesh->AddFace (node[0], node[1], node[2], node[3],
178 node[4], node[5], node[6] );
180 else if (nbnode == 8) {
181 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
182 node[4], node[5], node[6], node[7], ID);
183 else e = mesh->AddFace (node[0], node[1], node[2], node[3],
184 node[4], node[5], node[6], node[7] );
186 else if (nbnode == 9) {
187 if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3],
188 node[4], node[5], node[6], node[7], node[8], ID);
189 else e = mesh->AddFace (node[0], node[1], node[2], node[3],
190 node[4], node[5], node[6], node[7], node[8] );
193 if ( ID >= 1 ) e = mesh->AddPolygonalFaceWithID(node, ID);
194 else e = mesh->AddPolygonalFace (node );
201 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3], ID);
202 else e = mesh->AddVolume (node[0], node[1], node[2], node[3] );
204 else if (nbnode == 5) {
205 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
207 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
210 else if (nbnode == 6) {
211 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
212 node[4], node[5], ID);
213 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
216 else if (nbnode == 8) {
217 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
218 node[4], node[5], node[6], node[7], ID);
219 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
220 node[4], node[5], node[6], node[7] );
222 else if (nbnode == 10) {
223 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
224 node[4], node[5], node[6], node[7],
225 node[8], node[9], ID);
226 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
227 node[4], node[5], node[6], node[7],
230 else if (nbnode == 12) {
231 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
232 node[4], node[5], node[6], node[7],
233 node[8], node[9], node[10], node[11], ID);
234 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
235 node[4], node[5], node[6], node[7],
236 node[8], node[9], node[10], node[11] );
238 else if (nbnode == 13) {
239 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
240 node[4], node[5], node[6], node[7],
241 node[8], node[9], node[10],node[11],
243 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
244 node[4], node[5], node[6], node[7],
245 node[8], node[9], node[10],node[11],
248 else if (nbnode == 15) {
249 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
250 node[4], node[5], node[6], node[7],
251 node[8], node[9], node[10],node[11],
252 node[12],node[13],node[14],ID);
253 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
254 node[4], node[5], node[6], node[7],
255 node[8], node[9], node[10],node[11],
256 node[12],node[13],node[14] );
258 else if (nbnode == 20) {
259 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
260 node[4], node[5], node[6], node[7],
261 node[8], node[9], node[10],node[11],
262 node[12],node[13],node[14],node[15],
263 node[16],node[17],node[18],node[19],ID);
264 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
265 node[4], node[5], node[6], node[7],
266 node[8], node[9], node[10],node[11],
267 node[12],node[13],node[14],node[15],
268 node[16],node[17],node[18],node[19] );
270 else if (nbnode == 27) {
271 if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3],
272 node[4], node[5], node[6], node[7],
273 node[8], node[9], node[10],node[11],
274 node[12],node[13],node[14],node[15],
275 node[16],node[17],node[18],node[19],
276 node[20],node[21],node[22],node[23],
277 node[24],node[25],node[26], ID);
278 else e = mesh->AddVolume (node[0], node[1], node[2], node[3],
279 node[4], node[5], node[6], node[7],
280 node[8], node[9], node[10],node[11],
281 node[12],node[13],node[14],node[15],
282 node[16],node[17],node[18],node[19],
283 node[20],node[21],node[22],node[23],
284 node[24],node[25],node[26] );
291 if ( ID >= 1 ) e = mesh->AddEdgeWithID(node[0], node[1], ID);
292 else e = mesh->AddEdge (node[0], node[1] );
294 else if ( nbnode == 3 ) {
295 if ( ID >= 1 ) e = mesh->AddEdgeWithID(node[0], node[1], node[2], ID);
296 else e = mesh->AddEdge (node[0], node[1], node[2] );
300 case SMDSAbs_0DElement:
302 if ( ID >= 1 ) e = mesh->Add0DElementWithID(node[0], ID);
303 else e = mesh->Add0DElement (node[0] );
308 if ( ID >= 1 ) e = mesh->AddNodeWithID(node[0]->X(), node[0]->Y(), node[0]->Z(), ID);
309 else e = mesh->AddNode (node[0]->X(), node[0]->Y(), node[0]->Z());
313 if ( ID >= 1 ) e = mesh->AddBallWithID(node[0], ballDiameter, ID);
314 else e = mesh->AddBall (node[0], ballDiameter);
319 if ( e ) myLastCreatedElems.Append( e );
323 //=======================================================================
327 //=======================================================================
329 SMDS_MeshElement* SMESH_MeshEditor::AddElement(const vector<int> & nodeIDs,
330 const SMDSAbs_ElementType type,
334 vector<const SMDS_MeshNode*> nodes;
335 nodes.reserve( nodeIDs.size() );
336 vector<int>::const_iterator id = nodeIDs.begin();
337 while ( id != nodeIDs.end() ) {
338 if ( const SMDS_MeshNode* node = GetMeshDS()->FindNode( *id++ ))
339 nodes.push_back( node );
343 return AddElement( nodes, type, isPoly, ID );
346 //=======================================================================
348 //purpose : Remove a node or an element.
349 // Modify a compute state of sub-meshes which become empty
350 //=======================================================================
352 int SMESH_MeshEditor::Remove (const list< int >& theIDs,
355 myLastCreatedElems.Clear();
356 myLastCreatedNodes.Clear();
358 SMESHDS_Mesh* aMesh = GetMeshDS();
359 set< SMESH_subMesh *> smmap;
362 list<int>::const_iterator it = theIDs.begin();
363 for ( ; it != theIDs.end(); it++ ) {
364 const SMDS_MeshElement * elem;
366 elem = aMesh->FindNode( *it );
368 elem = aMesh->FindElement( *it );
372 // Notify VERTEX sub-meshes about modification
374 const SMDS_MeshNode* node = cast2Node( elem );
375 if ( node->GetPosition()->GetTypeOfPosition() == SMDS_TOP_VERTEX )
376 if ( int aShapeID = node->getshapeId() )
377 if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( aShapeID ) )
380 // Find sub-meshes to notify about modification
381 // SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
382 // while ( nodeIt->more() ) {
383 // const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
384 // const SMDS_PositionPtr& aPosition = node->GetPosition();
385 // if ( aPosition.get() ) {
386 // if ( int aShapeID = aPosition->GetShapeId() ) {
387 // if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( aShapeID ) )
388 // smmap.insert( sm );
395 aMesh->RemoveNode( static_cast< const SMDS_MeshNode* >( elem ));
397 aMesh->RemoveElement( elem );
401 // Notify sub-meshes about modification
402 if ( !smmap.empty() ) {
403 set< SMESH_subMesh *>::iterator smIt;
404 for ( smIt = smmap.begin(); smIt != smmap.end(); smIt++ )
405 (*smIt)->ComputeStateEngine( SMESH_subMesh::MESH_ENTITY_REMOVED );
408 // // Check if the whole mesh becomes empty
409 // if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( 1 ) )
410 // sm->ComputeStateEngine( SMESH_subMesh::CHECK_COMPUTE_STATE );
415 //================================================================================
417 * \brief Create 0D elements on all nodes of the given object except those
418 * nodes on which a 0D element already exists.
419 * \param elements - Elements on whose nodes to create 0D elements; if empty,
420 * the all mesh is treated
421 * \param all0DElems - returns all 0D elements found or created on nodes of \a elements
423 //================================================================================
425 void SMESH_MeshEditor::Create0DElementsOnAllNodes( const TIDSortedElemSet& elements,
426 TIDSortedElemSet& all0DElems )
428 SMDS_ElemIteratorPtr elemIt;
429 vector< const SMDS_MeshElement* > allNodes;
430 if ( elements.empty() )
432 allNodes.reserve( GetMeshDS()->NbNodes() );
433 elemIt = GetMeshDS()->elementsIterator( SMDSAbs_Node );
434 while ( elemIt->more() )
435 allNodes.push_back( elemIt->next() );
437 elemIt = elemSetIterator( allNodes );
441 elemIt = elemSetIterator( elements );
444 while ( elemIt->more() )
446 const SMDS_MeshElement* e = elemIt->next();
447 SMDS_ElemIteratorPtr nodeIt = e->nodesIterator();
448 while ( nodeIt->more() )
450 const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
451 SMDS_ElemIteratorPtr it0D = n->GetInverseElementIterator( SMDSAbs_0DElement );
453 all0DElems.insert( it0D->next() );
455 myLastCreatedElems.Append( GetMeshDS()->Add0DElement( n ));
456 all0DElems.insert( myLastCreatedElems.Last() );
462 //=======================================================================
463 //function : FindShape
464 //purpose : Return an index of the shape theElem is on
465 // or zero if a shape not found
466 //=======================================================================
468 int SMESH_MeshEditor::FindShape (const SMDS_MeshElement * theElem)
470 myLastCreatedElems.Clear();
471 myLastCreatedNodes.Clear();
473 SMESHDS_Mesh * aMesh = GetMeshDS();
474 if ( aMesh->ShapeToMesh().IsNull() )
477 int aShapeID = theElem->getshapeId();
481 if ( SMESHDS_SubMesh * sm = aMesh->MeshElements( aShapeID ))
482 if ( sm->Contains( theElem ))
485 if ( theElem->GetType() == SMDSAbs_Node ) {
486 MESSAGE( ":( Error: invalid myShapeId of node " << theElem->GetID() );
489 MESSAGE( ":( Error: invalid myShapeId of element " << theElem->GetID() );
492 TopoDS_Shape aShape; // the shape a node of theElem is on
493 if ( theElem->GetType() != SMDSAbs_Node )
495 SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator();
496 while ( nodeIt->more() ) {
497 const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
498 if ((aShapeID = node->getshapeId()) > 0) {
499 if ( SMESHDS_SubMesh * sm = aMesh->MeshElements( aShapeID ) ) {
500 if ( sm->Contains( theElem ))
502 if ( aShape.IsNull() )
503 aShape = aMesh->IndexToShape( aShapeID );
509 // None of nodes is on a proper shape,
510 // find the shape among ancestors of aShape on which a node is
511 if ( !aShape.IsNull() ) {
512 TopTools_ListIteratorOfListOfShape ancIt( GetMesh()->GetAncestors( aShape ));
513 for ( ; ancIt.More(); ancIt.Next() ) {
514 SMESHDS_SubMesh * sm = aMesh->MeshElements( ancIt.Value() );
515 if ( sm && sm->Contains( theElem ))
516 return aMesh->ShapeToIndex( ancIt.Value() );
521 const map<int,SMESHDS_SubMesh*>& id2sm = GetMeshDS()->SubMeshes();
522 map<int,SMESHDS_SubMesh*>::const_iterator id_sm = id2sm.begin();
523 for ( ; id_sm != id2sm.end(); ++id_sm )
524 if ( id_sm->second->Contains( theElem ))
528 //MESSAGE ("::FindShape() - SHAPE NOT FOUND")
532 //=======================================================================
533 //function : IsMedium
535 //=======================================================================
537 bool SMESH_MeshEditor::IsMedium(const SMDS_MeshNode* node,
538 const SMDSAbs_ElementType typeToCheck)
540 bool isMedium = false;
541 SMDS_ElemIteratorPtr it = node->GetInverseElementIterator(typeToCheck);
542 while (it->more() && !isMedium ) {
543 const SMDS_MeshElement* elem = it->next();
544 isMedium = elem->IsMediumNode(node);
549 //=======================================================================
550 //function : shiftNodesQuadTria
551 //purpose : Shift nodes in the array corresponded to quadratic triangle
552 // example: (0,1,2,3,4,5) -> (1,2,0,4,5,3)
553 //=======================================================================
555 static void shiftNodesQuadTria(vector< const SMDS_MeshNode* >& aNodes)
557 const SMDS_MeshNode* nd1 = aNodes[0];
558 aNodes[0] = aNodes[1];
559 aNodes[1] = aNodes[2];
561 const SMDS_MeshNode* nd2 = aNodes[3];
562 aNodes[3] = aNodes[4];
563 aNodes[4] = aNodes[5];
567 //=======================================================================
568 //function : nbEdgeConnectivity
569 //purpose : return number of the edges connected with the theNode.
570 // if theEdges has connections with the other type of the
571 // elements, return -1
572 //=======================================================================
574 static int nbEdgeConnectivity(const SMDS_MeshNode* theNode)
576 // SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator();
578 // while(elemIt->more()) {
583 return theNode->NbInverseElements();
586 //=======================================================================
587 //function : getNodesFromTwoTria
589 //=======================================================================
591 static bool getNodesFromTwoTria(const SMDS_MeshElement * theTria1,
592 const SMDS_MeshElement * theTria2,
593 vector< const SMDS_MeshNode*>& N1,
594 vector< const SMDS_MeshNode*>& N2)
596 N1.assign( theTria1->begin_nodes(), theTria1->end_nodes() );
597 if ( N1.size() < 6 ) return false;
598 N2.assign( theTria2->begin_nodes(), theTria2->end_nodes() );
599 if ( N2.size() < 6 ) return false;
601 int sames[3] = {-1,-1,-1};
613 if(nbsames!=2) return false;
615 shiftNodesQuadTria(N1);
617 shiftNodesQuadTria(N1);
620 i = sames[0] + sames[1] + sames[2];
622 shiftNodesQuadTria(N2);
624 // now we receive following N1 and N2 (using numeration as in the image below)
625 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
626 // i.e. first nodes from both arrays form a new diagonal
630 //=======================================================================
631 //function : InverseDiag
632 //purpose : Replace two neighbour triangles with ones built on the same 4 nodes
633 // but having other common link.
634 // Return False if args are improper
635 //=======================================================================
637 bool SMESH_MeshEditor::InverseDiag (const SMDS_MeshElement * theTria1,
638 const SMDS_MeshElement * theTria2 )
640 MESSAGE("InverseDiag");
641 myLastCreatedElems.Clear();
642 myLastCreatedNodes.Clear();
644 if (!theTria1 || !theTria2)
647 const SMDS_VtkFace* F1 = dynamic_cast<const SMDS_VtkFace*>( theTria1 );
648 if (!F1) return false;
649 const SMDS_VtkFace* F2 = dynamic_cast<const SMDS_VtkFace*>( theTria2 );
650 if (!F2) return false;
651 if ((theTria1->GetEntityType() == SMDSEntity_Triangle) &&
652 (theTria2->GetEntityType() == SMDSEntity_Triangle)) {
654 // 1 +--+ A theTria1: ( 1 A B ) A->2 ( 1 2 B ) 1 +--+ A
655 // | /| theTria2: ( B A 2 ) B->1 ( 1 A 2 ) |\ |
659 // put nodes in array and find out indices of the same ones
660 const SMDS_MeshNode* aNodes [6];
661 int sameInd [] = { -1, -1, -1, -1, -1, -1 };
663 SMDS_ElemIteratorPtr it = theTria1->nodesIterator();
664 while ( it->more() ) {
665 aNodes[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
667 if ( i > 2 ) // theTria2
668 // find same node of theTria1
669 for ( int j = 0; j < 3; j++ )
670 if ( aNodes[ i ] == aNodes[ j ]) {
679 return false; // theTria1 is not a triangle
680 it = theTria2->nodesIterator();
682 if ( i == 6 && it->more() )
683 return false; // theTria2 is not a triangle
686 // find indices of 1,2 and of A,B in theTria1
687 int iA = -1, iB = 0, i1 = 0, i2 = 0;
688 for ( i = 0; i < 6; i++ ) {
689 if ( sameInd [ i ] == -1 ) {
694 if ( iA >= 0) iB = i;
698 // nodes 1 and 2 should not be the same
699 if ( aNodes[ i1 ] == aNodes[ i2 ] )
703 aNodes[ iA ] = aNodes[ i2 ];
705 aNodes[ sameInd[ iB ]] = aNodes[ i1 ];
707 GetMeshDS()->ChangeElementNodes( theTria1, aNodes, 3 );
708 GetMeshDS()->ChangeElementNodes( theTria2, &aNodes[ 3 ], 3 );
712 } // end if(F1 && F2)
714 // check case of quadratic faces
715 if (theTria1->GetEntityType() != SMDSEntity_Quad_Triangle &&
716 theTria1->GetEntityType() != SMDSEntity_BiQuad_Triangle)
718 if (theTria2->GetEntityType() != SMDSEntity_Quad_Triangle&&
719 theTria2->GetEntityType() != SMDSEntity_BiQuad_Triangle)
723 // 1 +--+--+ 2 theTria1: (1 2 4 5 9 7) or (2 4 1 9 7 5) or (4 1 2 7 5 9)
724 // | /| theTria2: (2 3 4 6 8 9) or (3 4 2 8 9 6) or (4 2 3 9 6 8)
732 vector< const SMDS_MeshNode* > N1;
733 vector< const SMDS_MeshNode* > N2;
734 if(!getNodesFromTwoTria(theTria1,theTria2,N1,N2))
736 // now we receive following N1 and N2 (using numeration as above image)
737 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
738 // i.e. first nodes from both arrays determ new diagonal
740 vector< const SMDS_MeshNode*> N1new( N1.size() );
741 vector< const SMDS_MeshNode*> N2new( N2.size() );
742 N1new.back() = N1.back(); // central node of biquadratic
743 N2new.back() = N2.back();
744 N1new[0] = N1[0]; N2new[0] = N1[0];
745 N1new[1] = N2[0]; N2new[1] = N1[1];
746 N1new[2] = N2[1]; N2new[2] = N2[0];
747 N1new[3] = N1[4]; N2new[3] = N1[3];
748 N1new[4] = N2[3]; N2new[4] = N2[5];
749 N1new[5] = N1[5]; N2new[5] = N1[4];
750 // change nodes in faces
751 GetMeshDS()->ChangeElementNodes( theTria1, &N1new[0], N1new.size() );
752 GetMeshDS()->ChangeElementNodes( theTria2, &N2new[0], N2new.size() );
754 // move the central node of biquadratic triangle
755 SMESH_MesherHelper helper( *GetMesh() );
756 for ( int is2nd = 0; is2nd < 2; ++is2nd )
758 const SMDS_MeshElement* tria = is2nd ? theTria2 : theTria1;
759 vector< const SMDS_MeshNode*>& nodes = is2nd ? N2new : N1new;
760 if ( nodes.size() < 7 )
762 helper.SetSubShape( tria->getshapeId() );
763 const TopoDS_Face& F = TopoDS::Face( helper.GetSubShape() );
767 xyz = ( SMESH_TNodeXYZ( nodes[3] ) +
768 SMESH_TNodeXYZ( nodes[4] ) +
769 SMESH_TNodeXYZ( nodes[5] )) / 3.;
774 gp_XY uv = ( helper.GetNodeUV( F, nodes[3], nodes[2], &checkUV ) +
775 helper.GetNodeUV( F, nodes[4], nodes[0], &checkUV ) +
776 helper.GetNodeUV( F, nodes[5], nodes[1], &checkUV )) / 3.;
778 Handle(Geom_Surface) S = BRep_Tool::Surface(F,loc);
779 xyz = S->Value( uv.X(), uv.Y() );
780 xyz.Transform( loc );
781 if ( nodes[6]->GetPosition()->GetTypeOfPosition() == SMDS_TOP_FACE && // set UV
782 nodes[6]->getshapeId() > 0 )
783 GetMeshDS()->SetNodeOnFace( nodes[6], nodes[6]->getshapeId(), uv.X(), uv.Y() );
785 GetMeshDS()->MoveNode( nodes[6], xyz.X(), xyz.Y(), xyz.Z() );
790 //=======================================================================
791 //function : findTriangles
792 //purpose : find triangles sharing theNode1-theNode2 link
793 //=======================================================================
795 static bool findTriangles(const SMDS_MeshNode * theNode1,
796 const SMDS_MeshNode * theNode2,
797 const SMDS_MeshElement*& theTria1,
798 const SMDS_MeshElement*& theTria2)
800 if ( !theNode1 || !theNode2 ) return false;
802 theTria1 = theTria2 = 0;
804 set< const SMDS_MeshElement* > emap;
805 SMDS_ElemIteratorPtr it = theNode1->GetInverseElementIterator(SMDSAbs_Face);
807 const SMDS_MeshElement* elem = it->next();
808 if ( elem->NbCornerNodes() == 3 )
811 it = theNode2->GetInverseElementIterator(SMDSAbs_Face);
813 const SMDS_MeshElement* elem = it->next();
814 if ( emap.count( elem )) {
822 // theTria1 must be element with minimum ID
823 if ( theTria2->GetID() < theTria1->GetID() )
824 std::swap( theTria2, theTria1 );
832 //=======================================================================
833 //function : InverseDiag
834 //purpose : Replace two neighbour triangles sharing theNode1-theNode2 link
835 // with ones built on the same 4 nodes but having other common link.
836 // Return false if proper faces not found
837 //=======================================================================
839 bool SMESH_MeshEditor::InverseDiag (const SMDS_MeshNode * theNode1,
840 const SMDS_MeshNode * theNode2)
842 myLastCreatedElems.Clear();
843 myLastCreatedNodes.Clear();
845 MESSAGE( "::InverseDiag()" );
847 const SMDS_MeshElement *tr1, *tr2;
848 if ( !findTriangles( theNode1, theNode2, tr1, tr2 ))
851 const SMDS_VtkFace* F1 = dynamic_cast<const SMDS_VtkFace*>( tr1 );
852 if (!F1) return false;
853 const SMDS_VtkFace* F2 = dynamic_cast<const SMDS_VtkFace*>( tr2 );
854 if (!F2) return false;
855 if ((tr1->GetEntityType() == SMDSEntity_Triangle) &&
856 (tr2->GetEntityType() == SMDSEntity_Triangle)) {
858 // 1 +--+ A tr1: ( 1 A B ) A->2 ( 1 2 B ) 1 +--+ A
859 // | /| tr2: ( B A 2 ) B->1 ( 1 A 2 ) |\ |
863 // put nodes in array
864 // and find indices of 1,2 and of A in tr1 and of B in tr2
865 int i, iA1 = 0, i1 = 0;
866 const SMDS_MeshNode* aNodes1 [3];
867 SMDS_ElemIteratorPtr it;
868 for (i = 0, it = tr1->nodesIterator(); it->more(); i++ ) {
869 aNodes1[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
870 if ( aNodes1[ i ] == theNode1 )
871 iA1 = i; // node A in tr1
872 else if ( aNodes1[ i ] != theNode2 )
876 const SMDS_MeshNode* aNodes2 [3];
877 for (i = 0, it = tr2->nodesIterator(); it->more(); i++ ) {
878 aNodes2[ i ] = static_cast<const SMDS_MeshNode*>( it->next() );
879 if ( aNodes2[ i ] == theNode2 )
880 iB2 = i; // node B in tr2
881 else if ( aNodes2[ i ] != theNode1 )
885 // nodes 1 and 2 should not be the same
886 if ( aNodes1[ i1 ] == aNodes2[ i2 ] )
890 aNodes1[ iA1 ] = aNodes2[ i2 ];
892 aNodes2[ iB2 ] = aNodes1[ i1 ];
894 GetMeshDS()->ChangeElementNodes( tr1, aNodes1, 3 );
895 GetMeshDS()->ChangeElementNodes( tr2, aNodes2, 3 );
900 // check case of quadratic faces
901 return InverseDiag(tr1,tr2);
904 //=======================================================================
905 //function : getQuadrangleNodes
906 //purpose : fill theQuadNodes - nodes of a quadrangle resulting from
907 // fusion of triangles tr1 and tr2 having shared link on
908 // theNode1 and theNode2
909 //=======================================================================
911 bool getQuadrangleNodes(const SMDS_MeshNode * theQuadNodes [],
912 const SMDS_MeshNode * theNode1,
913 const SMDS_MeshNode * theNode2,
914 const SMDS_MeshElement * tr1,
915 const SMDS_MeshElement * tr2 )
917 if( tr1->NbNodes() != tr2->NbNodes() )
919 // find the 4-th node to insert into tr1
920 const SMDS_MeshNode* n4 = 0;
921 SMDS_ElemIteratorPtr it = tr2->nodesIterator();
923 while ( !n4 && i<3 ) {
924 const SMDS_MeshNode * n = cast2Node( it->next() );
926 bool isDiag = ( n == theNode1 || n == theNode2 );
930 // Make an array of nodes to be in a quadrangle
931 int iNode = 0, iFirstDiag = -1;
932 it = tr1->nodesIterator();
935 const SMDS_MeshNode * n = cast2Node( it->next() );
937 bool isDiag = ( n == theNode1 || n == theNode2 );
939 if ( iFirstDiag < 0 )
941 else if ( iNode - iFirstDiag == 1 )
942 theQuadNodes[ iNode++ ] = n4; // insert the 4-th node between diagonal nodes
944 else if ( n == n4 ) {
945 return false; // tr1 and tr2 should not have all the same nodes
947 theQuadNodes[ iNode++ ] = n;
949 if ( iNode == 3 ) // diagonal nodes have 0 and 2 indices
950 theQuadNodes[ iNode ] = n4;
955 //=======================================================================
956 //function : DeleteDiag
957 //purpose : Replace two neighbour triangles sharing theNode1-theNode2 link
958 // with a quadrangle built on the same 4 nodes.
959 // Return false if proper faces not found
960 //=======================================================================
962 bool SMESH_MeshEditor::DeleteDiag (const SMDS_MeshNode * theNode1,
963 const SMDS_MeshNode * theNode2)
965 myLastCreatedElems.Clear();
966 myLastCreatedNodes.Clear();
968 MESSAGE( "::DeleteDiag()" );
970 const SMDS_MeshElement *tr1, *tr2;
971 if ( !findTriangles( theNode1, theNode2, tr1, tr2 ))
974 const SMDS_VtkFace* F1 = dynamic_cast<const SMDS_VtkFace*>( tr1 );
975 if (!F1) return false;
976 const SMDS_VtkFace* F2 = dynamic_cast<const SMDS_VtkFace*>( tr2 );
977 if (!F2) return false;
978 SMESHDS_Mesh * aMesh = GetMeshDS();
980 if ((tr1->GetEntityType() == SMDSEntity_Triangle) &&
981 (tr2->GetEntityType() == SMDSEntity_Triangle)) {
983 const SMDS_MeshNode* aNodes [ 4 ];
984 if ( ! getQuadrangleNodes( aNodes, theNode1, theNode2, tr1, tr2 ))
987 const SMDS_MeshElement* newElem = 0;
988 newElem = aMesh->AddFace( aNodes[0], aNodes[1], aNodes[2], aNodes[3] );
989 myLastCreatedElems.Append(newElem);
990 AddToSameGroups( newElem, tr1, aMesh );
991 int aShapeId = tr1->getshapeId();
994 aMesh->SetMeshElementOnShape( newElem, aShapeId );
996 aMesh->RemoveElement( tr1 );
997 aMesh->RemoveElement( tr2 );
1002 // check case of quadratic faces
1003 if (tr1->GetEntityType() != SMDSEntity_Quad_Triangle)
1005 if (tr2->GetEntityType() != SMDSEntity_Quad_Triangle)
1009 // 1 +--+--+ 2 tr1: (1 2 4 5 9 7) or (2 4 1 9 7 5) or (4 1 2 7 5 9)
1010 // | /| tr2: (2 3 4 6 8 9) or (3 4 2 8 9 6) or (4 2 3 9 6 8)
1018 vector< const SMDS_MeshNode* > N1;
1019 vector< const SMDS_MeshNode* > N2;
1020 if(!getNodesFromTwoTria(tr1,tr2,N1,N2))
1022 // now we receive following N1 and N2 (using numeration as above image)
1023 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
1024 // i.e. first nodes from both arrays determ new diagonal
1026 const SMDS_MeshNode* aNodes[8];
1036 const SMDS_MeshElement* newElem = 0;
1037 newElem = aMesh->AddFace( aNodes[0], aNodes[1], aNodes[2], aNodes[3],
1038 aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
1039 myLastCreatedElems.Append(newElem);
1040 AddToSameGroups( newElem, tr1, aMesh );
1041 int aShapeId = tr1->getshapeId();
1044 aMesh->SetMeshElementOnShape( newElem, aShapeId );
1046 aMesh->RemoveElement( tr1 );
1047 aMesh->RemoveElement( tr2 );
1049 // remove middle node (9)
1050 GetMeshDS()->RemoveNode( N1[4] );
1055 //=======================================================================
1056 //function : Reorient
1057 //purpose : Reverse theElement orientation
1058 //=======================================================================
1060 bool SMESH_MeshEditor::Reorient (const SMDS_MeshElement * theElem)
1062 MESSAGE("Reorient");
1063 myLastCreatedElems.Clear();
1064 myLastCreatedNodes.Clear();
1068 SMDS_ElemIteratorPtr it = theElem->nodesIterator();
1069 if ( !it || !it->more() )
1072 const SMDSAbs_ElementType type = theElem->GetType();
1073 if ( type < SMDSAbs_Edge || type > SMDSAbs_Volume )
1076 const SMDSAbs_EntityType geomType = theElem->GetEntityType();
1077 if ( geomType == SMDSEntity_Polyhedra ) // polyhedron
1079 const SMDS_VtkVolume* aPolyedre =
1080 dynamic_cast<const SMDS_VtkVolume*>( theElem );
1082 MESSAGE("Warning: bad volumic element");
1085 const int nbFaces = aPolyedre->NbFaces();
1086 vector<const SMDS_MeshNode *> poly_nodes;
1087 vector<int> quantities (nbFaces);
1089 // reverse each face of the polyedre
1090 for (int iface = 1; iface <= nbFaces; iface++) {
1091 int inode, nbFaceNodes = aPolyedre->NbFaceNodes(iface);
1092 quantities[iface - 1] = nbFaceNodes;
1094 for (inode = nbFaceNodes; inode >= 1; inode--) {
1095 const SMDS_MeshNode* curNode = aPolyedre->GetFaceNode(iface, inode);
1096 poly_nodes.push_back(curNode);
1099 return GetMeshDS()->ChangePolyhedronNodes( theElem, poly_nodes, quantities );
1101 else // other elements
1103 vector<const SMDS_MeshNode*> nodes( theElem->begin_nodes(), theElem->end_nodes() );
1104 const std::vector<int>& interlace = SMDS_MeshCell::reverseSmdsOrder( geomType );
1105 if ( interlace.empty() )
1107 std::reverse( nodes.begin(), nodes.end() ); // polygon
1109 else if ( interlace.size() > 1 )
1111 SMDS_MeshCell::applyInterlace( interlace, nodes );
1113 return GetMeshDS()->ChangeElementNodes( theElem, &nodes[0], nodes.size() );
1118 //================================================================================
1120 * \brief Reorient faces.
1121 * \param theFaces - the faces to reorient. If empty the whole mesh is meant
1122 * \param theDirection - desired direction of normal of \a theFace
1123 * \param theFace - one of \a theFaces that sould be oriented according to
1124 * \a theDirection and whose orientation defines orientation of other faces
1125 * \return number of reoriented faces.
1127 //================================================================================
1129 int SMESH_MeshEditor::Reorient2D (TIDSortedElemSet & theFaces,
1130 const gp_Dir& theDirection,
1131 const SMDS_MeshElement * theFace)
1134 if ( !theFace || theFace->GetType() != SMDSAbs_Face ) return nbReori;
1136 if ( theFaces.empty() )
1138 SMDS_FaceIteratorPtr fIt = GetMeshDS()->facesIterator(/*idInceasingOrder=*/true);
1139 while ( fIt->more() )
1140 theFaces.insert( theFaces.end(), fIt->next() );
1143 // orient theFace according to theDirection
1145 SMESH_MeshAlgos::FaceNormal( theFace, normal, /*normalized=*/false );
1146 if ( normal * theDirection.XYZ() < 0 )
1147 nbReori += Reorient( theFace );
1149 // Orient other faces
1151 set< const SMDS_MeshElement* > startFaces, visitedFaces;
1152 TIDSortedElemSet avoidSet;
1153 set< SMESH_TLink > checkedLinks;
1154 pair< set< SMESH_TLink >::iterator, bool > linkIt_isNew;
1156 if ( theFaces.size() > 1 )// leave 1 face to prevent finding not selected faces
1157 theFaces.erase( theFace );
1158 startFaces.insert( theFace );
1160 int nodeInd1, nodeInd2;
1161 const SMDS_MeshElement* otherFace;
1162 vector< const SMDS_MeshElement* > facesNearLink;
1163 vector< std::pair< int, int > > nodeIndsOfFace;
1165 set< const SMDS_MeshElement* >::iterator startFace = startFaces.begin();
1166 while ( !startFaces.empty() )
1168 startFace = startFaces.begin();
1169 theFace = *startFace;
1170 startFaces.erase( startFace );
1171 if ( !visitedFaces.insert( theFace ).second )
1175 avoidSet.insert(theFace);
1177 NLink link( theFace->GetNode( 0 ), (SMDS_MeshNode *) 0 );
1179 const int nbNodes = theFace->NbCornerNodes();
1180 for ( int i = 0; i < nbNodes; ++i ) // loop on links of theFace
1182 link.second = theFace->GetNode(( i+1 ) % nbNodes );
1183 linkIt_isNew = checkedLinks.insert( link );
1184 if ( !linkIt_isNew.second )
1186 // link has already been checked and won't be encountered more
1187 // if the group (theFaces) is manifold
1188 //checkedLinks.erase( linkIt_isNew.first );
1192 facesNearLink.clear();
1193 nodeIndsOfFace.clear();
1194 while (( otherFace = SMESH_MeshAlgos::FindFaceInSet( link.first, link.second,
1196 &nodeInd1, &nodeInd2 )))
1197 if ( otherFace != theFace)
1199 facesNearLink.push_back( otherFace );
1200 nodeIndsOfFace.push_back( make_pair( nodeInd1, nodeInd2 ));
1201 avoidSet.insert( otherFace );
1203 if ( facesNearLink.size() > 1 )
1205 // NON-MANIFOLD mesh shell !
1206 // select a face most co-directed with theFace,
1207 // other faces won't be visited this time
1209 SMESH_MeshAlgos::FaceNormal( theFace, NF, /*normalized=*/false );
1210 double proj, maxProj = -1;
1211 for ( size_t i = 0; i < facesNearLink.size(); ++i ) {
1212 SMESH_MeshAlgos::FaceNormal( facesNearLink[i], NOF, /*normalized=*/false );
1213 if (( proj = Abs( NF * NOF )) > maxProj ) {
1215 otherFace = facesNearLink[i];
1216 nodeInd1 = nodeIndsOfFace[i].first;
1217 nodeInd2 = nodeIndsOfFace[i].second;
1220 // not to visit rejected faces
1221 for ( size_t i = 0; i < facesNearLink.size(); ++i )
1222 if ( facesNearLink[i] != otherFace && theFaces.size() > 1 )
1223 visitedFaces.insert( facesNearLink[i] );
1225 else if ( facesNearLink.size() == 1 )
1227 otherFace = facesNearLink[0];
1228 nodeInd1 = nodeIndsOfFace.back().first;
1229 nodeInd2 = nodeIndsOfFace.back().second;
1231 if ( otherFace && otherFace != theFace)
1233 // link must be reverse in otherFace if orientation ot otherFace
1234 // is same as that of theFace
1235 if ( abs(nodeInd2-nodeInd1) == 1 ? nodeInd2 > nodeInd1 : nodeInd1 > nodeInd2 )
1237 nbReori += Reorient( otherFace );
1239 startFaces.insert( otherFace );
1242 std::swap( link.first, link.second ); // reverse the link
1248 //=======================================================================
1249 //function : getBadRate
1251 //=======================================================================
1253 static double getBadRate (const SMDS_MeshElement* theElem,
1254 SMESH::Controls::NumericalFunctorPtr& theCrit)
1256 SMESH::Controls::TSequenceOfXYZ P;
1257 if ( !theElem || !theCrit->GetPoints( theElem, P ))
1259 return theCrit->GetBadRate( theCrit->GetValue( P ), theElem->NbNodes() );
1260 //return theCrit->GetBadRate( theCrit->GetValue( theElem->GetID() ), theElem->NbNodes() );
1263 //=======================================================================
1264 //function : QuadToTri
1265 //purpose : Cut quadrangles into triangles.
1266 // theCrit is used to select a diagonal to cut
1267 //=======================================================================
1269 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet & theElems,
1270 SMESH::Controls::NumericalFunctorPtr theCrit)
1272 myLastCreatedElems.Clear();
1273 myLastCreatedNodes.Clear();
1275 if ( !theCrit.get() )
1278 SMESHDS_Mesh * aMesh = GetMeshDS();
1280 Handle(Geom_Surface) surface;
1281 SMESH_MesherHelper helper( *GetMesh() );
1283 TIDSortedElemSet::iterator itElem;
1284 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
1286 const SMDS_MeshElement* elem = *itElem;
1287 if ( !elem || elem->GetType() != SMDSAbs_Face )
1289 if ( elem->NbCornerNodes() != 4 )
1292 // retrieve element nodes
1293 vector< const SMDS_MeshNode* > aNodes( elem->begin_nodes(), elem->end_nodes() );
1295 // compare two sets of possible triangles
1296 double aBadRate1, aBadRate2; // to what extent a set is bad
1297 SMDS_FaceOfNodes tr1 ( aNodes[0], aNodes[1], aNodes[2] );
1298 SMDS_FaceOfNodes tr2 ( aNodes[2], aNodes[3], aNodes[0] );
1299 aBadRate1 = getBadRate( &tr1, theCrit ) + getBadRate( &tr2, theCrit );
1301 SMDS_FaceOfNodes tr3 ( aNodes[1], aNodes[2], aNodes[3] );
1302 SMDS_FaceOfNodes tr4 ( aNodes[3], aNodes[0], aNodes[1] );
1303 aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit );
1305 const int aShapeId = FindShape( elem );
1306 const SMDS_MeshElement* newElem1 = 0;
1307 const SMDS_MeshElement* newElem2 = 0;
1309 if ( !elem->IsQuadratic() ) // split liner quadrangle
1311 // for MaxElementLength2D functor we return minimum diagonal for splitting,
1312 // because aBadRate1=2*len(diagonal 1-3); aBadRate2=2*len(diagonal 2-4)
1313 if ( aBadRate1 <= aBadRate2 ) {
1314 // tr1 + tr2 is better
1315 newElem1 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
1316 newElem2 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
1319 // tr3 + tr4 is better
1320 newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
1321 newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
1324 else // split quadratic quadrangle
1326 helper.SetIsQuadratic( true );
1327 helper.SetIsBiQuadratic( aNodes.size() == 9 );
1329 helper.AddTLinks( static_cast< const SMDS_MeshFace* >( elem ));
1330 if ( aNodes.size() == 9 )
1332 helper.SetIsBiQuadratic( true );
1333 if ( aBadRate1 <= aBadRate2 )
1334 helper.AddTLinkNode( aNodes[0], aNodes[2], aNodes[8] );
1336 helper.AddTLinkNode( aNodes[1], aNodes[3], aNodes[8] );
1338 // create a new element
1339 if ( aBadRate1 <= aBadRate2 ) {
1340 newElem1 = helper.AddFace( aNodes[2], aNodes[3], aNodes[0] );
1341 newElem2 = helper.AddFace( aNodes[2], aNodes[0], aNodes[1] );
1344 newElem1 = helper.AddFace( aNodes[3], aNodes[0], aNodes[1] );
1345 newElem2 = helper.AddFace( aNodes[3], aNodes[1], aNodes[2] );
1349 // care of a new element
1351 myLastCreatedElems.Append(newElem1);
1352 myLastCreatedElems.Append(newElem2);
1353 AddToSameGroups( newElem1, elem, aMesh );
1354 AddToSameGroups( newElem2, elem, aMesh );
1356 // put a new triangle on the same shape
1358 aMesh->SetMeshElementOnShape( newElem1, aShapeId );
1359 aMesh->SetMeshElementOnShape( newElem2, aShapeId );
1361 aMesh->RemoveElement( elem );
1366 //=======================================================================
1368 * \brief Split each of given quadrangles into 4 triangles.
1369 * \param theElems - The faces to be splitted. If empty all faces are split.
1371 //=======================================================================
1373 void SMESH_MeshEditor::QuadTo4Tri (TIDSortedElemSet & theElems)
1375 myLastCreatedElems.Clear();
1376 myLastCreatedNodes.Clear();
1378 SMESH_MesherHelper helper( *GetMesh() );
1379 helper.SetElementsOnShape( true );
1381 SMDS_ElemIteratorPtr faceIt;
1382 if ( theElems.empty() ) faceIt = GetMeshDS()->elementsIterator(SMDSAbs_Face);
1383 else faceIt = elemSetIterator( theElems );
1386 gp_XY uv [9]; uv[8] = gp_XY(0,0);
1388 vector< const SMDS_MeshNode* > nodes;
1389 SMESHDS_SubMesh* subMeshDS;
1391 Handle(Geom_Surface) surface;
1392 TopLoc_Location loc;
1394 while ( faceIt->more() )
1396 const SMDS_MeshElement* quad = faceIt->next();
1397 if ( !quad || quad->NbCornerNodes() != 4 )
1400 // get a surface the quad is on
1402 if ( quad->getshapeId() < 1 )
1405 helper.SetSubShape( 0 );
1408 else if ( quad->getshapeId() != helper.GetSubShapeID() )
1410 helper.SetSubShape( quad->getshapeId() );
1411 if ( !helper.GetSubShape().IsNull() &&
1412 helper.GetSubShape().ShapeType() == TopAbs_FACE )
1414 F = TopoDS::Face( helper.GetSubShape() );
1415 surface = BRep_Tool::Surface( F, loc );
1416 subMeshDS = GetMeshDS()->MeshElements( quad->getshapeId() );
1420 helper.SetSubShape( 0 );
1425 // create a central node
1427 const SMDS_MeshNode* nCentral;
1428 nodes.assign( quad->begin_nodes(), quad->end_nodes() );
1430 if ( nodes.size() == 9 )
1432 nCentral = nodes.back();
1439 for ( ; iN < nodes.size(); ++iN )
1440 xyz[ iN ] = SMESH_TNodeXYZ( nodes[ iN ] );
1442 for ( ; iN < 8; ++iN ) // mid-side points of a linear qudrangle
1443 xyz[ iN ] = 0.5 * ( xyz[ iN - 4 ] + xyz[( iN - 3 )%4 ] );
1445 xyz[ 8 ] = helper.calcTFI( 0.5, 0.5,
1446 xyz[0], xyz[1], xyz[2], xyz[3],
1447 xyz[4], xyz[5], xyz[6], xyz[7] );
1451 for ( ; iN < nodes.size(); ++iN )
1452 uv[ iN ] = helper.GetNodeUV( F, nodes[iN], nodes[(iN+2)%4], &checkUV );
1454 for ( ; iN < 8; ++iN ) // UV of mid-side points of a linear qudrangle
1455 uv[ iN ] = helper.GetMiddleUV( surface, uv[ iN - 4 ], uv[( iN - 3 )%4 ] );
1457 uv[ 8 ] = helper.calcTFI( 0.5, 0.5,
1458 uv[0], uv[1], uv[2], uv[3],
1459 uv[4], uv[5], uv[6], uv[7] );
1461 gp_Pnt p = surface->Value( uv[8].X(), uv[8].Y() ).Transformed( loc );
1465 nCentral = helper.AddNode( xyz[8].X(), xyz[8].Y(), xyz[8].Z(), /*id=*/0,
1466 uv[8].X(), uv[8].Y() );
1467 myLastCreatedNodes.Append( nCentral );
1470 // create 4 triangles
1472 GetMeshDS()->RemoveFreeElement( quad, subMeshDS, /*fromGroups=*/false );
1474 helper.SetIsQuadratic ( nodes.size() > 4 );
1475 helper.SetIsBiQuadratic( nodes.size() == 9 );
1476 if ( helper.GetIsQuadratic() )
1477 helper.AddTLinks( static_cast< const SMDS_MeshFace*>( quad ));
1479 for ( int i = 0; i < 4; ++i )
1481 SMDS_MeshElement* tria = helper.AddFace( nodes[ i ],
1484 ReplaceElemInGroups( tria, quad, GetMeshDS() );
1485 myLastCreatedElems.Append( tria );
1490 //=======================================================================
1491 //function : BestSplit
1492 //purpose : Find better diagonal for cutting.
1493 //=======================================================================
1495 int SMESH_MeshEditor::BestSplit (const SMDS_MeshElement* theQuad,
1496 SMESH::Controls::NumericalFunctorPtr theCrit)
1498 myLastCreatedElems.Clear();
1499 myLastCreatedNodes.Clear();
1504 if (!theQuad || theQuad->GetType() != SMDSAbs_Face )
1507 if( theQuad->NbNodes()==4 ||
1508 (theQuad->NbNodes()==8 && theQuad->IsQuadratic()) ) {
1510 // retrieve element nodes
1511 const SMDS_MeshNode* aNodes [4];
1512 SMDS_ElemIteratorPtr itN = theQuad->nodesIterator();
1514 //while (itN->more())
1516 aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
1518 // compare two sets of possible triangles
1519 double aBadRate1, aBadRate2; // to what extent a set is bad
1520 SMDS_FaceOfNodes tr1 ( aNodes[0], aNodes[1], aNodes[2] );
1521 SMDS_FaceOfNodes tr2 ( aNodes[2], aNodes[3], aNodes[0] );
1522 aBadRate1 = getBadRate( &tr1, theCrit ) + getBadRate( &tr2, theCrit );
1524 SMDS_FaceOfNodes tr3 ( aNodes[1], aNodes[2], aNodes[3] );
1525 SMDS_FaceOfNodes tr4 ( aNodes[3], aNodes[0], aNodes[1] );
1526 aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit );
1527 // for MaxElementLength2D functor we return minimum diagonal for splitting,
1528 // because aBadRate1=2*len(diagonal 1-3); aBadRate2=2*len(diagonal 2-4)
1529 if (aBadRate1 <= aBadRate2) // tr1 + tr2 is better
1530 return 1; // diagonal 1-3
1532 return 2; // diagonal 2-4
1539 // Methods of splitting volumes into tetra
1541 const int theHexTo5_1[5*4+1] =
1543 0, 1, 2, 5, 0, 4, 5, 7, 0, 2, 3, 7, 2, 5, 6, 7, 0, 5, 2, 7, -1
1545 const int theHexTo5_2[5*4+1] =
1547 1, 2, 3, 6, 1, 4, 5, 6, 0, 1, 3, 4, 3, 4, 6, 7, 1, 3, 4, 6, -1
1549 const int* theHexTo5[2] = { theHexTo5_1, theHexTo5_2 };
1551 const int theHexTo6_1[6*4+1] =
1553 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
1555 const int theHexTo6_2[6*4+1] =
1557 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
1559 const int theHexTo6_3[6*4+1] =
1561 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
1563 const int theHexTo6_4[6*4+1] =
1565 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
1567 const int* theHexTo6[4] = { theHexTo6_1, theHexTo6_2, theHexTo6_3, theHexTo6_4 };
1569 const int thePyraTo2_1[2*4+1] =
1571 0, 1, 2, 4, 0, 2, 3, 4, -1
1573 const int thePyraTo2_2[2*4+1] =
1575 1, 2, 3, 4, 1, 3, 0, 4, -1
1577 const int* thePyraTo2[2] = { thePyraTo2_1, thePyraTo2_2 };
1579 const int thePentaTo3_1[3*4+1] =
1581 0, 1, 2, 3, 1, 3, 4, 2, 2, 3, 4, 5, -1
1583 const int thePentaTo3_2[3*4+1] =
1585 1, 2, 0, 4, 2, 4, 5, 0, 0, 4, 5, 3, -1
1587 const int thePentaTo3_3[3*4+1] =
1589 2, 0, 1, 5, 0, 5, 3, 1, 1, 5, 3, 4, -1
1591 const int thePentaTo3_4[3*4+1] =
1593 0, 1, 2, 3, 1, 3, 4, 5, 2, 3, 1, 5, -1
1595 const int thePentaTo3_5[3*4+1] =
1597 1, 2, 0, 4, 2, 4, 5, 3, 0, 4, 2, 3, -1
1599 const int thePentaTo3_6[3*4+1] =
1601 2, 0, 1, 5, 0, 5, 3, 4, 1, 5, 0, 4, -1
1603 const int* thePentaTo3[6] = { thePentaTo3_1, thePentaTo3_2, thePentaTo3_3,
1604 thePentaTo3_4, thePentaTo3_5, thePentaTo3_6 };
1606 // Methods of splitting hexahedron into prisms
1608 const int theHexTo4Prisms_BT[6*4+1] = // bottom-top
1610 0, 1, 8, 4, 5, 9, 1, 2, 8, 5, 6, 9, 2, 3, 8, 6, 7, 9, 3, 0, 8, 7, 4, 9, -1
1612 const int theHexTo4Prisms_LR[6*4+1] = // left-right
1614 1, 0, 8, 2, 3, 9, 0, 4, 8, 3, 7, 9, 4, 5, 8, 7, 6, 9, 5, 1, 8, 6, 2, 9, -1
1616 const int theHexTo4Prisms_FB[6*4+1] = // front-back
1618 0, 3, 9, 1, 2, 8, 3, 7, 9, 2, 6, 8, 7, 4, 9, 6, 5, 8, 4, 0, 9, 5, 1, 8, -1
1621 const int theHexTo2Prisms_BT_1[6*2+1] =
1623 0, 1, 3, 4, 5, 7, 1, 2, 3, 5, 6, 7, -1
1625 const int theHexTo2Prisms_BT_2[6*2+1] =
1627 0, 1, 2, 4, 5, 6, 0, 2, 3, 4, 6, 7, -1
1629 const int* theHexTo2Prisms_BT[2] = { theHexTo2Prisms_BT_1, theHexTo2Prisms_BT_2 };
1631 const int theHexTo2Prisms_LR_1[6*2+1] =
1633 1, 0, 4, 2, 3, 7, 1, 4, 5, 2, 7, 6, -1
1635 const int theHexTo2Prisms_LR_2[6*2+1] =
1637 1, 0, 4, 2, 3, 7, 1, 4, 5, 2, 7, 6, -1
1639 const int* theHexTo2Prisms_LR[2] = { theHexTo2Prisms_LR_1, theHexTo2Prisms_LR_2 };
1641 const int theHexTo2Prisms_FB_1[6*2+1] =
1643 0, 3, 4, 1, 2, 5, 3, 7, 4, 2, 6, 5, -1
1645 const int theHexTo2Prisms_FB_2[6*2+1] =
1647 0, 3, 7, 1, 2, 7, 0, 7, 4, 1, 6, 5, -1
1649 const int* theHexTo2Prisms_FB[2] = { theHexTo2Prisms_FB_1, theHexTo2Prisms_FB_2 };
1652 struct TTriangleFacet //!< stores indices of three nodes of tetra facet
1655 TTriangleFacet(int n1, int n2, int n3): _n1(n1), _n2(n2), _n3(n3) {}
1656 bool contains(int n) const { return ( n == _n1 || n == _n2 || n == _n3 ); }
1657 bool hasAdjacentVol( const SMDS_MeshElement* elem,
1658 const SMDSAbs_GeometryType geom = SMDSGeom_TETRA) const;
1664 const int* _connectivity; //!< foursomes of tetra connectivy finished by -1
1665 bool _baryNode; //!< additional node is to be created at cell barycenter
1666 bool _ownConn; //!< to delete _connectivity in destructor
1667 map<int, const SMDS_MeshNode*> _faceBaryNode; //!< map face index to node at BC of face
1669 TSplitMethod( int nbTet=0, const int* conn=0, bool addNode=false)
1670 : _nbSplits(nbTet), _nbCorners(4), _connectivity(conn), _baryNode(addNode), _ownConn(false) {}
1671 ~TSplitMethod() { if ( _ownConn ) delete [] _connectivity; _connectivity = 0; }
1672 bool hasFacet( const TTriangleFacet& facet ) const
1674 if ( _nbCorners == 4 )
1676 const int* tetConn = _connectivity;
1677 for ( ; tetConn[0] >= 0; tetConn += 4 )
1678 if (( facet.contains( tetConn[0] ) +
1679 facet.contains( tetConn[1] ) +
1680 facet.contains( tetConn[2] ) +
1681 facet.contains( tetConn[3] )) == 3 )
1684 else // prism, _nbCorners == 6
1686 const int* prismConn = _connectivity;
1687 for ( ; prismConn[0] >= 0; prismConn += 6 )
1689 if (( facet.contains( prismConn[0] ) &&
1690 facet.contains( prismConn[1] ) &&
1691 facet.contains( prismConn[2] ))
1693 ( facet.contains( prismConn[3] ) &&
1694 facet.contains( prismConn[4] ) &&
1695 facet.contains( prismConn[5] )))
1703 //=======================================================================
1705 * \brief return TSplitMethod for the given element to split into tetrahedra
1707 //=======================================================================
1709 TSplitMethod getTetraSplitMethod( SMDS_VolumeTool& vol, const int theMethodFlags)
1711 const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
1713 // at HEXA_TO_24 method, each face of volume is split into triangles each based on
1714 // an edge and a face barycenter; tertaherdons are based on triangles and
1715 // a volume barycenter
1716 const bool is24TetMode = ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_24 );
1718 // Find out how adjacent volumes are split
1720 vector < list< TTriangleFacet > > triaSplitsByFace( vol.NbFaces() ); // splits of each side
1721 int hasAdjacentSplits = 0, maxTetConnSize = 0;
1722 for ( int iF = 0; iF < vol.NbFaces(); ++iF )
1724 int nbNodes = vol.NbFaceNodes( iF ) / iQ;
1725 maxTetConnSize += 4 * ( nbNodes - (is24TetMode ? 0 : 2));
1726 if ( nbNodes < 4 ) continue;
1728 list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
1729 const int* nInd = vol.GetFaceNodesIndices( iF );
1732 TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
1733 TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
1734 if ( t012.hasAdjacentVol( vol.Element() )) triaSplits.push_back( t012 );
1735 else if ( t123.hasAdjacentVol( vol.Element() )) triaSplits.push_back( t123 );
1739 int iCom = 0; // common node of triangle faces to split into
1740 for ( int iVar = 0; iVar < nbNodes; ++iVar, ++iCom )
1742 TTriangleFacet t012( nInd[ iQ * ( iCom )],
1743 nInd[ iQ * ( (iCom+1)%nbNodes )],
1744 nInd[ iQ * ( (iCom+2)%nbNodes )]);
1745 TTriangleFacet t023( nInd[ iQ * ( iCom )],
1746 nInd[ iQ * ( (iCom+2)%nbNodes )],
1747 nInd[ iQ * ( (iCom+3)%nbNodes )]);
1748 if ( t012.hasAdjacentVol( vol.Element() ) && t023.hasAdjacentVol( vol.Element() ))
1750 triaSplits.push_back( t012 );
1751 triaSplits.push_back( t023 );
1756 if ( !triaSplits.empty() )
1757 hasAdjacentSplits = true;
1760 // Among variants of split method select one compliant with adjacent volumes
1762 TSplitMethod method;
1763 if ( !vol.Element()->IsPoly() && !is24TetMode )
1765 int nbVariants = 2, nbTet = 0;
1766 const int** connVariants = 0;
1767 switch ( vol.Element()->GetEntityType() )
1769 case SMDSEntity_Hexa:
1770 case SMDSEntity_Quad_Hexa:
1771 case SMDSEntity_TriQuad_Hexa:
1772 if ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_5 )
1773 connVariants = theHexTo5, nbTet = 5;
1775 connVariants = theHexTo6, nbTet = 6, nbVariants = 4;
1777 case SMDSEntity_Pyramid:
1778 case SMDSEntity_Quad_Pyramid:
1779 connVariants = thePyraTo2; nbTet = 2;
1781 case SMDSEntity_Penta:
1782 case SMDSEntity_Quad_Penta:
1783 connVariants = thePentaTo3; nbTet = 3; nbVariants = 6;
1788 for ( int variant = 0; variant < nbVariants && method._nbSplits == 0; ++variant )
1790 // check method compliancy with adjacent tetras,
1791 // all found splits must be among facets of tetras described by this method
1792 method = TSplitMethod( nbTet, connVariants[variant] );
1793 if ( hasAdjacentSplits && method._nbSplits > 0 )
1795 bool facetCreated = true;
1796 for ( int iF = 0; facetCreated && iF < triaSplitsByFace.size(); ++iF )
1798 list< TTriangleFacet >::const_iterator facet = triaSplitsByFace[iF].begin();
1799 for ( ; facetCreated && facet != triaSplitsByFace[iF].end(); ++facet )
1800 facetCreated = method.hasFacet( *facet );
1802 if ( !facetCreated )
1803 method = TSplitMethod(0); // incompatible method
1807 if ( method._nbSplits < 1 )
1809 // No standard method is applicable, use a generic solution:
1810 // each facet of a volume is split into triangles and
1811 // each of triangles and a volume barycenter form a tetrahedron.
1813 const bool isHex27 = ( vol.Element()->GetEntityType() == SMDSEntity_TriQuad_Hexa );
1815 int* connectivity = new int[ maxTetConnSize + 1 ];
1816 method._connectivity = connectivity;
1817 method._ownConn = true;
1818 method._baryNode = !isHex27; // to create central node or not
1821 int baryCenInd = vol.NbNodes() - int( isHex27 );
1822 for ( int iF = 0; iF < vol.NbFaces(); ++iF )
1824 const int nbNodes = vol.NbFaceNodes( iF ) / iQ;
1825 const int* nInd = vol.GetFaceNodesIndices( iF );
1826 // find common node of triangle facets of tetra to create
1827 int iCommon = 0; // index in linear numeration
1828 const list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
1829 if ( !triaSplits.empty() )
1832 const TTriangleFacet* facet = &triaSplits.front();
1833 for ( ; iCommon < nbNodes-1 ; ++iCommon )
1834 if ( facet->contains( nInd[ iQ * iCommon ]) &&
1835 facet->contains( nInd[ iQ * ((iCommon+2)%nbNodes) ]))
1838 else if ( nbNodes > 3 && !is24TetMode )
1840 // find the best method of splitting into triangles by aspect ratio
1841 SMESH::Controls::NumericalFunctorPtr aspectRatio( new SMESH::Controls::AspectRatio);
1842 map< double, int > badness2iCommon;
1843 const SMDS_MeshNode** nodes = vol.GetFaceNodes( iF );
1844 int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
1845 for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCommon )
1848 for ( int iLast = iCommon+2; iLast < iCommon+nbNodes; ++iLast )
1850 SMDS_FaceOfNodes tria ( nodes[ iQ*( iCommon )],
1851 nodes[ iQ*((iLast-1)%nbNodes)],
1852 nodes[ iQ*((iLast )%nbNodes)]);
1853 badness += getBadRate( &tria, aspectRatio );
1855 badness2iCommon.insert( make_pair( badness, iCommon ));
1857 // use iCommon with lowest badness
1858 iCommon = badness2iCommon.begin()->second;
1860 if ( iCommon >= nbNodes )
1861 iCommon = 0; // something wrong
1863 // fill connectivity of tetrahedra based on a current face
1864 int nbTet = nbNodes - 2;
1865 if ( is24TetMode && nbNodes > 3 && triaSplits.empty())
1870 faceBaryCenInd = vol.GetCenterNodeIndex( iF );
1871 method._faceBaryNode[ iF ] = vol.GetNodes()[ faceBaryCenInd ];
1875 method._faceBaryNode[ iF ] = 0;
1876 faceBaryCenInd = baryCenInd + method._faceBaryNode.size();
1879 for ( int i = 0; i < nbTet; ++i )
1881 int i1 = i, i2 = (i+1) % nbNodes;
1882 if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
1883 connectivity[ connSize++ ] = nInd[ iQ * i1 ];
1884 connectivity[ connSize++ ] = nInd[ iQ * i2 ];
1885 connectivity[ connSize++ ] = faceBaryCenInd;
1886 connectivity[ connSize++ ] = baryCenInd;
1891 for ( int i = 0; i < nbTet; ++i )
1893 int i1 = (iCommon+1+i) % nbNodes, i2 = (iCommon+2+i) % nbNodes;
1894 if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
1895 connectivity[ connSize++ ] = nInd[ iQ * iCommon ];
1896 connectivity[ connSize++ ] = nInd[ iQ * i1 ];
1897 connectivity[ connSize++ ] = nInd[ iQ * i2 ];
1898 connectivity[ connSize++ ] = baryCenInd;
1901 method._nbSplits += nbTet;
1903 } // loop on volume faces
1905 connectivity[ connSize++ ] = -1;
1907 } // end of generic solution
1911 //=======================================================================
1913 * \brief return TSplitMethod to split haxhedron into prisms
1915 //=======================================================================
1917 TSplitMethod getPrismSplitMethod( SMDS_VolumeTool& vol,
1918 const int methodFlags,
1919 const int facetToSplit)
1921 // order of facets in HEX according to SMDS_VolumeTool::Hexa_F :
1923 const int iF = ( facetToSplit < 2 ) ? 0 : 1 + ( facetToSplit-2 ) % 2; // [0,1,2]
1925 if ( methodFlags == SMESH_MeshEditor::HEXA_TO_4_PRISMS )
1927 static TSplitMethod to4methods[4]; // order BT, LR, FB
1928 if ( to4methods[iF]._nbSplits == 0 )
1932 to4methods[iF]._connectivity = theHexTo4Prisms_BT;
1933 to4methods[iF]._faceBaryNode[ 0 ] = 0;
1934 to4methods[iF]._faceBaryNode[ 1 ] = 0;
1937 to4methods[iF]._connectivity = theHexTo4Prisms_LR;
1938 to4methods[iF]._faceBaryNode[ 2 ] = 0;
1939 to4methods[iF]._faceBaryNode[ 4 ] = 0;
1942 to4methods[iF]._connectivity = theHexTo4Prisms_FB;
1943 to4methods[iF]._faceBaryNode[ 3 ] = 0;
1944 to4methods[iF]._faceBaryNode[ 5 ] = 0;
1946 default: return to4methods[3];
1948 to4methods[iF]._nbSplits = 4;
1949 to4methods[iF]._nbCorners = 6;
1951 return to4methods[iF];
1953 // else if ( methodFlags == HEXA_TO_2_PRISMS )
1955 TSplitMethod method;
1957 const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
1959 const int nbVariants = 2, nbSplits = 2;
1960 const int** connVariants = 0;
1962 case 0: connVariants = theHexTo2Prisms_BT; break;
1963 case 1: connVariants = theHexTo2Prisms_LR; break;
1964 case 2: connVariants = theHexTo2Prisms_FB; break;
1965 default: return method;
1968 // look for prisms adjacent via facetToSplit and an opposite one
1969 for ( int is2nd = 0; is2nd < 2; ++is2nd )
1971 int iFacet = is2nd ? vol.GetOppFaceIndexOfHex( facetToSplit ) : facetToSplit;
1972 int nbNodes = vol.NbFaceNodes( iFacet ) / iQ;
1973 if ( nbNodes != 4 ) return method;
1975 const int* nInd = vol.GetFaceNodesIndices( iFacet );
1976 TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
1977 TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
1979 if ( t012.hasAdjacentVol( vol.Element(), SMDSGeom_PENTA ))
1981 else if ( t123.hasAdjacentVol( vol.Element(), SMDSGeom_PENTA ))
1986 // there are adjacent prism
1987 for ( int variant = 0; variant < nbVariants; ++variant )
1989 // check method compliancy with adjacent prisms,
1990 // the found prism facets must be among facets of prisms described by current method
1991 method._nbSplits = nbSplits;
1992 method._nbCorners = 6;
1993 method._connectivity = connVariants[ variant ];
1994 if ( method.hasFacet( *t ))
1999 // No adjacent prisms. Select a variant with a best aspect ratio.
2001 double badness[2] = { 0, 0 };
2002 static SMESH::Controls::NumericalFunctorPtr aspectRatio( new SMESH::Controls::AspectRatio);
2003 const SMDS_MeshNode** nodes = vol.GetNodes();
2004 for ( int variant = 0; variant < nbVariants; ++variant )
2005 for ( int is2nd = 0; is2nd < 2; ++is2nd )
2007 int iFacet = is2nd ? vol.GetOppFaceIndexOfHex( facetToSplit ) : facetToSplit;
2008 const int* nInd = vol.GetFaceNodesIndices( iFacet );
2010 method._connectivity = connVariants[ variant ];
2011 TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
2012 TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
2013 TTriangleFacet* t = ( method.hasFacet( t012 )) ? & t012 : & t123;
2015 SMDS_FaceOfNodes tria ( nodes[ t->_n1 ],
2018 badness[ variant ] += getBadRate( &tria, aspectRatio );
2020 const int iBetter = ( badness[1] < badness[0] && badness[0]-badness[1] > 0.1 * badness[0] );
2022 method._nbSplits = nbSplits;
2023 method._nbCorners = 6;
2024 method._connectivity = connVariants[ iBetter ];
2029 //================================================================================
2031 * \brief Check if there is a tetraherdon adjacent to the given element via this facet
2033 //================================================================================
2035 bool TTriangleFacet::hasAdjacentVol( const SMDS_MeshElement* elem,
2036 const SMDSAbs_GeometryType geom ) const
2038 // find the tetrahedron including the three nodes of facet
2039 const SMDS_MeshNode* n1 = elem->GetNode(_n1);
2040 const SMDS_MeshNode* n2 = elem->GetNode(_n2);
2041 const SMDS_MeshNode* n3 = elem->GetNode(_n3);
2042 SMDS_ElemIteratorPtr volIt1 = n1->GetInverseElementIterator(SMDSAbs_Volume);
2043 while ( volIt1->more() )
2045 const SMDS_MeshElement* v = volIt1->next();
2046 if ( v->GetGeomType() != geom )
2048 const int lastCornerInd = v->NbCornerNodes() - 1;
2049 if ( v->IsQuadratic() && v->GetNodeIndex( n1 ) > lastCornerInd )
2050 continue; // medium node not allowed
2051 const int ind2 = v->GetNodeIndex( n2 );
2052 if ( ind2 < 0 || lastCornerInd < ind2 )
2054 const int ind3 = v->GetNodeIndex( n3 );
2055 if ( ind3 < 0 || lastCornerInd < ind3 )
2062 //=======================================================================
2064 * \brief A key of a face of volume
2066 //=======================================================================
2068 struct TVolumeFaceKey: pair< pair< int, int>, pair< int, int> >
2070 TVolumeFaceKey( SMDS_VolumeTool& vol, int iF )
2072 TIDSortedNodeSet sortedNodes;
2073 const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
2074 int nbNodes = vol.NbFaceNodes( iF );
2075 const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iF );
2076 for ( int i = 0; i < nbNodes; i += iQ )
2077 sortedNodes.insert( fNodes[i] );
2078 TIDSortedNodeSet::iterator n = sortedNodes.begin();
2079 first.first = (*(n++))->GetID();
2080 first.second = (*(n++))->GetID();
2081 second.first = (*(n++))->GetID();
2082 second.second = ( sortedNodes.size() > 3 ) ? (*(n++))->GetID() : 0;
2087 //=======================================================================
2088 //function : SplitVolumes
2089 //purpose : Split volume elements into tetrahedra or prisms.
2090 // If facet ID < 0, element is split into tetrahedra,
2091 // else a hexahedron is split into prisms so that the given facet is
2092 // split into triangles
2093 //=======================================================================
2095 void SMESH_MeshEditor::SplitVolumes (const TFacetOfElem & theElems,
2096 const int theMethodFlags)
2098 // std-like iterator on coordinates of nodes of mesh element
2099 typedef SMDS_StdIterator< SMESH_TNodeXYZ, SMDS_ElemIteratorPtr > NXyzIterator;
2100 NXyzIterator xyzEnd;
2102 SMDS_VolumeTool volTool;
2103 SMESH_MesherHelper helper( *GetMesh()), fHelper(*GetMesh());
2104 fHelper.ToFixNodeParameters( true );
2106 SMESHDS_SubMesh* subMesh = 0;//GetMeshDS()->MeshElements(1);
2107 SMESHDS_SubMesh* fSubMesh = 0;//subMesh;
2109 SMESH_SequenceOfElemPtr newNodes, newElems;
2111 // map face of volume to it's baricenrtic node
2112 map< TVolumeFaceKey, const SMDS_MeshNode* > volFace2BaryNode;
2115 TFacetOfElem::const_iterator elem2facet = theElems.begin();
2116 for ( ; elem2facet != theElems.end(); ++elem2facet )
2118 const SMDS_MeshElement* elem = elem2facet->first;
2119 const int facetToSplit = elem2facet->second;
2120 if ( elem->GetType() != SMDSAbs_Volume )
2122 const SMDSAbs_EntityType geomType = elem->GetEntityType();
2123 if ( geomType == SMDSEntity_Tetra || geomType == SMDSEntity_Quad_Tetra )
2126 if ( !volTool.Set( elem, /*ignoreCentralNodes=*/false )) continue; // strange...
2128 TSplitMethod splitMethod = ( facetToSplit < 0 ?
2129 getTetraSplitMethod( volTool, theMethodFlags ) :
2130 getPrismSplitMethod( volTool, theMethodFlags, facetToSplit ));
2131 if ( splitMethod._nbSplits < 1 ) continue;
2133 // find submesh to add new tetras to
2134 if ( !subMesh || !subMesh->Contains( elem ))
2136 int shapeID = FindShape( elem );
2137 helper.SetSubShape( shapeID ); // helper will add tetras to the found submesh
2138 subMesh = GetMeshDS()->MeshElements( shapeID );
2141 if ( elem->IsQuadratic() )
2144 // add quadratic links to the helper
2145 for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
2147 const SMDS_MeshNode** fNodes = volTool.GetFaceNodes( iF );
2148 int nbN = volTool.NbFaceNodes( iF ) - bool( volTool.GetCenterNodeIndex(iF) > 0 );
2149 for ( int iN = 0; iN < nbN; iN += iQ )
2150 helper.AddTLinkNode( fNodes[iN], fNodes[iN+2], fNodes[iN+1] );
2152 helper.SetIsQuadratic( true );
2157 helper.SetIsQuadratic( false );
2159 vector<const SMDS_MeshNode*> nodes( volTool.GetNodes(),
2160 volTool.GetNodes() + elem->NbNodes() );
2161 helper.SetElementsOnShape( true );
2162 if ( splitMethod._baryNode )
2164 // make a node at barycenter
2165 volTool.GetBaryCenter( bc[0], bc[1], bc[2] );
2166 SMDS_MeshNode* gcNode = helper.AddNode( bc[0], bc[1], bc[2] );
2167 nodes.push_back( gcNode );
2168 newNodes.Append( gcNode );
2170 if ( !splitMethod._faceBaryNode.empty() )
2172 // make or find baricentric nodes of faces
2173 map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.begin();
2174 for ( ; iF_n != splitMethod._faceBaryNode.end(); ++iF_n )
2176 map< TVolumeFaceKey, const SMDS_MeshNode* >::iterator f_n =
2177 volFace2BaryNode.insert
2178 ( make_pair( TVolumeFaceKey( volTool,iF_n->first ), iF_n->second )).first;
2181 volTool.GetFaceBaryCenter( iF_n->first, bc[0], bc[1], bc[2] );
2182 newNodes.Append( f_n->second = helper.AddNode( bc[0], bc[1], bc[2] ));
2184 nodes.push_back( iF_n->second = f_n->second );
2189 vector<const SMDS_MeshElement* > splitVols( splitMethod._nbSplits ); // splits of a volume
2190 const int* volConn = splitMethod._connectivity;
2191 if ( splitMethod._nbCorners == 4 ) // tetra
2192 for ( int i = 0; i < splitMethod._nbSplits; ++i, volConn += splitMethod._nbCorners )
2193 newElems.Append( splitVols[ i ] = helper.AddVolume( nodes[ volConn[0] ],
2194 nodes[ volConn[1] ],
2195 nodes[ volConn[2] ],
2196 nodes[ volConn[3] ]));
2198 for ( int i = 0; i < splitMethod._nbSplits; ++i, volConn += splitMethod._nbCorners )
2199 newElems.Append( splitVols[ i ] = helper.AddVolume( nodes[ volConn[0] ],
2200 nodes[ volConn[1] ],
2201 nodes[ volConn[2] ],
2202 nodes[ volConn[3] ],
2203 nodes[ volConn[4] ],
2204 nodes[ volConn[5] ]));
2206 ReplaceElemInGroups( elem, splitVols, GetMeshDS() );
2208 // Split faces on sides of the split volume
2210 const SMDS_MeshNode** volNodes = volTool.GetNodes();
2211 for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
2213 const int nbNodes = volTool.NbFaceNodes( iF ) / iQ;
2214 if ( nbNodes < 4 ) continue;
2216 // find an existing face
2217 vector<const SMDS_MeshNode*> fNodes( volTool.GetFaceNodes( iF ),
2218 volTool.GetFaceNodes( iF ) + volTool.NbFaceNodes( iF ));
2219 while ( const SMDS_MeshElement* face = GetMeshDS()->FindElement( fNodes, SMDSAbs_Face,
2220 /*noMedium=*/false))
2223 helper.SetElementsOnShape( false );
2224 vector< const SMDS_MeshElement* > triangles;
2226 // find submesh to add new triangles in
2227 if ( !fSubMesh || !fSubMesh->Contains( face ))
2229 int shapeID = FindShape( face );
2230 fSubMesh = GetMeshDS()->MeshElements( shapeID );
2232 map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.find(iF);
2233 if ( iF_n != splitMethod._faceBaryNode.end() )
2235 const SMDS_MeshNode *baryNode = iF_n->second;
2236 for ( int iN = 0; iN < nbNodes*iQ; iN += iQ )
2238 const SMDS_MeshNode* n1 = fNodes[iN];
2239 const SMDS_MeshNode *n2 = fNodes[(iN+iQ)%(nbNodes*iQ)];
2240 const SMDS_MeshNode *n3 = baryNode;
2241 if ( !volTool.IsFaceExternal( iF ))
2243 triangles.push_back( helper.AddFace( n1,n2,n3 ));
2245 if ( fSubMesh ) // update position of the bary node on geometry
2248 subMesh->RemoveNode( baryNode, false );
2249 GetMeshDS()->SetNodeOnFace( baryNode, fSubMesh->GetID() );
2250 const TopoDS_Shape& s = GetMeshDS()->IndexToShape( fSubMesh->GetID() );
2251 if ( !s.IsNull() && s.ShapeType() == TopAbs_FACE )
2253 fHelper.SetSubShape( s );
2254 gp_XY uv( 1e100, 1e100 );
2256 if ( !fHelper.CheckNodeUV( TopoDS::Face( s ), baryNode,
2257 uv, /*tol=*/1e-7, /*force=*/true, distXYZ ) &&
2260 // node is too far from the surface
2261 GetMeshDS()->MoveNode( baryNode, distXYZ[1], distXYZ[2], distXYZ[3] );
2262 const_cast<SMDS_MeshNode*>( baryNode )->SetPosition
2263 ( SMDS_PositionPtr( new SMDS_FacePosition( uv.X(), uv.Y() )));
2270 // among possible triangles create ones discribed by split method
2271 const int* nInd = volTool.GetFaceNodesIndices( iF );
2272 int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
2273 int iCom = 0; // common node of triangle faces to split into
2274 list< TTriangleFacet > facets;
2275 for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCom )
2277 TTriangleFacet t012( nInd[ iQ * ( iCom )],
2278 nInd[ iQ * ( (iCom+1)%nbNodes )],
2279 nInd[ iQ * ( (iCom+2)%nbNodes )]);
2280 TTriangleFacet t023( nInd[ iQ * ( iCom )],
2281 nInd[ iQ * ( (iCom+2)%nbNodes )],
2282 nInd[ iQ * ( (iCom+3)%nbNodes )]);
2283 if ( splitMethod.hasFacet( t012 ) && splitMethod.hasFacet( t023 ))
2285 facets.push_back( t012 );
2286 facets.push_back( t023 );
2287 for ( int iLast = iCom+4; iLast < iCom+nbNodes; ++iLast )
2288 facets.push_back( TTriangleFacet( nInd[ iQ * ( iCom )],
2289 nInd[ iQ * ((iLast-1)%nbNodes )],
2290 nInd[ iQ * ((iLast )%nbNodes )]));
2294 list< TTriangleFacet >::iterator facet = facets.begin();
2295 if ( facet == facets.end() )
2297 for ( ; facet != facets.end(); ++facet )
2299 if ( !volTool.IsFaceExternal( iF ))
2300 swap( facet->_n2, facet->_n3 );
2301 triangles.push_back( helper.AddFace( volNodes[ facet->_n1 ],
2302 volNodes[ facet->_n2 ],
2303 volNodes[ facet->_n3 ]));
2306 for ( int i = 0; i < triangles.size(); ++i )
2308 if ( !triangles[i] ) continue;
2310 fSubMesh->AddElement( triangles[i]);
2311 newElems.Append( triangles[i] );
2313 ReplaceElemInGroups( face, triangles, GetMeshDS() );
2314 GetMeshDS()->RemoveFreeElement( face, fSubMesh, /*fromGroups=*/false );
2316 } // while a face based on facet nodes exists
2317 } // loop on volume faces to split them into triangles
2319 GetMeshDS()->RemoveFreeElement( elem, subMesh, /*fromGroups=*/false );
2321 if ( geomType == SMDSEntity_TriQuad_Hexa )
2323 // remove medium nodes that could become free
2324 for ( int i = 20; i < volTool.NbNodes(); ++i )
2325 if ( volNodes[i]->NbInverseElements() == 0 )
2326 GetMeshDS()->RemoveNode( volNodes[i] );
2328 } // loop on volumes to split
2330 myLastCreatedNodes = newNodes;
2331 myLastCreatedElems = newElems;
2334 //=======================================================================
2335 //function : GetHexaFacetsToSplit
2336 //purpose : For hexahedra that will be split into prisms, finds facets to
2337 // split into triangles. Only hexahedra adjacent to the one closest
2338 // to theFacetNormal.Location() are returned.
2339 //param [in,out] theHexas - the hexahedra
2340 //param [in] theFacetNormal - facet normal
2341 //param [out] theFacets - the hexahedra and found facet IDs
2342 //=======================================================================
2344 void SMESH_MeshEditor::GetHexaFacetsToSplit( TIDSortedElemSet& theHexas,
2345 const gp_Ax1& theFacetNormal,
2346 TFacetOfElem & theFacets)
2348 #define THIS_METHOD "SMESH_MeshEditor::GetHexaFacetsToSplit(): "
2350 // Find a hexa closest to the location of theFacetNormal
2352 const SMDS_MeshElement* startHex;
2354 // get SMDS_ElemIteratorPtr on theHexas
2355 typedef const SMDS_MeshElement* TValue;
2356 typedef TIDSortedElemSet::iterator TSetIterator;
2357 typedef SMDS::SimpleAccessor<TValue,TSetIterator> TAccesor;
2358 typedef SMDS_MeshElement::GeomFilter TFilter;
2359 typedef SMDS_SetIterator < TValue, TSetIterator, TAccesor, TFilter > TElemSetIter;
2360 SMDS_ElemIteratorPtr elemIt = SMDS_ElemIteratorPtr
2361 ( new TElemSetIter( theHexas.begin(),
2363 SMDS_MeshElement::GeomFilter( SMDSGeom_HEXA )));
2365 SMESH_ElementSearcher* searcher =
2366 SMESH_MeshAlgos::GetElementSearcher( *myMesh->GetMeshDS(), elemIt );
2368 startHex = searcher->FindClosestTo( theFacetNormal.Location(), SMDSAbs_Volume );
2373 throw SALOME_Exception( THIS_METHOD "startHex not found");
2376 // Select a facet of startHex by theFacetNormal
2378 SMDS_VolumeTool vTool( startHex );
2379 double norm[3], dot, maxDot = 0;
2381 for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2382 if ( vTool.GetFaceNormal( iF, norm[0], norm[1], norm[2] ))
2384 dot = Abs( theFacetNormal.Direction().Dot( gp_Dir( norm[0], norm[1], norm[2] )));
2392 throw SALOME_Exception( THIS_METHOD "facet of startHex not found");
2394 // Fill theFacets starting from facetID of startHex
2396 // facets used for seach of volumes adjacent to already treated ones
2397 typedef pair< TFacetOfElem::iterator, int > TElemFacets;
2398 typedef map< TVolumeFaceKey, TElemFacets > TFacetMap;
2399 TFacetMap facetsToCheck;
2401 set<const SMDS_MeshNode*> facetNodes;
2402 const SMDS_MeshElement* curHex;
2404 const bool allHex = ( theHexas.size() == myMesh->NbHexas() );
2408 // move in two directions from startHex via facetID
2409 for ( int is2nd = 0; is2nd < 2; ++is2nd )
2412 int curFacet = facetID;
2413 if ( is2nd ) // do not treat startHex twice
2415 vTool.Set( curHex );
2416 if ( vTool.IsFreeFace( curFacet, &curHex ))
2422 vTool.GetFaceNodes( curFacet, facetNodes );
2423 vTool.Set( curHex );
2424 curFacet = vTool.GetFaceIndex( facetNodes );
2429 // store a facet to split
2430 if ( curHex->GetGeomType() != SMDSGeom_HEXA )
2432 theFacets.insert( make_pair( curHex, -1 ));
2435 if ( !allHex && !theHexas.count( curHex ))
2438 pair< TFacetOfElem::iterator, bool > facetIt2isNew =
2439 theFacets.insert( make_pair( curHex, curFacet ));
2440 if ( !facetIt2isNew.second )
2443 // remember not-to-split facets in facetsToCheck
2444 int oppFacet = vTool.GetOppFaceIndexOfHex( curFacet );
2445 for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2447 if ( iF == curFacet && iF == oppFacet )
2449 TVolumeFaceKey facetKey ( vTool, iF );
2450 TElemFacets elemFacet( facetIt2isNew.first, iF );
2451 pair< TFacetMap::iterator, bool > it2isnew =
2452 facetsToCheck.insert( make_pair( facetKey, elemFacet ));
2453 if ( !it2isnew.second )
2454 facetsToCheck.erase( it2isnew.first ); // adjacent hex already checked
2456 // pass to a volume adjacent via oppFacet
2457 if ( vTool.IsFreeFace( oppFacet, &curHex ))
2463 // get a new curFacet
2464 vTool.GetFaceNodes( oppFacet, facetNodes );
2465 vTool.Set( curHex );
2466 curFacet = vTool.GetFaceIndex( facetNodes, /*hint=*/curFacet );
2469 } // move in two directions from startHex via facetID
2471 // Find a new startHex by facetsToCheck
2475 TFacetMap::iterator fIt = facetsToCheck.begin();
2476 while ( !startHex && fIt != facetsToCheck.end() )
2478 const TElemFacets& elemFacets = fIt->second;
2479 const SMDS_MeshElement* hex = elemFacets.first->first;
2480 int splitFacet = elemFacets.first->second;
2481 int lateralFacet = elemFacets.second;
2482 facetsToCheck.erase( fIt );
2483 fIt = facetsToCheck.begin();
2486 if ( vTool.IsFreeFace( lateralFacet, &curHex ) ||
2487 curHex->GetGeomType() != SMDSGeom_HEXA )
2489 if ( !allHex && !theHexas.count( curHex ))
2494 // find a facet of startHex to split
2496 set<const SMDS_MeshNode*> lateralNodes;
2497 vTool.GetFaceNodes( lateralFacet, lateralNodes );
2498 vTool.GetFaceNodes( splitFacet, facetNodes );
2499 int oppLateralFacet = vTool.GetOppFaceIndexOfHex( lateralFacet );
2500 vTool.Set( startHex );
2501 lateralFacet = vTool.GetFaceIndex( lateralNodes, oppLateralFacet );
2503 // look for a facet of startHex having common nodes with facetNodes
2504 // but not lateralFacet
2505 for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2507 if ( iF == lateralFacet )
2509 int nbCommonNodes = 0;
2510 const SMDS_MeshNode** nn = vTool.GetFaceNodes( iF );
2511 for ( int iN = 0, nbN = vTool.NbFaceNodes( iF ); iN < nbN; ++iN )
2512 nbCommonNodes += facetNodes.count( nn[ iN ]);
2514 if ( nbCommonNodes >= 2 )
2521 throw SALOME_Exception( THIS_METHOD "facet of a new startHex not found");
2523 } // while ( startHex )
2526 //=======================================================================
2527 //function : AddToSameGroups
2528 //purpose : add elemToAdd to the groups the elemInGroups belongs to
2529 //=======================================================================
2531 void SMESH_MeshEditor::AddToSameGroups (const SMDS_MeshElement* elemToAdd,
2532 const SMDS_MeshElement* elemInGroups,
2533 SMESHDS_Mesh * aMesh)
2535 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2536 if (!groups.empty()) {
2537 set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2538 for ( ; grIt != groups.end(); grIt++ ) {
2539 SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2540 if ( group && group->Contains( elemInGroups ))
2541 group->SMDSGroup().Add( elemToAdd );
2547 //=======================================================================
2548 //function : RemoveElemFromGroups
2549 //purpose : Remove removeelem to the groups the elemInGroups belongs to
2550 //=======================================================================
2551 void SMESH_MeshEditor::RemoveElemFromGroups (const SMDS_MeshElement* removeelem,
2552 SMESHDS_Mesh * aMesh)
2554 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2555 if (!groups.empty())
2557 set<SMESHDS_GroupBase*>::const_iterator GrIt = groups.begin();
2558 for (; GrIt != groups.end(); GrIt++)
2560 SMESHDS_Group* grp = dynamic_cast<SMESHDS_Group*>(*GrIt);
2561 if (!grp || grp->IsEmpty()) continue;
2562 grp->SMDSGroup().Remove(removeelem);
2567 //================================================================================
2569 * \brief Replace elemToRm by elemToAdd in the all groups
2571 //================================================================================
2573 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement* elemToRm,
2574 const SMDS_MeshElement* elemToAdd,
2575 SMESHDS_Mesh * aMesh)
2577 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2578 if (!groups.empty()) {
2579 set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2580 for ( ; grIt != groups.end(); grIt++ ) {
2581 SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2582 if ( group && group->SMDSGroup().Remove( elemToRm ) && elemToAdd )
2583 group->SMDSGroup().Add( elemToAdd );
2588 //================================================================================
2590 * \brief Replace elemToRm by elemToAdd in the all groups
2592 //================================================================================
2594 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement* elemToRm,
2595 const vector<const SMDS_MeshElement*>& elemToAdd,
2596 SMESHDS_Mesh * aMesh)
2598 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2599 if (!groups.empty())
2601 set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2602 for ( ; grIt != groups.end(); grIt++ ) {
2603 SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2604 if ( group && group->SMDSGroup().Remove( elemToRm ) )
2605 for ( int i = 0; i < elemToAdd.size(); ++i )
2606 group->SMDSGroup().Add( elemToAdd[ i ] );
2611 //=======================================================================
2612 //function : QuadToTri
2613 //purpose : Cut quadrangles into triangles.
2614 // theCrit is used to select a diagonal to cut
2615 //=======================================================================
2617 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet & theElems,
2618 const bool the13Diag)
2620 myLastCreatedElems.Clear();
2621 myLastCreatedNodes.Clear();
2623 MESSAGE( "::QuadToTri()" );
2625 SMESHDS_Mesh * aMesh = GetMeshDS();
2627 Handle(Geom_Surface) surface;
2628 SMESH_MesherHelper helper( *GetMesh() );
2630 TIDSortedElemSet::iterator itElem;
2631 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
2632 const SMDS_MeshElement* elem = *itElem;
2633 if ( !elem || elem->GetType() != SMDSAbs_Face )
2635 bool isquad = elem->NbNodes()==4 || elem->NbNodes()==8;
2636 if(!isquad) continue;
2638 if(elem->NbNodes()==4) {
2639 // retrieve element nodes
2640 const SMDS_MeshNode* aNodes [4];
2641 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
2643 while ( itN->more() )
2644 aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
2646 int aShapeId = FindShape( elem );
2647 const SMDS_MeshElement* newElem1 = 0;
2648 const SMDS_MeshElement* newElem2 = 0;
2650 newElem1 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
2651 newElem2 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
2654 newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
2655 newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
2657 myLastCreatedElems.Append(newElem1);
2658 myLastCreatedElems.Append(newElem2);
2659 // put a new triangle on the same shape and add to the same groups
2662 aMesh->SetMeshElementOnShape( newElem1, aShapeId );
2663 aMesh->SetMeshElementOnShape( newElem2, aShapeId );
2665 AddToSameGroups( newElem1, elem, aMesh );
2666 AddToSameGroups( newElem2, elem, aMesh );
2667 //aMesh->RemoveFreeElement(elem, aMesh->MeshElements(aShapeId), true);
2668 aMesh->RemoveElement( elem );
2671 // Quadratic quadrangle
2673 if( elem->NbNodes()==8 && elem->IsQuadratic() ) {
2675 // get surface elem is on
2676 int aShapeId = FindShape( elem );
2677 if ( aShapeId != helper.GetSubShapeID() ) {
2681 shape = aMesh->IndexToShape( aShapeId );
2682 if ( !shape.IsNull() && shape.ShapeType() == TopAbs_FACE ) {
2683 TopoDS_Face face = TopoDS::Face( shape );
2684 surface = BRep_Tool::Surface( face );
2685 if ( !surface.IsNull() )
2686 helper.SetSubShape( shape );
2690 const SMDS_MeshNode* aNodes [8];
2691 const SMDS_MeshNode* inFaceNode = 0;
2692 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
2694 while ( itN->more() ) {
2695 aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
2696 if ( !inFaceNode && helper.GetNodeUVneedInFaceNode() &&
2697 aNodes[ i-1 ]->GetPosition()->GetTypeOfPosition() == SMDS_TOP_FACE )
2699 inFaceNode = aNodes[ i-1 ];
2703 // find middle point for (0,1,2,3)
2704 // and create a node in this point;
2706 if ( surface.IsNull() ) {
2708 p += gp_XYZ(aNodes[i]->X(), aNodes[i]->Y(), aNodes[i]->Z() );
2712 TopoDS_Face geomFace = TopoDS::Face( helper.GetSubShape() );
2715 uv += helper.GetNodeUV( geomFace, aNodes[i], inFaceNode );
2717 p = surface->Value( uv.X(), uv.Y() ).XYZ();
2719 const SMDS_MeshNode* newN = aMesh->AddNode( p.X(), p.Y(), p.Z() );
2720 myLastCreatedNodes.Append(newN);
2722 // create a new element
2723 const SMDS_MeshElement* newElem1 = 0;
2724 const SMDS_MeshElement* newElem2 = 0;
2726 newElem1 = aMesh->AddFace(aNodes[2], aNodes[3], aNodes[0],
2727 aNodes[6], aNodes[7], newN );
2728 newElem2 = aMesh->AddFace(aNodes[2], aNodes[0], aNodes[1],
2729 newN, aNodes[4], aNodes[5] );
2732 newElem1 = aMesh->AddFace(aNodes[3], aNodes[0], aNodes[1],
2733 aNodes[7], aNodes[4], newN );
2734 newElem2 = aMesh->AddFace(aNodes[3], aNodes[1], aNodes[2],
2735 newN, aNodes[5], aNodes[6] );
2737 myLastCreatedElems.Append(newElem1);
2738 myLastCreatedElems.Append(newElem2);
2739 // put a new triangle on the same shape and add to the same groups
2742 aMesh->SetMeshElementOnShape( newElem1, aShapeId );
2743 aMesh->SetMeshElementOnShape( newElem2, aShapeId );
2745 AddToSameGroups( newElem1, elem, aMesh );
2746 AddToSameGroups( newElem2, elem, aMesh );
2747 aMesh->RemoveElement( elem );
2754 //=======================================================================
2755 //function : getAngle
2757 //=======================================================================
2759 double getAngle(const SMDS_MeshElement * tr1,
2760 const SMDS_MeshElement * tr2,
2761 const SMDS_MeshNode * n1,
2762 const SMDS_MeshNode * n2)
2764 double angle = 2. * M_PI; // bad angle
2767 SMESH::Controls::TSequenceOfXYZ P1, P2;
2768 if ( !SMESH::Controls::NumericalFunctor::GetPoints( tr1, P1 ) ||
2769 !SMESH::Controls::NumericalFunctor::GetPoints( tr2, P2 ))
2772 if(!tr1->IsQuadratic())
2773 N1 = gp_Vec( P1(2) - P1(1) ) ^ gp_Vec( P1(3) - P1(1) );
2775 N1 = gp_Vec( P1(3) - P1(1) ) ^ gp_Vec( P1(5) - P1(1) );
2776 if ( N1.SquareMagnitude() <= gp::Resolution() )
2778 if(!tr2->IsQuadratic())
2779 N2 = gp_Vec( P2(2) - P2(1) ) ^ gp_Vec( P2(3) - P2(1) );
2781 N2 = gp_Vec( P2(3) - P2(1) ) ^ gp_Vec( P2(5) - P2(1) );
2782 if ( N2.SquareMagnitude() <= gp::Resolution() )
2785 // find the first diagonal node n1 in the triangles:
2786 // take in account a diagonal link orientation
2787 const SMDS_MeshElement *nFirst[2], *tr[] = { tr1, tr2 };
2788 for ( int t = 0; t < 2; t++ ) {
2789 SMDS_ElemIteratorPtr it = tr[ t ]->nodesIterator();
2790 int i = 0, iDiag = -1;
2791 while ( it->more()) {
2792 const SMDS_MeshElement *n = it->next();
2793 if ( n == n1 || n == n2 ) {
2797 if ( i - iDiag == 1 )
2798 nFirst[ t ] = ( n == n1 ? n2 : n1 );
2807 if ( nFirst[ 0 ] == nFirst[ 1 ] )
2810 angle = N1.Angle( N2 );
2815 // =================================================
2816 // class generating a unique ID for a pair of nodes
2817 // and able to return nodes by that ID
2818 // =================================================
2822 LinkID_Gen( const SMESHDS_Mesh* theMesh )
2823 :myMesh( theMesh ), myMaxID( theMesh->MaxNodeID() + 1)
2826 long GetLinkID (const SMDS_MeshNode * n1,
2827 const SMDS_MeshNode * n2) const
2829 return ( Min(n1->GetID(),n2->GetID()) * myMaxID + Max(n1->GetID(),n2->GetID()));
2832 bool GetNodes (const long theLinkID,
2833 const SMDS_MeshNode* & theNode1,
2834 const SMDS_MeshNode* & theNode2) const
2836 theNode1 = myMesh->FindNode( theLinkID / myMaxID );
2837 if ( !theNode1 ) return false;
2838 theNode2 = myMesh->FindNode( theLinkID % myMaxID );
2839 if ( !theNode2 ) return false;
2845 const SMESHDS_Mesh* myMesh;
2850 //=======================================================================
2851 //function : TriToQuad
2852 //purpose : Fuse neighbour triangles into quadrangles.
2853 // theCrit is used to select a neighbour to fuse with.
2854 // theMaxAngle is a max angle between element normals at which
2855 // fusion is still performed.
2856 //=======================================================================
2858 bool SMESH_MeshEditor::TriToQuad (TIDSortedElemSet & theElems,
2859 SMESH::Controls::NumericalFunctorPtr theCrit,
2860 const double theMaxAngle)
2862 myLastCreatedElems.Clear();
2863 myLastCreatedNodes.Clear();
2865 MESSAGE( "::TriToQuad()" );
2867 if ( !theCrit.get() )
2870 SMESHDS_Mesh * aMesh = GetMeshDS();
2872 // Prepare data for algo: build
2873 // 1. map of elements with their linkIDs
2874 // 2. map of linkIDs with their elements
2876 map< SMESH_TLink, list< const SMDS_MeshElement* > > mapLi_listEl;
2877 map< SMESH_TLink, list< const SMDS_MeshElement* > >::iterator itLE;
2878 map< const SMDS_MeshElement*, set< SMESH_TLink > > mapEl_setLi;
2879 map< const SMDS_MeshElement*, set< SMESH_TLink > >::iterator itEL;
2881 TIDSortedElemSet::iterator itElem;
2882 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
2884 const SMDS_MeshElement* elem = *itElem;
2885 if(!elem || elem->GetType() != SMDSAbs_Face ) continue;
2886 bool IsTria = ( elem->NbCornerNodes()==3 );
2887 if (!IsTria) continue;
2889 // retrieve element nodes
2890 const SMDS_MeshNode* aNodes [4];
2891 SMDS_NodeIteratorPtr itN = elem->nodeIterator();
2894 aNodes[ i++ ] = itN->next();
2895 aNodes[ 3 ] = aNodes[ 0 ];
2898 for ( i = 0; i < 3; i++ ) {
2899 SMESH_TLink link( aNodes[i], aNodes[i+1] );
2900 // check if elements sharing a link can be fused
2901 itLE = mapLi_listEl.find( link );
2902 if ( itLE != mapLi_listEl.end() ) {
2903 if ((*itLE).second.size() > 1 ) // consider only 2 elems adjacent by a link
2905 const SMDS_MeshElement* elem2 = (*itLE).second.front();
2906 //if ( FindShape( elem ) != FindShape( elem2 ))
2907 // continue; // do not fuse triangles laying on different shapes
2908 if ( getAngle( elem, elem2, aNodes[i], aNodes[i+1] ) > theMaxAngle )
2909 continue; // avoid making badly shaped quads
2910 (*itLE).second.push_back( elem );
2913 mapLi_listEl[ link ].push_back( elem );
2915 mapEl_setLi [ elem ].insert( link );
2918 // Clean the maps from the links shared by a sole element, ie
2919 // links to which only one element is bound in mapLi_listEl
2921 for ( itLE = mapLi_listEl.begin(); itLE != mapLi_listEl.end(); itLE++ ) {
2922 int nbElems = (*itLE).second.size();
2923 if ( nbElems < 2 ) {
2924 const SMDS_MeshElement* elem = (*itLE).second.front();
2925 SMESH_TLink link = (*itLE).first;
2926 mapEl_setLi[ elem ].erase( link );
2927 if ( mapEl_setLi[ elem ].empty() )
2928 mapEl_setLi.erase( elem );
2932 // Algo: fuse triangles into quadrangles
2934 while ( ! mapEl_setLi.empty() ) {
2935 // Look for the start element:
2936 // the element having the least nb of shared links
2937 const SMDS_MeshElement* startElem = 0;
2939 for ( itEL = mapEl_setLi.begin(); itEL != mapEl_setLi.end(); itEL++ ) {
2940 int nbLinks = (*itEL).second.size();
2941 if ( nbLinks < minNbLinks ) {
2942 startElem = (*itEL).first;
2943 minNbLinks = nbLinks;
2944 if ( minNbLinks == 1 )
2949 // search elements to fuse starting from startElem or links of elements
2950 // fused earlyer - startLinks
2951 list< SMESH_TLink > startLinks;
2952 while ( startElem || !startLinks.empty() ) {
2953 while ( !startElem && !startLinks.empty() ) {
2954 // Get an element to start, by a link
2955 SMESH_TLink linkId = startLinks.front();
2956 startLinks.pop_front();
2957 itLE = mapLi_listEl.find( linkId );
2958 if ( itLE != mapLi_listEl.end() ) {
2959 list< const SMDS_MeshElement* > & listElem = (*itLE).second;
2960 list< const SMDS_MeshElement* >::iterator itE = listElem.begin();
2961 for ( ; itE != listElem.end() ; itE++ )
2962 if ( mapEl_setLi.find( (*itE) ) != mapEl_setLi.end() )
2964 mapLi_listEl.erase( itLE );
2969 // Get candidates to be fused
2970 const SMDS_MeshElement *tr1 = startElem, *tr2 = 0, *tr3 = 0;
2971 const SMESH_TLink *link12, *link13;
2973 ASSERT( mapEl_setLi.find( tr1 ) != mapEl_setLi.end() );
2974 set< SMESH_TLink >& setLi = mapEl_setLi[ tr1 ];
2975 ASSERT( !setLi.empty() );
2976 set< SMESH_TLink >::iterator itLi;
2977 for ( itLi = setLi.begin(); itLi != setLi.end(); itLi++ )
2979 const SMESH_TLink & link = (*itLi);
2980 itLE = mapLi_listEl.find( link );
2981 if ( itLE == mapLi_listEl.end() )
2984 const SMDS_MeshElement* elem = (*itLE).second.front();
2986 elem = (*itLE).second.back();
2987 mapLi_listEl.erase( itLE );
2988 if ( mapEl_setLi.find( elem ) == mapEl_setLi.end())
2999 // add other links of elem to list of links to re-start from
3000 set< SMESH_TLink >& links = mapEl_setLi[ elem ];
3001 set< SMESH_TLink >::iterator it;
3002 for ( it = links.begin(); it != links.end(); it++ ) {
3003 const SMESH_TLink& link2 = (*it);
3004 if ( link2 != link )
3005 startLinks.push_back( link2 );
3009 // Get nodes of possible quadrangles
3010 const SMDS_MeshNode *n12 [4], *n13 [4];
3011 bool Ok12 = false, Ok13 = false;
3012 const SMDS_MeshNode *linkNode1, *linkNode2;
3014 linkNode1 = link12->first;
3015 linkNode2 = link12->second;
3016 if ( tr2 && getQuadrangleNodes( n12, linkNode1, linkNode2, tr1, tr2 ))
3020 linkNode1 = link13->first;
3021 linkNode2 = link13->second;
3022 if ( tr3 && getQuadrangleNodes( n13, linkNode1, linkNode2, tr1, tr3 ))
3026 // Choose a pair to fuse
3027 if ( Ok12 && Ok13 ) {
3028 SMDS_FaceOfNodes quad12 ( n12[ 0 ], n12[ 1 ], n12[ 2 ], n12[ 3 ] );
3029 SMDS_FaceOfNodes quad13 ( n13[ 0 ], n13[ 1 ], n13[ 2 ], n13[ 3 ] );
3030 double aBadRate12 = getBadRate( &quad12, theCrit );
3031 double aBadRate13 = getBadRate( &quad13, theCrit );
3032 if ( aBadRate13 < aBadRate12 )
3039 // and remove fused elems and remove links from the maps
3040 mapEl_setLi.erase( tr1 );
3043 mapEl_setLi.erase( tr2 );
3044 mapLi_listEl.erase( *link12 );
3045 if ( tr1->NbNodes() == 3 )
3047 const SMDS_MeshElement* newElem = 0;
3048 newElem = aMesh->AddFace(n12[0], n12[1], n12[2], n12[3] );
3049 myLastCreatedElems.Append(newElem);
3050 AddToSameGroups( newElem, tr1, aMesh );
3051 int aShapeId = tr1->getshapeId();
3053 aMesh->SetMeshElementOnShape( newElem, aShapeId );
3054 aMesh->RemoveElement( tr1 );
3055 aMesh->RemoveElement( tr2 );
3058 vector< const SMDS_MeshNode* > N1;
3059 vector< const SMDS_MeshNode* > N2;
3060 getNodesFromTwoTria(tr1,tr2,N1,N2);
3061 // now we receive following N1 and N2 (using numeration as in image in InverseDiag())
3062 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
3063 // i.e. first nodes from both arrays form a new diagonal
3064 const SMDS_MeshNode* aNodes[8];
3073 const SMDS_MeshElement* newElem = 0;
3074 if ( N1.size() == 7 || N2.size() == 7 ) // biquadratic
3075 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3076 aNodes[4], aNodes[5], aNodes[6], aNodes[7], N1[4]);
3078 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3079 aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
3080 myLastCreatedElems.Append(newElem);
3081 AddToSameGroups( newElem, tr1, aMesh );
3082 int aShapeId = tr1->getshapeId();
3084 aMesh->SetMeshElementOnShape( newElem, aShapeId );
3085 aMesh->RemoveElement( tr1 );
3086 aMesh->RemoveElement( tr2 );
3087 // remove middle node (9)
3088 if ( N1[4]->NbInverseElements() == 0 )
3089 aMesh->RemoveNode( N1[4] );
3090 if ( N1.size() == 7 && N1[6]->NbInverseElements() == 0 )
3091 aMesh->RemoveNode( N1[6] );
3092 if ( N2.size() == 7 && N2[6]->NbInverseElements() == 0 )
3093 aMesh->RemoveNode( N2[6] );
3098 mapEl_setLi.erase( tr3 );
3099 mapLi_listEl.erase( *link13 );
3100 if ( tr1->NbNodes() == 3 ) {
3101 const SMDS_MeshElement* newElem = 0;
3102 newElem = aMesh->AddFace(n13[0], n13[1], n13[2], n13[3] );
3103 myLastCreatedElems.Append(newElem);
3104 AddToSameGroups( newElem, tr1, aMesh );
3105 int aShapeId = tr1->getshapeId();
3107 aMesh->SetMeshElementOnShape( newElem, aShapeId );
3108 aMesh->RemoveElement( tr1 );
3109 aMesh->RemoveElement( tr3 );
3112 vector< const SMDS_MeshNode* > N1;
3113 vector< const SMDS_MeshNode* > N2;
3114 getNodesFromTwoTria(tr1,tr3,N1,N2);
3115 // now we receive following N1 and N2 (using numeration as above image)
3116 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
3117 // i.e. first nodes from both arrays form a new diagonal
3118 const SMDS_MeshNode* aNodes[8];
3127 const SMDS_MeshElement* newElem = 0;
3128 if ( N1.size() == 7 || N2.size() == 7 ) // biquadratic
3129 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3130 aNodes[4], aNodes[5], aNodes[6], aNodes[7], N1[4]);
3132 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3133 aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
3134 myLastCreatedElems.Append(newElem);
3135 AddToSameGroups( newElem, tr1, aMesh );
3136 int aShapeId = tr1->getshapeId();
3138 aMesh->SetMeshElementOnShape( newElem, aShapeId );
3139 aMesh->RemoveElement( tr1 );
3140 aMesh->RemoveElement( tr3 );
3141 // remove middle node (9)
3142 if ( N1[4]->NbInverseElements() == 0 )
3143 aMesh->RemoveNode( N1[4] );
3144 if ( N1.size() == 7 && N1[6]->NbInverseElements() == 0 )
3145 aMesh->RemoveNode( N1[6] );
3146 if ( N2.size() == 7 && N2[6]->NbInverseElements() == 0 )
3147 aMesh->RemoveNode( N2[6] );
3151 // Next element to fuse: the rejected one
3153 startElem = Ok12 ? tr3 : tr2;
3155 } // if ( startElem )
3156 } // while ( startElem || !startLinks.empty() )
3157 } // while ( ! mapEl_setLi.empty() )
3163 /*#define DUMPSO(txt) \
3164 // cout << txt << endl;
3165 //=============================================================================
3169 //=============================================================================
3170 static void swap( int i1, int i2, int idNodes[], gp_Pnt P[] )
3174 int tmp = idNodes[ i1 ];
3175 idNodes[ i1 ] = idNodes[ i2 ];
3176 idNodes[ i2 ] = tmp;
3177 gp_Pnt Ptmp = P[ i1 ];
3180 DUMPSO( i1 << "(" << idNodes[ i2 ] << ") <-> " << i2 << "(" << idNodes[ i1 ] << ")");
3183 //=======================================================================
3184 //function : SortQuadNodes
3185 //purpose : Set 4 nodes of a quadrangle face in a good order.
3186 // Swap 1<->2 or 2<->3 nodes and correspondingly return
3188 //=======================================================================
3190 int SMESH_MeshEditor::SortQuadNodes (const SMDS_Mesh * theMesh,
3195 for ( i = 0; i < 4; i++ ) {
3196 const SMDS_MeshNode *n = theMesh->FindNode( idNodes[i] );
3198 P[ i ].SetCoord( n->X(), n->Y(), n->Z() );
3201 gp_Vec V1(P[0], P[1]);
3202 gp_Vec V2(P[0], P[2]);
3203 gp_Vec V3(P[0], P[3]);
3205 gp_Vec Cross1 = V1 ^ V2;
3206 gp_Vec Cross2 = V2 ^ V3;
3209 if (Cross1.Dot(Cross2) < 0)
3214 if (Cross1.Dot(Cross2) < 0)
3218 swap ( i, i + 1, idNodes, P );
3220 // for ( int ii = 0; ii < 4; ii++ ) {
3221 // const SMDS_MeshNode *n = theMesh->FindNode( idNodes[ii] );
3222 // DUMPSO( ii << "(" << idNodes[ii] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
3228 //=======================================================================
3229 //function : SortHexaNodes
3230 //purpose : Set 8 nodes of a hexahedron in a good order.
3231 // Return success status
3232 //=======================================================================
3234 bool SMESH_MeshEditor::SortHexaNodes (const SMDS_Mesh * theMesh,
3239 DUMPSO( "INPUT: ========================================");
3240 for ( i = 0; i < 8; i++ ) {
3241 const SMDS_MeshNode *n = theMesh->FindNode( idNodes[i] );
3242 if ( !n ) return false;
3243 P[ i ].SetCoord( n->X(), n->Y(), n->Z() );
3244 DUMPSO( i << "(" << idNodes[i] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
3246 DUMPSO( "========================================");
3249 set<int> faceNodes; // ids of bottom face nodes, to be found
3250 set<int> checkedId1; // ids of tried 2-nd nodes
3251 Standard_Real leastDist = DBL_MAX; // dist of the 4-th node from 123 plane
3252 const Standard_Real tol = 1.e-6; // tolerance to find nodes in plane
3253 int iMin, iLoop1 = 0;
3255 // Loop to try the 2-nd nodes
3257 while ( leastDist > DBL_MIN && ++iLoop1 < 8 )
3259 // Find not checked 2-nd node
3260 for ( i = 1; i < 8; i++ )
3261 if ( checkedId1.find( idNodes[i] ) == checkedId1.end() ) {
3262 int id1 = idNodes[i];
3263 swap ( 1, i, idNodes, P );
3264 checkedId1.insert ( id1 );
3268 // Find the 3-d node so that 1-2-3 triangle to be on a hexa face,
3269 // ie that all but meybe one (id3 which is on the same face) nodes
3270 // lay on the same side from the triangle plane.
3272 bool manyInPlane = false; // more than 4 nodes lay in plane
3274 while ( ++iLoop2 < 6 ) {
3276 // get 1-2-3 plane coeffs
3277 Standard_Real A, B, C, D;
3278 gp_Vec N = gp_Vec (P[0], P[1]).Crossed( gp_Vec (P[0], P[2]) );
3279 if ( N.SquareMagnitude() > gp::Resolution() )
3281 gp_Pln pln ( P[0], N );
3282 pln.Coefficients( A, B, C, D );
3284 // find the node (iMin) closest to pln
3285 Standard_Real dist[ 8 ], minDist = DBL_MAX;
3287 for ( i = 3; i < 8; i++ ) {
3288 dist[i] = A * P[i].X() + B * P[i].Y() + C * P[i].Z() + D;
3289 if ( fabs( dist[i] ) < minDist ) {
3290 minDist = fabs( dist[i] );
3293 if ( fabs( dist[i] ) <= tol )
3294 idInPln.insert( idNodes[i] );
3297 // there should not be more than 4 nodes in bottom plane
3298 if ( idInPln.size() > 1 )
3300 DUMPSO( "### idInPln.size() = " << idInPln.size());
3301 // idInPlane does not contain the first 3 nodes
3302 if ( manyInPlane || idInPln.size() == 5)
3303 return false; // all nodes in one plane
3306 // set the 1-st node to be not in plane
3307 for ( i = 3; i < 8; i++ ) {
3308 if ( idInPln.find( idNodes[ i ] ) == idInPln.end() ) {
3309 DUMPSO( "### Reset 0-th node");
3310 swap( 0, i, idNodes, P );
3315 // reset to re-check second nodes
3316 leastDist = DBL_MAX;
3320 break; // from iLoop2;
3323 // check that the other 4 nodes are on the same side
3324 bool sameSide = true;
3325 bool isNeg = dist[ iMin == 3 ? 4 : 3 ] <= 0.;
3326 for ( i = 3; sameSide && i < 8; i++ ) {
3328 sameSide = ( isNeg == dist[i] <= 0.);
3331 // keep best solution
3332 if ( sameSide && minDist < leastDist ) {
3333 leastDist = minDist;
3335 faceNodes.insert( idNodes[ 1 ] );
3336 faceNodes.insert( idNodes[ 2 ] );
3337 faceNodes.insert( idNodes[ iMin ] );
3338 DUMPSO( "loop " << iLoop2 << " id2 " << idNodes[ 1 ] << " id3 " << idNodes[ 2 ]
3339 << " leastDist = " << leastDist);
3340 if ( leastDist <= DBL_MIN )
3345 // set next 3-d node to check
3346 int iNext = 2 + iLoop2;
3348 DUMPSO( "Try 2-nd");
3349 swap ( 2, iNext, idNodes, P );
3351 } // while ( iLoop2 < 6 )
3354 if ( faceNodes.empty() ) return false;
3356 // Put the faceNodes in proper places
3357 for ( i = 4; i < 8; i++ ) {
3358 if ( faceNodes.find( idNodes[ i ] ) != faceNodes.end() ) {
3359 // find a place to put
3361 while ( faceNodes.find( idNodes[ iTo ] ) != faceNodes.end() )
3363 DUMPSO( "Set faceNodes");
3364 swap ( iTo, i, idNodes, P );
3369 // Set nodes of the found bottom face in good order
3370 DUMPSO( " Found bottom face: ");
3371 i = SortQuadNodes( theMesh, idNodes );
3373 gp_Pnt Ptmp = P[ i ];
3378 // for ( int ii = 0; ii < 4; ii++ ) {
3379 // const SMDS_MeshNode *n = theMesh->FindNode( idNodes[ii] );
3380 // DUMPSO( ii << "(" << idNodes[ii] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
3383 // Gravity center of the top and bottom faces
3384 gp_Pnt aGCb = ( P[0].XYZ() + P[1].XYZ() + P[2].XYZ() + P[3].XYZ() ) / 4.;
3385 gp_Pnt aGCt = ( P[4].XYZ() + P[5].XYZ() + P[6].XYZ() + P[7].XYZ() ) / 4.;
3387 // Get direction from the bottom to the top face
3388 gp_Vec upDir ( aGCb, aGCt );
3389 Standard_Real upDirSize = upDir.Magnitude();
3390 if ( upDirSize <= gp::Resolution() ) return false;
3393 // Assure that the bottom face normal points up
3394 gp_Vec Nb = gp_Vec (P[0], P[1]).Crossed( gp_Vec (P[0], P[2]) );
3395 Nb += gp_Vec (P[0], P[2]).Crossed( gp_Vec (P[0], P[3]) );
3396 if ( Nb.Dot( upDir ) < 0 ) {
3397 DUMPSO( "Reverse bottom face");
3398 swap( 1, 3, idNodes, P );
3401 // Find 5-th node - the one closest to the 1-st among the last 4 nodes.
3402 Standard_Real minDist = DBL_MAX;
3403 for ( i = 4; i < 8; i++ ) {
3404 // projection of P[i] to the plane defined by P[0] and upDir
3405 gp_Pnt Pp = P[i].Translated( upDir * ( upDir.Dot( gp_Vec( P[i], P[0] ))));
3406 Standard_Real sqDist = P[0].SquareDistance( Pp );
3407 if ( sqDist < minDist ) {
3412 DUMPSO( "Set 4-th");
3413 swap ( 4, iMin, idNodes, P );
3415 // Set nodes of the top face in good order
3416 DUMPSO( "Sort top face");
3417 i = SortQuadNodes( theMesh, &idNodes[4] );
3420 gp_Pnt Ptmp = P[ i ];
3425 // Assure that direction of the top face normal is from the bottom face
3426 gp_Vec Nt = gp_Vec (P[4], P[5]).Crossed( gp_Vec (P[4], P[6]) );
3427 Nt += gp_Vec (P[4], P[6]).Crossed( gp_Vec (P[4], P[7]) );
3428 if ( Nt.Dot( upDir ) < 0 ) {
3429 DUMPSO( "Reverse top face");
3430 swap( 5, 7, idNodes, P );
3433 // DUMPSO( "OUTPUT: ========================================");
3434 // for ( i = 0; i < 8; i++ ) {
3435 // float *p = ugrid->GetPoint(idNodes[i]);
3436 // DUMPSO( i << "(" << idNodes[i] << ") : " << p[0] << " " << p[1] << " " << p[2]);
3442 //================================================================================
3444 * \brief Return nodes linked to the given one
3445 * \param theNode - the node
3446 * \param linkedNodes - the found nodes
3447 * \param type - the type of elements to check
3449 * Medium nodes are ignored
3451 //================================================================================
3453 void SMESH_MeshEditor::GetLinkedNodes( const SMDS_MeshNode* theNode,
3454 TIDSortedElemSet & linkedNodes,
3455 SMDSAbs_ElementType type )
3457 SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(type);
3458 while ( elemIt->more() )
3460 const SMDS_MeshElement* elem = elemIt->next();
3461 if(elem->GetType() == SMDSAbs_0DElement)
3464 SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
3465 if ( elem->GetType() == SMDSAbs_Volume )
3467 SMDS_VolumeTool vol( elem );
3468 while ( nodeIt->more() ) {
3469 const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
3470 if ( theNode != n && vol.IsLinked( theNode, n ))
3471 linkedNodes.insert( n );
3476 for ( int i = 0; nodeIt->more(); ++i ) {
3477 const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
3478 if ( n == theNode ) {
3479 int iBefore = i - 1;
3481 if ( elem->IsQuadratic() ) {
3482 int nb = elem->NbNodes() / 2;
3483 iAfter = SMESH_MesherHelper::WrapIndex( iAfter, nb );
3484 iBefore = SMESH_MesherHelper::WrapIndex( iBefore, nb );
3486 linkedNodes.insert( elem->GetNodeWrap( iAfter ));
3487 linkedNodes.insert( elem->GetNodeWrap( iBefore ));
3494 //=======================================================================
3495 //function : laplacianSmooth
3496 //purpose : pulls theNode toward the center of surrounding nodes directly
3497 // connected to that node along an element edge
3498 //=======================================================================
3500 void laplacianSmooth(const SMDS_MeshNode* theNode,
3501 const Handle(Geom_Surface)& theSurface,
3502 map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
3504 // find surrounding nodes
3506 TIDSortedElemSet nodeSet;
3507 SMESH_MeshEditor::GetLinkedNodes( theNode, nodeSet, SMDSAbs_Face );
3509 // compute new coodrs
3511 double coord[] = { 0., 0., 0. };
3512 TIDSortedElemSet::iterator nodeSetIt = nodeSet.begin();
3513 for ( ; nodeSetIt != nodeSet.end(); nodeSetIt++ ) {
3514 const SMDS_MeshNode* node = cast2Node(*nodeSetIt);
3515 if ( theSurface.IsNull() ) { // smooth in 3D
3516 coord[0] += node->X();
3517 coord[1] += node->Y();
3518 coord[2] += node->Z();
3520 else { // smooth in 2D
3521 ASSERT( theUVMap.find( node ) != theUVMap.end() );
3522 gp_XY* uv = theUVMap[ node ];
3523 coord[0] += uv->X();
3524 coord[1] += uv->Y();
3527 int nbNodes = nodeSet.size();
3530 coord[0] /= nbNodes;
3531 coord[1] /= nbNodes;
3533 if ( !theSurface.IsNull() ) {
3534 ASSERT( theUVMap.find( theNode ) != theUVMap.end() );
3535 theUVMap[ theNode ]->SetCoord( coord[0], coord[1] );
3536 gp_Pnt p3d = theSurface->Value( coord[0], coord[1] );
3542 coord[2] /= nbNodes;
3546 const_cast< SMDS_MeshNode* >( theNode )->setXYZ(coord[0],coord[1],coord[2]);
3549 //=======================================================================
3550 //function : centroidalSmooth
3551 //purpose : pulls theNode toward the element-area-weighted centroid of the
3552 // surrounding elements
3553 //=======================================================================
3555 void centroidalSmooth(const SMDS_MeshNode* theNode,
3556 const Handle(Geom_Surface)& theSurface,
3557 map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
3559 gp_XYZ aNewXYZ(0.,0.,0.);
3560 SMESH::Controls::Area anAreaFunc;
3561 double totalArea = 0.;
3566 SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(SMDSAbs_Face);
3567 while ( elemIt->more() )
3569 const SMDS_MeshElement* elem = elemIt->next();
3572 gp_XYZ elemCenter(0.,0.,0.);
3573 SMESH::Controls::TSequenceOfXYZ aNodePoints;
3574 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3575 int nn = elem->NbNodes();
3576 if(elem->IsQuadratic()) nn = nn/2;
3578 //while ( itN->more() ) {
3580 const SMDS_MeshNode* aNode = static_cast<const SMDS_MeshNode*>( itN->next() );
3582 gp_XYZ aP( aNode->X(), aNode->Y(), aNode->Z() );
3583 aNodePoints.push_back( aP );
3584 if ( !theSurface.IsNull() ) { // smooth in 2D
3585 ASSERT( theUVMap.find( aNode ) != theUVMap.end() );
3586 gp_XY* uv = theUVMap[ aNode ];
3587 aP.SetCoord( uv->X(), uv->Y(), 0. );
3591 double elemArea = anAreaFunc.GetValue( aNodePoints );
3592 totalArea += elemArea;
3594 aNewXYZ += elemCenter * elemArea;
3596 aNewXYZ /= totalArea;
3597 if ( !theSurface.IsNull() ) {
3598 theUVMap[ theNode ]->SetCoord( aNewXYZ.X(), aNewXYZ.Y() );
3599 aNewXYZ = theSurface->Value( aNewXYZ.X(), aNewXYZ.Y() ).XYZ();
3604 const_cast< SMDS_MeshNode* >( theNode )->setXYZ(aNewXYZ.X(),aNewXYZ.Y(),aNewXYZ.Z());
3607 //=======================================================================
3608 //function : getClosestUV
3609 //purpose : return UV of closest projection
3610 //=======================================================================
3612 static bool getClosestUV (Extrema_GenExtPS& projector,
3613 const gp_Pnt& point,
3616 projector.Perform( point );
3617 if ( projector.IsDone() ) {
3618 double u, v, minVal = DBL_MAX;
3619 for ( int i = projector.NbExt(); i > 0; i-- )
3620 #if OCC_VERSION_LARGE > 0x06040000 // Porting to OCCT6.5.1
3621 if ( projector.SquareDistance( i ) < minVal ) {
3622 minVal = projector.SquareDistance( i );
3624 if ( projector.Value( i ) < minVal ) {
3625 minVal = projector.Value( i );
3627 projector.Point( i ).Parameter( u, v );
3629 result.SetCoord( u, v );
3635 //=======================================================================
3637 //purpose : Smooth theElements during theNbIterations or until a worst
3638 // element has aspect ratio <= theTgtAspectRatio.
3639 // Aspect Ratio varies in range [1.0, inf].
3640 // If theElements is empty, the whole mesh is smoothed.
3641 // theFixedNodes contains additionally fixed nodes. Nodes built
3642 // on edges and boundary nodes are always fixed.
3643 //=======================================================================
3645 void SMESH_MeshEditor::Smooth (TIDSortedElemSet & theElems,
3646 set<const SMDS_MeshNode*> & theFixedNodes,
3647 const SmoothMethod theSmoothMethod,
3648 const int theNbIterations,
3649 double theTgtAspectRatio,
3652 myLastCreatedElems.Clear();
3653 myLastCreatedNodes.Clear();
3655 MESSAGE((theSmoothMethod==LAPLACIAN ? "LAPLACIAN" : "CENTROIDAL") << "--::Smooth()");
3657 if ( theTgtAspectRatio < 1.0 )
3658 theTgtAspectRatio = 1.0;
3660 const double disttol = 1.e-16;
3662 SMESH::Controls::AspectRatio aQualityFunc;
3664 SMESHDS_Mesh* aMesh = GetMeshDS();
3666 if ( theElems.empty() ) {
3667 // add all faces to theElems
3668 SMDS_FaceIteratorPtr fIt = aMesh->facesIterator();
3669 while ( fIt->more() ) {
3670 const SMDS_MeshElement* face = fIt->next();
3671 theElems.insert( theElems.end(), face );
3674 // get all face ids theElems are on
3675 set< int > faceIdSet;
3676 TIDSortedElemSet::iterator itElem;
3678 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
3679 int fId = FindShape( *itElem );
3680 // check that corresponding submesh exists and a shape is face
3682 faceIdSet.find( fId ) == faceIdSet.end() &&
3683 aMesh->MeshElements( fId )) {
3684 TopoDS_Shape F = aMesh->IndexToShape( fId );
3685 if ( !F.IsNull() && F.ShapeType() == TopAbs_FACE )
3686 faceIdSet.insert( fId );
3689 faceIdSet.insert( 0 ); // to smooth elements that are not on any TopoDS_Face
3691 // ===============================================
3692 // smooth elements on each TopoDS_Face separately
3693 // ===============================================
3695 set< int >::reverse_iterator fId = faceIdSet.rbegin(); // treate 0 fId at the end
3696 for ( ; fId != faceIdSet.rend(); ++fId ) {
3697 // get face surface and submesh
3698 Handle(Geom_Surface) surface;
3699 SMESHDS_SubMesh* faceSubMesh = 0;
3701 double fToler2 = 0, f,l;
3702 double u1 = 0, u2 = 0, v1 = 0, v2 = 0;
3703 bool isUPeriodic = false, isVPeriodic = false;
3705 face = TopoDS::Face( aMesh->IndexToShape( *fId ));
3706 surface = BRep_Tool::Surface( face );
3707 faceSubMesh = aMesh->MeshElements( *fId );
3708 fToler2 = BRep_Tool::Tolerance( face );
3709 fToler2 *= fToler2 * 10.;
3710 isUPeriodic = surface->IsUPeriodic();
3713 isVPeriodic = surface->IsVPeriodic();
3716 surface->Bounds( u1, u2, v1, v2 );
3718 // ---------------------------------------------------------
3719 // for elements on a face, find movable and fixed nodes and
3720 // compute UV for them
3721 // ---------------------------------------------------------
3722 bool checkBoundaryNodes = false;
3723 bool isQuadratic = false;
3724 set<const SMDS_MeshNode*> setMovableNodes;
3725 map< const SMDS_MeshNode*, gp_XY* > uvMap, uvMap2;
3726 list< gp_XY > listUV; // uvs the 2 uvMaps refer to
3727 list< const SMDS_MeshElement* > elemsOnFace;
3729 Extrema_GenExtPS projector;
3730 GeomAdaptor_Surface surfAdaptor;
3731 if ( !surface.IsNull() ) {
3732 surfAdaptor.Load( surface );
3733 projector.Initialize( surfAdaptor, 20,20, 1e-5,1e-5 );
3735 int nbElemOnFace = 0;
3736 itElem = theElems.begin();
3737 // loop on not yet smoothed elements: look for elems on a face
3738 while ( itElem != theElems.end() ) {
3739 if ( faceSubMesh && nbElemOnFace == faceSubMesh->NbElements() )
3740 break; // all elements found
3742 const SMDS_MeshElement* elem = *itElem;
3743 if ( !elem || elem->GetType() != SMDSAbs_Face || elem->NbNodes() < 3 ||
3744 ( faceSubMesh && !faceSubMesh->Contains( elem ))) {
3748 elemsOnFace.push_back( elem );
3749 theElems.erase( itElem++ );
3753 isQuadratic = elem->IsQuadratic();
3755 // get movable nodes of elem
3756 const SMDS_MeshNode* node;
3757 SMDS_TypeOfPosition posType;
3758 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3759 int nn = 0, nbn = elem->NbNodes();
3760 if(elem->IsQuadratic())
3762 while ( nn++ < nbn ) {
3763 node = static_cast<const SMDS_MeshNode*>( itN->next() );
3764 const SMDS_PositionPtr& pos = node->GetPosition();
3765 posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
3766 if (posType != SMDS_TOP_EDGE &&
3767 posType != SMDS_TOP_VERTEX &&
3768 theFixedNodes.find( node ) == theFixedNodes.end())
3770 // check if all faces around the node are on faceSubMesh
3771 // because a node on edge may be bound to face
3772 SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
3774 if ( faceSubMesh ) {
3775 while ( eIt->more() && all ) {
3776 const SMDS_MeshElement* e = eIt->next();
3777 all = faceSubMesh->Contains( e );
3781 setMovableNodes.insert( node );
3783 checkBoundaryNodes = true;
3785 if ( posType == SMDS_TOP_3DSPACE )
3786 checkBoundaryNodes = true;
3789 if ( surface.IsNull() )
3792 // get nodes to check UV
3793 list< const SMDS_MeshNode* > uvCheckNodes;
3794 itN = elem->nodesIterator();
3795 nn = 0; nbn = elem->NbNodes();
3796 if(elem->IsQuadratic())
3798 while ( nn++ < nbn ) {
3799 node = static_cast<const SMDS_MeshNode*>( itN->next() );
3800 if ( uvMap.find( node ) == uvMap.end() )
3801 uvCheckNodes.push_back( node );
3802 // add nodes of elems sharing node
3803 // SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
3804 // while ( eIt->more() ) {
3805 // const SMDS_MeshElement* e = eIt->next();
3806 // if ( e != elem ) {
3807 // SMDS_ElemIteratorPtr nIt = e->nodesIterator();
3808 // while ( nIt->more() ) {
3809 // const SMDS_MeshNode* n =
3810 // static_cast<const SMDS_MeshNode*>( nIt->next() );
3811 // if ( uvMap.find( n ) == uvMap.end() )
3812 // uvCheckNodes.push_back( n );
3818 list< const SMDS_MeshNode* >::iterator n = uvCheckNodes.begin();
3819 for ( ; n != uvCheckNodes.end(); ++n ) {
3822 const SMDS_PositionPtr& pos = node->GetPosition();
3823 posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
3825 switch ( posType ) {
3826 case SMDS_TOP_FACE: {
3827 SMDS_FacePosition* fPos = ( SMDS_FacePosition* ) pos;
3828 uv.SetCoord( fPos->GetUParameter(), fPos->GetVParameter() );
3831 case SMDS_TOP_EDGE: {
3832 TopoDS_Shape S = aMesh->IndexToShape( node->getshapeId() );
3833 Handle(Geom2d_Curve) pcurve;
3834 if ( !S.IsNull() && S.ShapeType() == TopAbs_EDGE )
3835 pcurve = BRep_Tool::CurveOnSurface( TopoDS::Edge( S ), face, f,l );
3836 if ( !pcurve.IsNull() ) {
3837 double u = (( SMDS_EdgePosition* ) pos )->GetUParameter();
3838 uv = pcurve->Value( u ).XY();
3842 case SMDS_TOP_VERTEX: {
3843 TopoDS_Shape S = aMesh->IndexToShape( node->getshapeId() );
3844 if ( !S.IsNull() && S.ShapeType() == TopAbs_VERTEX )
3845 uv = BRep_Tool::Parameters( TopoDS::Vertex( S ), face ).XY();
3850 // check existing UV
3851 bool project = true;
3852 gp_Pnt pNode ( node->X(), node->Y(), node->Z() );
3853 double dist1 = DBL_MAX, dist2 = 0;
3854 if ( posType != SMDS_TOP_3DSPACE ) {
3855 dist1 = pNode.SquareDistance( surface->Value( uv.X(), uv.Y() ));
3856 project = dist1 > fToler2;
3858 if ( project ) { // compute new UV
3860 if ( !getClosestUV( projector, pNode, newUV )) {
3861 MESSAGE("Node Projection Failed " << node);
3865 newUV.SetX( ElCLib::InPeriod( newUV.X(), u1, u2 ));
3867 newUV.SetY( ElCLib::InPeriod( newUV.Y(), v1, v2 ));
3869 if ( posType != SMDS_TOP_3DSPACE )
3870 dist2 = pNode.SquareDistance( surface->Value( newUV.X(), newUV.Y() ));
3871 if ( dist2 < dist1 )
3875 // store UV in the map
3876 listUV.push_back( uv );
3877 uvMap.insert( make_pair( node, &listUV.back() ));
3879 } // loop on not yet smoothed elements
3881 if ( !faceSubMesh || nbElemOnFace != faceSubMesh->NbElements() )
3882 checkBoundaryNodes = true;
3884 // fix nodes on mesh boundary
3886 if ( checkBoundaryNodes ) {
3887 map< SMESH_TLink, int > linkNbMap; // how many times a link encounters in elemsOnFace
3888 map< SMESH_TLink, int >::iterator link_nb;
3889 // put all elements links to linkNbMap
3890 list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
3891 for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
3892 const SMDS_MeshElement* elem = (*elemIt);
3893 int nbn = elem->NbCornerNodes();
3894 // loop on elem links: insert them in linkNbMap
3895 for ( int iN = 0; iN < nbn; ++iN ) {
3896 const SMDS_MeshNode* n1 = elem->GetNode( iN );
3897 const SMDS_MeshNode* n2 = elem->GetNode(( iN+1 ) % nbn);
3898 SMESH_TLink link( n1, n2 );
3899 link_nb = linkNbMap.insert( make_pair( link, 0 )).first;
3903 // remove nodes that are in links encountered only once from setMovableNodes
3904 for ( link_nb = linkNbMap.begin(); link_nb != linkNbMap.end(); ++link_nb ) {
3905 if ( link_nb->second == 1 ) {
3906 setMovableNodes.erase( link_nb->first.node1() );
3907 setMovableNodes.erase( link_nb->first.node2() );
3912 // -----------------------------------------------------
3913 // for nodes on seam edge, compute one more UV ( uvMap2 );
3914 // find movable nodes linked to nodes on seam and which
3915 // are to be smoothed using the second UV ( uvMap2 )
3916 // -----------------------------------------------------
3918 set<const SMDS_MeshNode*> nodesNearSeam; // to smooth using uvMap2
3919 if ( !surface.IsNull() ) {
3920 TopExp_Explorer eExp( face, TopAbs_EDGE );
3921 for ( ; eExp.More(); eExp.Next() ) {
3922 TopoDS_Edge edge = TopoDS::Edge( eExp.Current() );
3923 if ( !BRep_Tool::IsClosed( edge, face ))
3925 SMESHDS_SubMesh* sm = aMesh->MeshElements( edge );
3926 if ( !sm ) continue;
3927 // find out which parameter varies for a node on seam
3930 Handle(Geom2d_Curve) pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
3931 if ( pcurve.IsNull() ) continue;
3932 uv1 = pcurve->Value( f );
3934 pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
3935 if ( pcurve.IsNull() ) continue;
3936 uv2 = pcurve->Value( f );
3937 int iPar = Abs( uv1.X() - uv2.X() ) > Abs( uv1.Y() - uv2.Y() ) ? 1 : 2;
3939 if ( uv1.Coord( iPar ) > uv2.Coord( iPar )) {
3940 gp_Pnt2d tmp = uv1; uv1 = uv2; uv2 = tmp;
3942 // get nodes on seam and its vertices
3943 list< const SMDS_MeshNode* > seamNodes;
3944 SMDS_NodeIteratorPtr nSeamIt = sm->GetNodes();
3945 while ( nSeamIt->more() ) {
3946 const SMDS_MeshNode* node = nSeamIt->next();
3947 if ( !isQuadratic || !IsMedium( node ))
3948 seamNodes.push_back( node );
3950 TopExp_Explorer vExp( edge, TopAbs_VERTEX );
3951 for ( ; vExp.More(); vExp.Next() ) {
3952 sm = aMesh->MeshElements( vExp.Current() );
3954 nSeamIt = sm->GetNodes();
3955 while ( nSeamIt->more() )
3956 seamNodes.push_back( nSeamIt->next() );
3959 // loop on nodes on seam
3960 list< const SMDS_MeshNode* >::iterator noSeIt = seamNodes.begin();
3961 for ( ; noSeIt != seamNodes.end(); ++noSeIt ) {
3962 const SMDS_MeshNode* nSeam = *noSeIt;
3963 map< const SMDS_MeshNode*, gp_XY* >::iterator n_uv = uvMap.find( nSeam );
3964 if ( n_uv == uvMap.end() )
3967 n_uv->second->SetCoord( iPar, uv1.Coord( iPar ));
3968 // set the second UV
3969 listUV.push_back( *n_uv->second );
3970 listUV.back().SetCoord( iPar, uv2.Coord( iPar ));
3971 if ( uvMap2.empty() )
3972 uvMap2 = uvMap; // copy the uvMap contents
3973 uvMap2[ nSeam ] = &listUV.back();
3975 // collect movable nodes linked to ones on seam in nodesNearSeam
3976 SMDS_ElemIteratorPtr eIt = nSeam->GetInverseElementIterator(SMDSAbs_Face);
3977 while ( eIt->more() ) {
3978 const SMDS_MeshElement* e = eIt->next();
3979 int nbUseMap1 = 0, nbUseMap2 = 0;
3980 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
3981 int nn = 0, nbn = e->NbNodes();
3982 if(e->IsQuadratic()) nbn = nbn/2;
3983 while ( nn++ < nbn )
3985 const SMDS_MeshNode* n =
3986 static_cast<const SMDS_MeshNode*>( nIt->next() );
3988 setMovableNodes.find( n ) == setMovableNodes.end() )
3990 // add only nodes being closer to uv2 than to uv1
3991 gp_Pnt pMid (0.5 * ( n->X() + nSeam->X() ),
3992 0.5 * ( n->Y() + nSeam->Y() ),
3993 0.5 * ( n->Z() + nSeam->Z() ));
3995 getClosestUV( projector, pMid, uv );
3996 if ( uv.Coord( iPar ) > uvMap[ n ]->Coord( iPar ) ) {
3997 nodesNearSeam.insert( n );
4003 // for centroidalSmooth all element nodes must
4004 // be on one side of a seam
4005 if ( theSmoothMethod == CENTROIDAL && nbUseMap1 && nbUseMap2 ) {
4006 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
4008 while ( nn++ < nbn ) {
4009 const SMDS_MeshNode* n =
4010 static_cast<const SMDS_MeshNode*>( nIt->next() );
4011 setMovableNodes.erase( n );
4015 } // loop on nodes on seam
4016 } // loop on edge of a face
4017 } // if ( !face.IsNull() )
4019 if ( setMovableNodes.empty() ) {
4020 MESSAGE( "Face id : " << *fId << " - NO SMOOTHING: no nodes to move!!!");
4021 continue; // goto next face
4029 double maxRatio = -1., maxDisplacement = -1.;
4030 set<const SMDS_MeshNode*>::iterator nodeToMove;
4031 for ( it = 0; it < theNbIterations; it++ ) {
4032 maxDisplacement = 0.;
4033 nodeToMove = setMovableNodes.begin();
4034 for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
4035 const SMDS_MeshNode* node = (*nodeToMove);
4036 gp_XYZ aPrevPos ( node->X(), node->Y(), node->Z() );
4039 bool map2 = ( nodesNearSeam.find( node ) != nodesNearSeam.end() );
4040 if ( theSmoothMethod == LAPLACIAN )
4041 laplacianSmooth( node, surface, map2 ? uvMap2 : uvMap );
4043 centroidalSmooth( node, surface, map2 ? uvMap2 : uvMap );
4045 // node displacement
4046 gp_XYZ aNewPos ( node->X(), node->Y(), node->Z() );
4047 Standard_Real aDispl = (aPrevPos - aNewPos).SquareModulus();
4048 if ( aDispl > maxDisplacement )
4049 maxDisplacement = aDispl;
4051 // no node movement => exit
4052 //if ( maxDisplacement < 1.e-16 ) {
4053 if ( maxDisplacement < disttol ) {
4054 MESSAGE("-- no node movement --");
4058 // check elements quality
4060 list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4061 for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
4062 const SMDS_MeshElement* elem = (*elemIt);
4063 if ( !elem || elem->GetType() != SMDSAbs_Face )
4065 SMESH::Controls::TSequenceOfXYZ aPoints;
4066 if ( aQualityFunc.GetPoints( elem, aPoints )) {
4067 double aValue = aQualityFunc.GetValue( aPoints );
4068 if ( aValue > maxRatio )
4072 if ( maxRatio <= theTgtAspectRatio ) {
4073 MESSAGE("-- quality achived --");
4076 if (it+1 == theNbIterations) {
4077 MESSAGE("-- Iteration limit exceeded --");
4079 } // smoothing iterations
4081 MESSAGE(" Face id: " << *fId <<
4082 " Nb iterstions: " << it <<
4083 " Displacement: " << maxDisplacement <<
4084 " Aspect Ratio " << maxRatio);
4086 // ---------------------------------------
4087 // new nodes positions are computed,
4088 // record movement in DS and set new UV
4089 // ---------------------------------------
4090 nodeToMove = setMovableNodes.begin();
4091 for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
4092 SMDS_MeshNode* node = const_cast< SMDS_MeshNode* > (*nodeToMove);
4093 aMesh->MoveNode( node, node->X(), node->Y(), node->Z() );
4094 map< const SMDS_MeshNode*, gp_XY* >::iterator node_uv = uvMap.find( node );
4095 if ( node_uv != uvMap.end() ) {
4096 gp_XY* uv = node_uv->second;
4098 ( SMDS_PositionPtr( new SMDS_FacePosition( uv->X(), uv->Y() )));
4102 // move medium nodes of quadratic elements
4105 SMESH_MesherHelper helper( *GetMesh() );
4106 helper.SetSubShape( face );
4107 vector<const SMDS_MeshNode*> nodes;
4109 list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4110 for ( ; elemIt != elemsOnFace.end(); ++elemIt )
4112 const SMDS_MeshElement* QF = *elemIt;
4113 if ( QF->IsQuadratic() )
4115 nodes.assign( SMDS_MeshElement::iterator( QF->interlacedNodesElemIterator() ),
4116 SMDS_MeshElement::iterator() );
4117 nodes.push_back( nodes[0] );
4119 for (size_t i = 1; i < nodes.size(); i += 2 ) // i points to a medium node
4121 if ( !surface.IsNull() )
4123 gp_XY uv1 = helper.GetNodeUV( face, nodes[i-1], nodes[i+1], &checkUV );
4124 gp_XY uv2 = helper.GetNodeUV( face, nodes[i+1], nodes[i-1], &checkUV );
4125 gp_XY uv = helper.GetMiddleUV( surface, uv1, uv2 );
4126 xyz = surface->Value( uv.X(), uv.Y() );
4129 xyz = 0.5 * ( SMESH_TNodeXYZ( nodes[i-1] ) + SMESH_TNodeXYZ( nodes[i+1] ));
4131 if (( SMESH_TNodeXYZ( nodes[i] ) - xyz.XYZ() ).Modulus() > disttol )
4132 // we have to move a medium node
4133 aMesh->MoveNode( nodes[i], xyz.X(), xyz.Y(), xyz.Z() );
4139 } // loop on face ids
4143 //=======================================================================
4144 //function : isReverse
4145 //purpose : Return true if normal of prevNodes is not co-directied with
4146 // gp_Vec(prevNodes[iNotSame],nextNodes[iNotSame]).
4147 // iNotSame is where prevNodes and nextNodes are different.
4148 // If result is true then future volume orientation is OK
4149 //=======================================================================
4151 static bool isReverse(const SMDS_MeshElement* face,
4152 const vector<const SMDS_MeshNode*>& prevNodes,
4153 const vector<const SMDS_MeshNode*>& nextNodes,
4157 SMESH_TNodeXYZ pP = prevNodes[ iNotSame ];
4158 SMESH_TNodeXYZ pN = nextNodes[ iNotSame ];
4159 gp_XYZ extrDir( pN - pP ), faceNorm;
4160 SMESH_MeshAlgos::FaceNormal( face, faceNorm, /*normalized=*/false );
4162 return faceNorm * extrDir < 0.0;
4165 //=======================================================================
4167 * \brief Create elements by sweeping an element
4168 * \param elem - element to sweep
4169 * \param newNodesItVec - nodes generated from each node of the element
4170 * \param newElems - generated elements
4171 * \param nbSteps - number of sweeping steps
4172 * \param srcElements - to append elem for each generated element
4174 //=======================================================================
4176 void SMESH_MeshEditor::sweepElement(const SMDS_MeshElement* elem,
4177 const vector<TNodeOfNodeListMapItr> & newNodesItVec,
4178 list<const SMDS_MeshElement*>& newElems,
4180 SMESH_SequenceOfElemPtr& srcElements)
4182 //MESSAGE("sweepElement " << nbSteps);
4183 SMESHDS_Mesh* aMesh = GetMeshDS();
4185 const int nbNodes = elem->NbNodes();
4186 const int nbCorners = elem->NbCornerNodes();
4187 SMDSAbs_EntityType baseType = elem->GetEntityType(); /* it can change in case of
4188 polyhedron creation !!! */
4189 // Loop on elem nodes:
4190 // find new nodes and detect same nodes indices
4191 vector < list< const SMDS_MeshNode* >::const_iterator > itNN( nbNodes );
4192 vector<const SMDS_MeshNode*> prevNod( nbNodes );
4193 vector<const SMDS_MeshNode*> nextNod( nbNodes );
4194 vector<const SMDS_MeshNode*> midlNod( nbNodes );
4196 int iNode, nbSame = 0, nbDouble = 0, iNotSameNode = 0;
4197 vector<int> sames(nbNodes);
4198 vector<bool> isSingleNode(nbNodes);
4200 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4201 TNodeOfNodeListMapItr nnIt = newNodesItVec[ iNode ];
4202 const SMDS_MeshNode* node = nnIt->first;
4203 const list< const SMDS_MeshNode* > & listNewNodes = nnIt->second;
4204 if ( listNewNodes.empty() )
4207 itNN [ iNode ] = listNewNodes.begin();
4208 prevNod[ iNode ] = node;
4209 nextNod[ iNode ] = listNewNodes.front();
4211 isSingleNode[iNode] = (listNewNodes.size()==nbSteps); /* medium node of quadratic or
4212 corner node of linear */
4213 if ( prevNod[ iNode ] != nextNod [ iNode ])
4214 nbDouble += !isSingleNode[iNode];
4216 if( iNode < nbCorners ) { // check corners only
4217 if ( prevNod[ iNode ] == nextNod [ iNode ])
4218 sames[nbSame++] = iNode;
4220 iNotSameNode = iNode;
4224 if ( nbSame == nbNodes || nbSame > 2) {
4225 MESSAGE( " Too many same nodes of element " << elem->GetID() );
4229 if ( elem->GetType() == SMDSAbs_Face && !isReverse( elem, prevNod, nextNod, iNotSameNode ))
4231 // fix nodes order to have bottom normal external
4232 if ( baseType == SMDSEntity_Polygon )
4234 std::reverse( itNN.begin(), itNN.end() );
4235 std::reverse( prevNod.begin(), prevNod.end() );
4236 std::reverse( midlNod.begin(), midlNod.end() );
4237 std::reverse( nextNod.begin(), nextNod.end() );
4238 std::reverse( isSingleNode.begin(), isSingleNode.end() );
4242 const vector<int>& ind = SMDS_MeshCell::reverseSmdsOrder( baseType );
4243 SMDS_MeshCell::applyInterlace( ind, itNN );
4244 SMDS_MeshCell::applyInterlace( ind, prevNod );
4245 SMDS_MeshCell::applyInterlace( ind, nextNod );
4246 SMDS_MeshCell::applyInterlace( ind, midlNod );
4247 SMDS_MeshCell::applyInterlace( ind, isSingleNode );
4250 sames[nbSame] = iNotSameNode;
4251 for ( int j = 0; j <= nbSame; ++j )
4252 for ( size_t i = 0; i < ind.size(); ++i )
4253 if ( ind[i] == sames[j] )
4258 iNotSameNode = sames[nbSame];
4263 int iSameNode = 0, iBeforeSame = 0, iAfterSame = 0, iOpposSame = 0;
4265 iSameNode = sames[ nbSame-1 ];
4266 iBeforeSame = ( iSameNode + nbCorners - 1 ) % nbCorners;
4267 iAfterSame = ( iSameNode + 1 ) % nbCorners;
4268 iOpposSame = ( iSameNode - 2 < 0 ? iSameNode + 2 : iSameNode - 2 );
4271 // make new elements
4272 for (int iStep = 0; iStep < nbSteps; iStep++ )
4275 for ( iNode = 0; iNode < nbNodes; iNode++ )
4277 midlNod[ iNode ] = isSingleNode[iNode] ? 0 : *itNN[ iNode ]++;
4278 nextNod[ iNode ] = *itNN[ iNode ]++;
4281 SMDS_MeshElement* aNewElem = 0;
4282 /*if(!elem->IsPoly())*/ {
4283 switch ( baseType ) {
4285 case SMDSEntity_Node: { // sweep NODE
4286 if ( nbSame == 0 ) {
4287 if ( isSingleNode[0] )
4288 aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ] );
4290 aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ], midlNod[ 0 ] );
4296 case SMDSEntity_Edge: { // sweep EDGE
4297 if ( nbDouble == 0 )
4299 if ( nbSame == 0 ) // ---> quadrangle
4300 aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
4301 nextNod[ 1 ], nextNod[ 0 ] );
4302 else // ---> triangle
4303 aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
4304 nextNod[ iNotSameNode ] );
4306 else // ---> polygon
4308 vector<const SMDS_MeshNode*> poly_nodes;
4309 poly_nodes.push_back( prevNod[0] );
4310 poly_nodes.push_back( prevNod[1] );
4311 if ( prevNod[1] != nextNod[1] )
4313 if ( midlNod[1]) poly_nodes.push_back( midlNod[1]);
4314 poly_nodes.push_back( nextNod[1] );
4316 if ( prevNod[0] != nextNod[0] )
4318 poly_nodes.push_back( nextNod[0] );
4319 if ( midlNod[0]) poly_nodes.push_back( midlNod[0]);
4321 switch ( poly_nodes.size() ) {
4323 aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ], poly_nodes[ 2 ]);
4326 aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ],
4327 poly_nodes[ 2 ], poly_nodes[ 3 ]);
4330 aNewElem = aMesh->AddPolygonalFace (poly_nodes);
4335 case SMDSEntity_Triangle: // TRIANGLE --->
4337 if ( nbDouble > 0 ) break;
4338 if ( nbSame == 0 ) // ---> pentahedron
4339 aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
4340 nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ] );
4342 else if ( nbSame == 1 ) // ---> pyramid
4343 aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
4344 nextNod[ iAfterSame ], nextNod[ iBeforeSame ],
4345 nextNod[ iSameNode ]);
4347 else // 2 same nodes: ---> tetrahedron
4348 aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
4349 nextNod[ iNotSameNode ]);
4352 case SMDSEntity_Quad_Edge: // sweep quadratic EDGE --->
4356 if ( nbDouble+nbSame == 2 )
4358 if(nbSame==0) { // ---> quadratic quadrangle
4359 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
4360 prevNod[2], midlNod[1], nextNod[2], midlNod[0]);
4362 else { //(nbSame==1) // ---> quadratic triangle
4364 return; // medium node on axis
4366 else if(sames[0]==0)
4367 aNewElem = aMesh->AddFace(prevNod[0], nextNod[1], prevNod[1],
4368 nextNod[2], midlNod[1], prevNod[2]);
4370 aNewElem = aMesh->AddFace(prevNod[0], nextNod[0], prevNod[1],
4371 midlNod[0], nextNod[2], prevNod[2]);
4374 else if ( nbDouble == 3 )
4376 if ( nbSame == 0 ) { // ---> bi-quadratic quadrangle
4377 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
4378 prevNod[2], midlNod[1], nextNod[2], midlNod[0], midlNod[2]);
4385 case SMDSEntity_Quadrangle: { // sweep QUADRANGLE --->
4386 if ( nbDouble > 0 ) break;
4388 if ( nbSame == 0 ) // ---> hexahedron
4389 aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ], prevNod[ 3 ],
4390 nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ], nextNod[ 3 ]);
4392 else if ( nbSame == 1 ) { // ---> pyramid + pentahedron
4393 aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
4394 nextNod[ iAfterSame ], nextNod[ iBeforeSame ],
4395 nextNod[ iSameNode ]);
4396 newElems.push_back( aNewElem );
4397 aNewElem = aMesh->AddVolume (prevNod[ iAfterSame ], prevNod[ iOpposSame ],
4398 prevNod[ iBeforeSame ], nextNod[ iAfterSame ],
4399 nextNod[ iOpposSame ], nextNod[ iBeforeSame ] );
4401 else if ( nbSame == 2 ) { // ---> pentahedron
4402 if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] )
4403 // iBeforeSame is same too
4404 aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iOpposSame ],
4405 nextNod[ iOpposSame ], prevNod[ iSameNode ],
4406 prevNod[ iAfterSame ], nextNod[ iAfterSame ]);
4408 // iAfterSame is same too
4409 aNewElem = aMesh->AddVolume (prevNod[ iSameNode ], prevNod[ iBeforeSame ],
4410 nextNod[ iBeforeSame ], prevNod[ iAfterSame ],
4411 prevNod[ iOpposSame ], nextNod[ iOpposSame ]);
4415 case SMDSEntity_Quad_Triangle: // sweep (Bi)Quadratic TRIANGLE --->
4416 case SMDSEntity_BiQuad_Triangle: /* ??? */ {
4417 if ( nbDouble+nbSame != 3 ) break;
4419 // ---> pentahedron with 15 nodes
4420 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
4421 nextNod[0], nextNod[1], nextNod[2],
4422 prevNod[3], prevNod[4], prevNod[5],
4423 nextNod[3], nextNod[4], nextNod[5],
4424 midlNod[0], midlNod[1], midlNod[2]);
4426 else if(nbSame==1) {
4427 // ---> 2d order pyramid of 13 nodes
4428 int apex = iSameNode;
4429 int i0 = ( apex + 1 ) % nbCorners;
4430 int i1 = ( apex - 1 + nbCorners ) % nbCorners;
4434 aNewElem = aMesh->AddVolume(prevNod[i1], prevNod[i0],
4435 nextNod[i0], nextNod[i1], prevNod[apex],
4436 prevNod[i01], midlNod[i0],
4437 nextNod[i01], midlNod[i1],
4438 prevNod[i1a], prevNod[i0a],
4439 nextNod[i0a], nextNod[i1a]);
4441 else if(nbSame==2) {
4442 // ---> 2d order tetrahedron of 10 nodes
4443 int n1 = iNotSameNode;
4444 int n2 = ( n1 + 1 ) % nbCorners;
4445 int n3 = ( n1 + nbCorners - 1 ) % nbCorners;
4449 aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], prevNod[n3], nextNod[n1],
4450 prevNod[n12], prevNod[n23], prevNod[n31],
4451 midlNod[n1], nextNod[n12], nextNod[n31]);
4455 case SMDSEntity_Quad_Quadrangle: { // sweep Quadratic QUADRANGLE --->
4457 if ( nbDouble != 4 ) break;
4458 // ---> hexahedron with 20 nodes
4459 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
4460 nextNod[0], nextNod[1], nextNod[2], nextNod[3],
4461 prevNod[4], prevNod[5], prevNod[6], prevNod[7],
4462 nextNod[4], nextNod[5], nextNod[6], nextNod[7],
4463 midlNod[0], midlNod[1], midlNod[2], midlNod[3]);
4465 else if(nbSame==1) {
4466 // ---> pyramid + pentahedron - can not be created since it is needed
4467 // additional middle node at the center of face
4468 INFOS( " Sweep for face " << elem->GetID() << " can not be created" );
4471 else if( nbSame == 2 ) {
4472 if ( nbDouble != 2 ) break;
4473 // ---> 2d order Pentahedron with 15 nodes
4475 if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] ) {
4476 // iBeforeSame is same too
4483 // iAfterSame is same too
4493 aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], nextNod[n2],
4494 prevNod[n4], prevNod[n5], nextNod[n5],
4495 prevNod[n12], midlNod[n2], nextNod[n12],
4496 prevNod[n45], midlNod[n5], nextNod[n45],
4497 prevNod[n14], prevNod[n25], nextNod[n25]);
4501 case SMDSEntity_BiQuad_Quadrangle: { // sweep BiQuadratic QUADRANGLE --->
4503 if( nbSame == 0 && nbDouble == 9 ) {
4504 // ---> tri-quadratic hexahedron with 27 nodes
4505 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
4506 nextNod[0], nextNod[1], nextNod[2], nextNod[3],
4507 prevNod[4], prevNod[5], prevNod[6], prevNod[7],
4508 nextNod[4], nextNod[5], nextNod[6], nextNod[7],
4509 midlNod[0], midlNod[1], midlNod[2], midlNod[3],
4510 prevNod[8], // bottom center
4511 midlNod[4], midlNod[5], midlNod[6], midlNod[7],
4512 nextNod[8], // top center
4513 midlNod[8]);// elem center
4521 case SMDSEntity_Polygon: { // sweep POLYGON
4523 if ( nbNodes == 6 && nbSame == 0 && nbDouble == 0 ) {
4524 // ---> hexagonal prism
4525 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
4526 prevNod[3], prevNod[4], prevNod[5],
4527 nextNod[0], nextNod[1], nextNod[2],
4528 nextNod[3], nextNod[4], nextNod[5]);
4532 case SMDSEntity_Ball:
4540 if ( !aNewElem && elem->GetType() == SMDSAbs_Face ) // try to create a polyherdal prism
4542 if ( baseType != SMDSEntity_Polygon )
4544 const std::vector<int>& ind = SMDS_MeshCell::interlacedSmdsOrder(baseType);
4545 SMDS_MeshCell::applyInterlace( ind, prevNod );
4546 SMDS_MeshCell::applyInterlace( ind, nextNod );
4547 SMDS_MeshCell::applyInterlace( ind, midlNod );
4548 SMDS_MeshCell::applyInterlace( ind, itNN );
4549 SMDS_MeshCell::applyInterlace( ind, isSingleNode );
4550 baseType = SMDSEntity_Polygon; // WARNING: change baseType !!!!
4552 vector<const SMDS_MeshNode*> polyedre_nodes (nbNodes*2 + 4*nbNodes);
4553 vector<int> quantities (nbNodes + 2);
4554 polyedre_nodes.clear();
4558 for (int inode = 0; inode < nbNodes; inode++)
4559 polyedre_nodes.push_back( prevNod[inode] );
4560 quantities.push_back( nbNodes );
4563 polyedre_nodes.push_back( nextNod[0] );
4564 for (int inode = nbNodes; inode-1; --inode )
4565 polyedre_nodes.push_back( nextNod[inode-1] );
4566 quantities.push_back( nbNodes );
4569 for (int iface = 0; iface < nbNodes; iface++)
4571 const int prevNbNodes = polyedre_nodes.size();
4572 int inextface = (iface+1) % nbNodes;
4573 polyedre_nodes.push_back( prevNod[inextface] );
4574 polyedre_nodes.push_back( prevNod[iface] );
4575 if ( prevNod[iface] != nextNod[iface] )
4577 if ( midlNod[ iface ]) polyedre_nodes.push_back( midlNod[ iface ]);
4578 polyedre_nodes.push_back( nextNod[iface] );
4580 if ( prevNod[inextface] != nextNod[inextface] )
4582 polyedre_nodes.push_back( nextNod[inextface] );
4583 if ( midlNod[ inextface ]) polyedre_nodes.push_back( midlNod[ inextface ]);
4585 const int nbFaceNodes = polyedre_nodes.size() - prevNbNodes;
4586 if ( nbFaceNodes > 2 )
4587 quantities.push_back( nbFaceNodes );
4588 else // degenerated face
4589 polyedre_nodes.resize( prevNbNodes );
4591 aNewElem = aMesh->AddPolyhedralVolume (polyedre_nodes, quantities);
4595 newElems.push_back( aNewElem );
4596 myLastCreatedElems.Append(aNewElem);
4597 srcElements.Append( elem );
4600 // set new prev nodes
4601 for ( iNode = 0; iNode < nbNodes; iNode++ )
4602 prevNod[ iNode ] = nextNod[ iNode ];
4607 //=======================================================================
4609 * \brief Create 1D and 2D elements around swept elements
4610 * \param mapNewNodes - source nodes and ones generated from them
4611 * \param newElemsMap - source elements and ones generated from them
4612 * \param elemNewNodesMap - nodes generated from each node of each element
4613 * \param elemSet - all swept elements
4614 * \param nbSteps - number of sweeping steps
4615 * \param srcElements - to append elem for each generated element
4617 //=======================================================================
4619 void SMESH_MeshEditor::makeWalls (TNodeOfNodeListMap & mapNewNodes,
4620 TTElemOfElemListMap & newElemsMap,
4621 TElemOfVecOfNnlmiMap & elemNewNodesMap,
4622 TIDSortedElemSet& elemSet,
4624 SMESH_SequenceOfElemPtr& srcElements)
4626 ASSERT( newElemsMap.size() == elemNewNodesMap.size() );
4627 SMESHDS_Mesh* aMesh = GetMeshDS();
4629 // Find nodes belonging to only one initial element - sweep them into edges.
4631 TNodeOfNodeListMapItr nList = mapNewNodes.begin();
4632 for ( ; nList != mapNewNodes.end(); nList++ )
4634 const SMDS_MeshNode* node =
4635 static_cast<const SMDS_MeshNode*>( nList->first );
4636 if ( newElemsMap.count( node ))
4637 continue; // node was extruded into edge
4638 SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator();
4639 int nbInitElems = 0;
4640 const SMDS_MeshElement* el = 0;
4641 SMDSAbs_ElementType highType = SMDSAbs_Edge; // count most complex elements only
4642 while ( eIt->more() && nbInitElems < 2 ) {
4644 SMDSAbs_ElementType type = el->GetType();
4645 if ( type == SMDSAbs_Volume || type < highType ) continue;
4646 if ( type > highType ) {
4650 nbInitElems += elemSet.count(el);
4652 if ( nbInitElems < 2 ) {
4653 bool NotCreateEdge = el && el->IsMediumNode(node);
4654 if(!NotCreateEdge) {
4655 vector<TNodeOfNodeListMapItr> newNodesItVec( 1, nList );
4656 list<const SMDS_MeshElement*> newEdges;
4657 sweepElement( node, newNodesItVec, newEdges, nbSteps, srcElements );
4662 // Make a ceiling for each element ie an equal element of last new nodes.
4663 // Find free links of faces - make edges and sweep them into faces.
4665 TTElemOfElemListMap::iterator itElem = newElemsMap.begin();
4666 TElemOfVecOfNnlmiMap::iterator itElemNodes = elemNewNodesMap.begin();
4667 for ( ; itElem != newElemsMap.end(); itElem++, itElemNodes++ )
4669 const SMDS_MeshElement* elem = itElem->first;
4670 vector<TNodeOfNodeListMapItr>& vecNewNodes = itElemNodes->second;
4672 if(itElem->second.size()==0) continue;
4674 const bool isQuadratic = elem->IsQuadratic();
4676 if ( elem->GetType() == SMDSAbs_Edge ) {
4677 // create a ceiling edge
4678 if ( !isQuadratic ) {
4679 if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
4680 vecNewNodes[ 1 ]->second.back())) {
4681 myLastCreatedElems.Append(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
4682 vecNewNodes[ 1 ]->second.back()));
4683 srcElements.Append( elem );
4687 if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
4688 vecNewNodes[ 1 ]->second.back(),
4689 vecNewNodes[ 2 ]->second.back())) {
4690 myLastCreatedElems.Append(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
4691 vecNewNodes[ 1 ]->second.back(),
4692 vecNewNodes[ 2 ]->second.back()));
4693 srcElements.Append( elem );
4697 if ( elem->GetType() != SMDSAbs_Face )
4700 bool hasFreeLinks = false;
4702 TIDSortedElemSet avoidSet;
4703 avoidSet.insert( elem );
4705 set<const SMDS_MeshNode*> aFaceLastNodes;
4706 int iNode, nbNodes = vecNewNodes.size();
4707 if ( !isQuadratic ) {
4708 // loop on the face nodes
4709 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4710 aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4711 // look for free links of the face
4712 int iNext = ( iNode + 1 == nbNodes ) ? 0 : iNode + 1;
4713 const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
4714 const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
4715 // check if a link n1-n2 is free
4716 if ( ! SMESH_MeshAlgos::FindFaceInSet ( n1, n2, elemSet, avoidSet )) {
4717 hasFreeLinks = true;
4718 // make a new edge and a ceiling for a new edge
4719 const SMDS_MeshElement* edge;
4720 if ( ! ( edge = aMesh->FindEdge( n1, n2 ))) {
4721 myLastCreatedElems.Append( edge = aMesh->AddEdge( n1, n2 )); // free link edge
4722 srcElements.Append( myLastCreatedElems.Last() );
4724 n1 = vecNewNodes[ iNode ]->second.back();
4725 n2 = vecNewNodes[ iNext ]->second.back();
4726 if ( !aMesh->FindEdge( n1, n2 )) {
4727 myLastCreatedElems.Append(aMesh->AddEdge( n1, n2 )); // new edge ceiling
4728 srcElements.Append( edge );
4733 else { // elem is quadratic face
4734 int nbn = nbNodes/2;
4735 for ( iNode = 0; iNode < nbn; iNode++ ) {
4736 aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4737 int iNext = ( iNode + 1 == nbn ) ? 0 : iNode + 1;
4738 const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
4739 const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
4740 const SMDS_MeshNode* n3 = vecNewNodes[ iNode+nbn ]->first;
4741 // check if a link is free
4742 if ( ! SMESH_MeshAlgos::FindFaceInSet ( n1, n2, elemSet, avoidSet ) &&
4743 ! SMESH_MeshAlgos::FindFaceInSet ( n1, n3, elemSet, avoidSet ) &&
4744 ! SMESH_MeshAlgos::FindFaceInSet ( n3, n2, elemSet, avoidSet ) ) {
4745 hasFreeLinks = true;
4746 // make an edge and a ceiling for a new edge
4748 if ( !aMesh->FindEdge( n1, n2, n3 )) {
4749 myLastCreatedElems.Append(aMesh->AddEdge( n1, n2, n3 )); // free link edge
4750 srcElements.Append( elem );
4752 n1 = vecNewNodes[ iNode ]->second.back();
4753 n2 = vecNewNodes[ iNext ]->second.back();
4754 n3 = vecNewNodes[ iNode+nbn ]->second.back();
4755 if ( !aMesh->FindEdge( n1, n2, n3 )) {
4756 myLastCreatedElems.Append(aMesh->AddEdge( n1, n2, n3 )); // ceiling edge
4757 srcElements.Append( elem );
4761 for ( iNode = nbn; iNode < nbNodes; iNode++ ) {
4762 aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4766 // sweep free links into faces
4768 if ( hasFreeLinks ) {
4769 list<const SMDS_MeshElement*> & newVolumes = itElem->second;
4770 int iVol, volNb, nbVolumesByStep = newVolumes.size() / nbSteps;
4772 set<const SMDS_MeshNode*> initNodeSet, topNodeSet, faceNodeSet;
4773 set<const SMDS_MeshNode*> initNodeSetNoCenter/*, topNodeSetNoCenter*/;
4774 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4775 initNodeSet.insert( vecNewNodes[ iNode ]->first );
4776 topNodeSet .insert( vecNewNodes[ iNode ]->second.back() );
4778 if ( isQuadratic && nbNodes % 2 ) { // node set for the case of a biquadratic
4779 initNodeSetNoCenter = initNodeSet; // swept face and a not biquadratic volume
4780 initNodeSetNoCenter.erase( vecNewNodes.back()->first );
4782 for ( volNb = 0; volNb < nbVolumesByStep; volNb++ ) {
4783 list<const SMDS_MeshElement*>::iterator v = newVolumes.begin();
4784 std::advance( v, volNb );
4785 // find indices of free faces of a volume and their source edges
4786 list< int > freeInd;
4787 list< const SMDS_MeshElement* > srcEdges; // source edges of free faces
4788 SMDS_VolumeTool vTool( *v, /*ignoreCentralNodes=*/false );
4789 int iF, nbF = vTool.NbFaces();
4790 for ( iF = 0; iF < nbF; iF ++ ) {
4791 if (vTool.IsFreeFace( iF ) &&
4792 vTool.GetFaceNodes( iF, faceNodeSet ) &&
4793 initNodeSet != faceNodeSet) // except an initial face
4795 if ( nbSteps == 1 && faceNodeSet == topNodeSet )
4797 if ( faceNodeSet == initNodeSetNoCenter )
4799 freeInd.push_back( iF );
4800 // find source edge of a free face iF
4801 vector<const SMDS_MeshNode*> commonNodes; // shared by the initial and free faces
4802 commonNodes.resize( initNodeSet.size(), NULL ); // avoid spoiling memory
4803 std::set_intersection( faceNodeSet.begin(), faceNodeSet.end(),
4804 initNodeSet.begin(), initNodeSet.end(),
4805 commonNodes.begin());
4806 if ( (*v)->IsQuadratic() )
4807 srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1],commonNodes[2]));
4809 srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1]));
4811 if ( !srcEdges.back() )
4813 cout << "SMESH_MeshEditor::makeWalls(), no source edge found for a free face #"
4814 << iF << " of volume #" << vTool.ID() << endl;
4819 if ( freeInd.empty() )
4822 // create faces for all steps;
4823 // if such a face has been already created by sweep of edge,
4824 // assure that its orientation is OK
4825 for ( int iStep = 0; iStep < nbSteps; iStep++ ) {
4826 vTool.Set( *v, /*ignoreCentralNodes=*/false );
4827 vTool.SetExternalNormal();
4828 const int nextShift = vTool.IsForward() ? +1 : -1;
4829 list< int >::iterator ind = freeInd.begin();
4830 list< const SMDS_MeshElement* >::iterator srcEdge = srcEdges.begin();
4831 for ( ; ind != freeInd.end(); ++ind, ++srcEdge ) // loop on free faces
4833 const SMDS_MeshNode** nodes = vTool.GetFaceNodes( *ind );
4834 int nbn = vTool.NbFaceNodes( *ind );
4835 const SMDS_MeshElement * f = 0;
4836 if ( nbn == 3 ) ///// triangle
4838 f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ]);
4840 nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
4842 const SMDS_MeshNode* newOrder[3] = { nodes[ 1 - nextShift ],
4844 nodes[ 1 + nextShift ] };
4846 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4848 myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
4852 else if ( nbn == 4 ) ///// quadrangle
4854 f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ], nodes[ 3 ]);
4856 nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
4858 const SMDS_MeshNode* newOrder[4] = { nodes[ 0 ], nodes[ 2-nextShift ],
4859 nodes[ 2 ], nodes[ 2+nextShift ] };
4861 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4863 myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
4864 newOrder[ 2 ], newOrder[ 3 ]));
4867 else if ( nbn == 6 && isQuadratic ) /////// quadratic triangle
4869 f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[1], nodes[3], nodes[5] );
4871 nodes[2] != f->GetNodeWrap( f->GetNodeIndex( nodes[0] ) + 2*nextShift ))
4873 const SMDS_MeshNode* newOrder[6] = { nodes[2 - 2*nextShift],
4875 nodes[2 + 2*nextShift],
4876 nodes[3 - 2*nextShift],
4878 nodes[3 + 2*nextShift]};
4880 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4882 myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ],
4890 else if ( nbn == 8 && isQuadratic ) /////// quadratic quadrangle
4892 f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[6],
4893 nodes[1], nodes[3], nodes[5], nodes[7] );
4895 nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
4897 const SMDS_MeshNode* newOrder[8] = { nodes[0],
4898 nodes[4 - 2*nextShift],
4900 nodes[4 + 2*nextShift],
4902 nodes[5 - 2*nextShift],
4904 nodes[5 + 2*nextShift] };
4906 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4908 myLastCreatedElems.Append(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
4909 newOrder[ 2 ], newOrder[ 3 ],
4910 newOrder[ 4 ], newOrder[ 5 ],
4911 newOrder[ 6 ], newOrder[ 7 ]));
4914 else if ( nbn == 9 && isQuadratic ) /////// bi-quadratic quadrangle
4916 f = aMesh->FindElement( vector<const SMDS_MeshNode*>( nodes, nodes+nbn ),
4917 SMDSAbs_Face, /*noMedium=*/false);
4919 nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
4921 const SMDS_MeshNode* newOrder[9] = { nodes[0],
4922 nodes[4 - 2*nextShift],
4924 nodes[4 + 2*nextShift],
4926 nodes[5 - 2*nextShift],
4928 nodes[5 + 2*nextShift],
4931 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4933 myLastCreatedElems.Append(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
4934 newOrder[ 2 ], newOrder[ 3 ],
4935 newOrder[ 4 ], newOrder[ 5 ],
4936 newOrder[ 6 ], newOrder[ 7 ],
4940 else //////// polygon
4942 vector<const SMDS_MeshNode*> polygon_nodes ( nodes, nodes+nbn );
4943 const SMDS_MeshFace * f = aMesh->FindFace( polygon_nodes );
4945 nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + nextShift ))
4947 if ( !vTool.IsForward() )
4948 std::reverse( polygon_nodes.begin(), polygon_nodes.end());
4950 aMesh->ChangeElementNodes( f, &polygon_nodes[0], nbn );
4952 AddElement(polygon_nodes, SMDSAbs_Face, polygon_nodes.size()>4);
4956 while ( srcElements.Length() < myLastCreatedElems.Length() )
4957 srcElements.Append( *srcEdge );
4959 } // loop on free faces
4961 // go to the next volume
4963 while ( iVol++ < nbVolumesByStep ) v++;
4966 } // loop on volumes of one step
4967 } // sweep free links into faces
4969 // Make a ceiling face with a normal external to a volume
4971 // use SMDS_VolumeTool to get a correctly ordered nodes of a ceiling face
4972 SMDS_VolumeTool lastVol( itElem->second.back(), /*ignoreCentralNodes=*/false );
4973 int iF = lastVol.GetFaceIndex( aFaceLastNodes );
4975 if ( iF < 0 && isQuadratic && nbNodes % 2 ) { // remove a central node of biquadratic
4976 aFaceLastNodes.erase( vecNewNodes.back()->second.back() );
4977 iF = lastVol.GetFaceIndex( aFaceLastNodes );
4980 lastVol.SetExternalNormal();
4981 const SMDS_MeshNode** nodes = lastVol.GetFaceNodes( iF );
4982 int nbn = lastVol.NbFaceNodes( iF );
4983 // we do not use this->AddElement() because nodes are interlaced
4984 vector<const SMDS_MeshNode*> nodeVec( nodes, nodes+nbn );
4985 if ( !hasFreeLinks ||
4986 !aMesh->FindElement( nodeVec, SMDSAbs_Face, /*noMedium=*/false) )
4989 myLastCreatedElems.Append(aMesh->AddFace( nodes[0], nodes[1], nodes[2] ));
4991 else if ( nbn == 4 )
4992 myLastCreatedElems.Append(aMesh->AddFace( nodes[0], nodes[1], nodes[2], nodes[3]));
4994 else if ( nbn == 6 && isQuadratic )
4995 myLastCreatedElems.Append(aMesh->AddFace( nodes[0], nodes[2], nodes[4],
4996 nodes[1], nodes[3], nodes[5]));
4997 else if ( nbn == 7 && isQuadratic )
4998 myLastCreatedElems.Append(aMesh->AddFace( nodes[0], nodes[2], nodes[4],
4999 nodes[1], nodes[3], nodes[5], nodes[6]));
5000 else if ( nbn == 8 && isQuadratic )
5001 myLastCreatedElems.Append(aMesh->AddFace( nodes[0], nodes[2], nodes[4], nodes[6],
5002 nodes[1], nodes[3], nodes[5], nodes[7]));
5003 else if ( nbn == 9 && isQuadratic )
5004 myLastCreatedElems.Append(aMesh->AddFace( nodes[0], nodes[2], nodes[4], nodes[6],
5005 nodes[1], nodes[3], nodes[5], nodes[7],
5008 myLastCreatedElems.Append(aMesh->AddPolygonalFace( nodeVec ));
5010 while ( srcElements.Length() < myLastCreatedElems.Length() )
5011 srcElements.Append( elem );
5014 } // loop on swept elements
5017 //=======================================================================
5018 //function : RotationSweep
5020 //=======================================================================
5022 SMESH_MeshEditor::PGroupIDs
5023 SMESH_MeshEditor::RotationSweep(TIDSortedElemSet & theElems,
5024 const gp_Ax1& theAxis,
5025 const double theAngle,
5026 const int theNbSteps,
5027 const double theTol,
5028 const bool theMakeGroups,
5029 const bool theMakeWalls)
5031 myLastCreatedElems.Clear();
5032 myLastCreatedNodes.Clear();
5034 // source elements for each generated one
5035 SMESH_SequenceOfElemPtr srcElems, srcNodes;
5037 MESSAGE( "RotationSweep()");
5039 aTrsf.SetRotation( theAxis, theAngle );
5041 aTrsf2.SetRotation( theAxis, theAngle/2. );
5043 gp_Lin aLine( theAxis );
5044 double aSqTol = theTol * theTol;
5046 SMESHDS_Mesh* aMesh = GetMeshDS();
5048 TNodeOfNodeListMap mapNewNodes;
5049 TElemOfVecOfNnlmiMap mapElemNewNodes;
5050 TTElemOfElemListMap newElemsMap;
5052 const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
5053 myMesh->NbFaces(ORDER_QUADRATIC) +
5054 myMesh->NbVolumes(ORDER_QUADRATIC) );
5056 TIDSortedElemSet::iterator itElem;
5057 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
5058 const SMDS_MeshElement* elem = *itElem;
5059 if ( !elem || elem->GetType() == SMDSAbs_Volume )
5061 vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5062 newNodesItVec.reserve( elem->NbNodes() );
5064 // loop on elem nodes
5065 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5066 while ( itN->more() )
5068 // check if a node has been already sweeped
5069 const SMDS_MeshNode* node = cast2Node( itN->next() );
5071 gp_XYZ aXYZ( node->X(), node->Y(), node->Z() );
5073 aXYZ.Coord( coord[0], coord[1], coord[2] );
5074 bool isOnAxis = ( aLine.SquareDistance( aXYZ ) <= aSqTol );
5076 TNodeOfNodeListMapItr nIt =
5077 mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5078 list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5079 if ( listNewNodes.empty() )
5081 // check if we are to create medium nodes between corner ones
5082 bool needMediumNodes = false;
5083 if ( isQuadraticMesh )
5085 SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
5086 while (it->more() && !needMediumNodes )
5088 const SMDS_MeshElement* invElem = it->next();
5089 if ( invElem != elem && !theElems.count( invElem )) continue;
5090 needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
5091 if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
5092 needMediumNodes = true;
5097 const SMDS_MeshNode * newNode = node;
5098 for ( int i = 0; i < theNbSteps; i++ ) {
5100 if ( needMediumNodes ) // create a medium node
5102 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
5103 newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5104 myLastCreatedNodes.Append(newNode);
5105 srcNodes.Append( node );
5106 listNewNodes.push_back( newNode );
5107 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
5110 aTrsf.Transforms( coord[0], coord[1], coord[2] );
5112 // create a corner node
5113 newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5114 myLastCreatedNodes.Append(newNode);
5115 srcNodes.Append( node );
5116 listNewNodes.push_back( newNode );
5119 listNewNodes.push_back( newNode );
5120 // if ( needMediumNodes )
5121 // listNewNodes.push_back( newNode );
5125 newNodesItVec.push_back( nIt );
5127 // make new elements
5128 sweepElement( elem, newNodesItVec, newElemsMap[elem], theNbSteps, srcElems );
5132 makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElems, theNbSteps, srcElems );
5134 PGroupIDs newGroupIDs;
5135 if ( theMakeGroups )
5136 newGroupIDs = generateGroups( srcNodes, srcElems, "rotated");
5142 //=======================================================================
5143 //function : CreateNode
5145 //=======================================================================
5146 const SMDS_MeshNode* SMESH_MeshEditor::CreateNode(const double x,
5149 const double tolnode,
5150 SMESH_SequenceOfNode& aNodes)
5152 // myLastCreatedElems.Clear();
5153 // myLastCreatedNodes.Clear();
5156 SMESHDS_Mesh * aMesh = myMesh->GetMeshDS();
5158 // try to search in sequence of existing nodes
5159 // if aNodes.Length()>0 we 'nave to use given sequence
5160 // else - use all nodes of mesh
5161 if(aNodes.Length()>0) {
5163 for(i=1; i<=aNodes.Length(); i++) {
5164 gp_Pnt P2(aNodes.Value(i)->X(),aNodes.Value(i)->Y(),aNodes.Value(i)->Z());
5165 if(P1.Distance(P2)<tolnode)
5166 return aNodes.Value(i);
5170 SMDS_NodeIteratorPtr itn = aMesh->nodesIterator();
5171 while(itn->more()) {
5172 const SMDS_MeshNode* aN = static_cast<const SMDS_MeshNode*> (itn->next());
5173 gp_Pnt P2(aN->X(),aN->Y(),aN->Z());
5174 if(P1.Distance(P2)<tolnode)
5179 // create new node and return it
5180 const SMDS_MeshNode* NewNode = aMesh->AddNode(x,y,z);
5181 //myLastCreatedNodes.Append(NewNode);
5186 //=======================================================================
5187 //function : ExtrusionSweep
5189 //=======================================================================
5191 SMESH_MeshEditor::PGroupIDs
5192 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet & theElems,
5193 const gp_Vec& theStep,
5194 const int theNbSteps,
5195 TTElemOfElemListMap& newElemsMap,
5196 const bool theMakeGroups,
5198 const double theTolerance)
5200 ExtrusParam aParams;
5201 aParams.myDir = gp_Dir(theStep);
5202 aParams.myNodes.Clear();
5203 aParams.mySteps = new TColStd_HSequenceOfReal;
5205 for(i=1; i<=theNbSteps; i++)
5206 aParams.mySteps->Append(theStep.Magnitude());
5209 ExtrusionSweep(theElems,aParams,newElemsMap,theMakeGroups,theFlags,theTolerance);
5213 //=======================================================================
5214 //function : ExtrusionSweep
5216 //=======================================================================
5218 SMESH_MeshEditor::PGroupIDs
5219 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet & theElems,
5220 ExtrusParam& theParams,
5221 TTElemOfElemListMap& newElemsMap,
5222 const bool theMakeGroups,
5224 const double theTolerance)
5226 myLastCreatedElems.Clear();
5227 myLastCreatedNodes.Clear();
5229 // source elements for each generated one
5230 SMESH_SequenceOfElemPtr srcElems, srcNodes;
5232 SMESHDS_Mesh* aMesh = GetMeshDS();
5234 int nbsteps = theParams.mySteps->Length();
5236 TNodeOfNodeListMap mapNewNodes;
5237 //TNodeOfNodeVecMap mapNewNodes;
5238 TElemOfVecOfNnlmiMap mapElemNewNodes;
5239 //TElemOfVecOfMapNodesMap mapElemNewNodes;
5241 const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
5242 myMesh->NbFaces(ORDER_QUADRATIC) +
5243 myMesh->NbVolumes(ORDER_QUADRATIC) );
5245 TIDSortedElemSet::iterator itElem;
5246 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
5247 // check element type
5248 const SMDS_MeshElement* elem = *itElem;
5249 if ( !elem || elem->GetType() == SMDSAbs_Volume )
5252 vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5253 newNodesItVec.reserve( elem->NbNodes() );
5255 // loop on elem nodes
5256 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5257 while ( itN->more() )
5259 // check if a node has been already sweeped
5260 const SMDS_MeshNode* node = cast2Node( itN->next() );
5261 TNodeOfNodeListMap::iterator nIt =
5262 mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5263 list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5264 if ( listNewNodes.empty() )
5268 // check if we are to create medium nodes between corner ones
5269 bool needMediumNodes = false;
5270 if ( isQuadraticMesh )
5272 SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
5273 while (it->more() && !needMediumNodes )
5275 const SMDS_MeshElement* invElem = it->next();
5276 if ( invElem != elem && !theElems.count( invElem )) continue;
5277 needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
5278 if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
5279 needMediumNodes = true;
5283 double coord[] = { node->X(), node->Y(), node->Z() };
5284 for ( int i = 0; i < nbsteps; i++ )
5286 if ( needMediumNodes ) // create a medium node
5288 double x = coord[0] + theParams.myDir.X()*theParams.mySteps->Value(i+1)/2.;
5289 double y = coord[1] + theParams.myDir.Y()*theParams.mySteps->Value(i+1)/2.;
5290 double z = coord[2] + theParams.myDir.Z()*theParams.mySteps->Value(i+1)/2.;
5291 if( theFlags & EXTRUSION_FLAG_SEW ) {
5292 const SMDS_MeshNode * newNode = CreateNode(x, y, z,
5293 theTolerance, theParams.myNodes);
5294 listNewNodes.push_back( newNode );
5297 const SMDS_MeshNode * newNode = aMesh->AddNode(x, y, z);
5298 myLastCreatedNodes.Append(newNode);
5299 srcNodes.Append( node );
5300 listNewNodes.push_back( newNode );
5303 // create a corner node
5304 coord[0] = coord[0] + theParams.myDir.X()*theParams.mySteps->Value(i+1);
5305 coord[1] = coord[1] + theParams.myDir.Y()*theParams.mySteps->Value(i+1);
5306 coord[2] = coord[2] + theParams.myDir.Z()*theParams.mySteps->Value(i+1);
5307 if( theFlags & EXTRUSION_FLAG_SEW ) {
5308 const SMDS_MeshNode * newNode = CreateNode(coord[0], coord[1], coord[2],
5309 theTolerance, theParams.myNodes);
5310 listNewNodes.push_back( newNode );
5313 const SMDS_MeshNode * newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5314 myLastCreatedNodes.Append(newNode);
5315 srcNodes.Append( node );
5316 listNewNodes.push_back( newNode );
5320 newNodesItVec.push_back( nIt );
5322 // make new elements
5323 sweepElement( elem, newNodesItVec, newElemsMap[elem], nbsteps, srcElems );
5326 if( theFlags & EXTRUSION_FLAG_BOUNDARY ) {
5327 makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElems, nbsteps, srcElems );
5329 PGroupIDs newGroupIDs;
5330 if ( theMakeGroups )
5331 newGroupIDs = generateGroups( srcNodes, srcElems, "extruded");
5336 //=======================================================================
5337 //function : ExtrusionAlongTrack
5339 //=======================================================================
5340 SMESH_MeshEditor::Extrusion_Error
5341 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet & theElements,
5342 SMESH_subMesh* theTrack,
5343 const SMDS_MeshNode* theN1,
5344 const bool theHasAngles,
5345 list<double>& theAngles,
5346 const bool theLinearVariation,
5347 const bool theHasRefPoint,
5348 const gp_Pnt& theRefPoint,
5349 const bool theMakeGroups)
5351 MESSAGE("ExtrusionAlongTrack");
5352 myLastCreatedElems.Clear();
5353 myLastCreatedNodes.Clear();
5356 std::list<double> aPrms;
5357 TIDSortedElemSet::iterator itElem;
5360 TopoDS_Edge aTrackEdge;
5361 TopoDS_Vertex aV1, aV2;
5363 SMDS_ElemIteratorPtr aItE;
5364 SMDS_NodeIteratorPtr aItN;
5365 SMDSAbs_ElementType aTypeE;
5367 TNodeOfNodeListMap mapNewNodes;
5370 aNbE = theElements.size();
5373 return EXTR_NO_ELEMENTS;
5375 // 1.1 Track Pattern
5378 SMESHDS_SubMesh* pSubMeshDS = theTrack->GetSubMeshDS();
5380 aItE = pSubMeshDS->GetElements();
5381 while ( aItE->more() ) {
5382 const SMDS_MeshElement* pE = aItE->next();
5383 aTypeE = pE->GetType();
5384 // Pattern must contain links only
5385 if ( aTypeE != SMDSAbs_Edge )
5386 return EXTR_PATH_NOT_EDGE;
5389 list<SMESH_MeshEditor_PathPoint> fullList;
5391 const TopoDS_Shape& aS = theTrack->GetSubShape();
5392 // Sub-shape for the Pattern must be an Edge or Wire
5393 if( aS.ShapeType() == TopAbs_EDGE ) {
5394 aTrackEdge = TopoDS::Edge( aS );
5395 // the Edge must not be degenerated
5396 if ( SMESH_Algo::isDegenerated( aTrackEdge ) )
5397 return EXTR_BAD_PATH_SHAPE;
5398 TopExp::Vertices( aTrackEdge, aV1, aV2 );
5399 aItN = theTrack->GetFather()->GetSubMesh( aV1 )->GetSubMeshDS()->GetNodes();
5400 const SMDS_MeshNode* aN1 = aItN->next();
5401 aItN = theTrack->GetFather()->GetSubMesh( aV2 )->GetSubMeshDS()->GetNodes();
5402 const SMDS_MeshNode* aN2 = aItN->next();
5403 // starting node must be aN1 or aN2
5404 if ( !( aN1 == theN1 || aN2 == theN1 ) )
5405 return EXTR_BAD_STARTING_NODE;
5406 aItN = pSubMeshDS->GetNodes();
5407 while ( aItN->more() ) {
5408 const SMDS_MeshNode* pNode = aItN->next();
5409 const SMDS_EdgePosition* pEPos =
5410 static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
5411 double aT = pEPos->GetUParameter();
5412 aPrms.push_back( aT );
5414 //Extrusion_Error err =
5415 MakeEdgePathPoints(aPrms, aTrackEdge, (aN1==theN1), fullList);
5416 } else if( aS.ShapeType() == TopAbs_WIRE ) {
5417 list< SMESH_subMesh* > LSM;
5418 TopTools_SequenceOfShape Edges;
5419 SMESH_subMeshIteratorPtr itSM = theTrack->getDependsOnIterator(false,true);
5420 while(itSM->more()) {
5421 SMESH_subMesh* SM = itSM->next();
5423 const TopoDS_Shape& aS = SM->GetSubShape();
5426 list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
5427 int startNid = theN1->GetID();
5428 TColStd_MapOfInteger UsedNums;
5430 int NbEdges = Edges.Length();
5432 for(; i<=NbEdges; i++) {
5434 list< SMESH_subMesh* >::iterator itLSM = LSM.begin();
5435 for(; itLSM!=LSM.end(); itLSM++) {
5437 if(UsedNums.Contains(k)) continue;
5438 aTrackEdge = TopoDS::Edge( Edges.Value(k) );
5439 SMESH_subMesh* locTrack = *itLSM;
5440 SMESHDS_SubMesh* locMeshDS = locTrack->GetSubMeshDS();
5441 TopExp::Vertices( aTrackEdge, aV1, aV2 );
5442 aItN = locTrack->GetFather()->GetSubMesh(aV1)->GetSubMeshDS()->GetNodes();
5443 const SMDS_MeshNode* aN1 = aItN->next();
5444 aItN = locTrack->GetFather()->GetSubMesh(aV2)->GetSubMeshDS()->GetNodes();
5445 const SMDS_MeshNode* aN2 = aItN->next();
5446 // starting node must be aN1 or aN2
5447 if ( !( aN1->GetID() == startNid || aN2->GetID() == startNid ) ) continue;
5448 // 2. Collect parameters on the track edge
5450 aItN = locMeshDS->GetNodes();
5451 while ( aItN->more() ) {
5452 const SMDS_MeshNode* pNode = aItN->next();
5453 const SMDS_EdgePosition* pEPos =
5454 static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
5455 double aT = pEPos->GetUParameter();
5456 aPrms.push_back( aT );
5458 list<SMESH_MeshEditor_PathPoint> LPP;
5459 //Extrusion_Error err =
5460 MakeEdgePathPoints(aPrms, aTrackEdge,(aN1->GetID()==startNid), LPP);
5461 LLPPs.push_back(LPP);
5463 // update startN for search following egde
5464 if( aN1->GetID() == startNid ) startNid = aN2->GetID();
5465 else startNid = aN1->GetID();
5469 list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
5470 list<SMESH_MeshEditor_PathPoint> firstList = *itLLPP;
5471 list<SMESH_MeshEditor_PathPoint>::iterator itPP = firstList.begin();
5472 for(; itPP!=firstList.end(); itPP++) {
5473 fullList.push_back( *itPP );
5475 SMESH_MeshEditor_PathPoint PP1 = fullList.back();
5476 fullList.pop_back();
5478 for(; itLLPP!=LLPPs.end(); itLLPP++) {
5479 list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
5480 itPP = currList.begin();
5481 SMESH_MeshEditor_PathPoint PP2 = currList.front();
5482 gp_Dir D1 = PP1.Tangent();
5483 gp_Dir D2 = PP2.Tangent();
5484 gp_Dir Dnew( gp_Vec( (D1.X()+D2.X())/2, (D1.Y()+D2.Y())/2,
5485 (D1.Z()+D2.Z())/2 ) );
5486 PP1.SetTangent(Dnew);
5487 fullList.push_back(PP1);
5489 for(; itPP!=firstList.end(); itPP++) {
5490 fullList.push_back( *itPP );
5492 PP1 = fullList.back();
5493 fullList.pop_back();
5495 // if wire not closed
5496 fullList.push_back(PP1);
5500 return EXTR_BAD_PATH_SHAPE;
5503 return MakeExtrElements(theElements, fullList, theHasAngles, theAngles, theLinearVariation,
5504 theHasRefPoint, theRefPoint, theMakeGroups);
5508 //=======================================================================
5509 //function : ExtrusionAlongTrack
5511 //=======================================================================
5512 SMESH_MeshEditor::Extrusion_Error
5513 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet & theElements,
5514 SMESH_Mesh* theTrack,
5515 const SMDS_MeshNode* theN1,
5516 const bool theHasAngles,
5517 list<double>& theAngles,
5518 const bool theLinearVariation,
5519 const bool theHasRefPoint,
5520 const gp_Pnt& theRefPoint,
5521 const bool theMakeGroups)
5523 myLastCreatedElems.Clear();
5524 myLastCreatedNodes.Clear();
5527 std::list<double> aPrms;
5528 TIDSortedElemSet::iterator itElem;
5531 TopoDS_Edge aTrackEdge;
5532 TopoDS_Vertex aV1, aV2;
5534 SMDS_ElemIteratorPtr aItE;
5535 SMDS_NodeIteratorPtr aItN;
5536 SMDSAbs_ElementType aTypeE;
5538 TNodeOfNodeListMap mapNewNodes;
5541 aNbE = theElements.size();
5544 return EXTR_NO_ELEMENTS;
5546 // 1.1 Track Pattern
5549 SMESHDS_Mesh* pMeshDS = theTrack->GetMeshDS();
5551 aItE = pMeshDS->elementsIterator();
5552 while ( aItE->more() ) {
5553 const SMDS_MeshElement* pE = aItE->next();
5554 aTypeE = pE->GetType();
5555 // Pattern must contain links only
5556 if ( aTypeE != SMDSAbs_Edge )
5557 return EXTR_PATH_NOT_EDGE;
5560 list<SMESH_MeshEditor_PathPoint> fullList;
5562 const TopoDS_Shape& aS = theTrack->GetShapeToMesh();
5564 if ( !theTrack->HasShapeToMesh() ) {
5565 //Mesh without shape
5566 const SMDS_MeshNode* currentNode = NULL;
5567 const SMDS_MeshNode* prevNode = theN1;
5568 std::vector<const SMDS_MeshNode*> aNodesList;
5569 aNodesList.push_back(theN1);
5570 int nbEdges = 0, conn=0;
5571 const SMDS_MeshElement* prevElem = NULL;
5572 const SMDS_MeshElement* currentElem = NULL;
5573 int totalNbEdges = theTrack->NbEdges();
5574 SMDS_ElemIteratorPtr nIt;
5577 if( !theTrack->GetMeshDS()->Contains(theN1) ) {
5578 return EXTR_BAD_STARTING_NODE;
5581 conn = nbEdgeConnectivity(theN1);
5583 return EXTR_PATH_NOT_EDGE;
5585 aItE = theN1->GetInverseElementIterator();
5586 prevElem = aItE->next();
5587 currentElem = prevElem;
5589 if(totalNbEdges == 1 ) {
5590 nIt = currentElem->nodesIterator();
5591 currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
5592 if(currentNode == prevNode)
5593 currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
5594 aNodesList.push_back(currentNode);
5596 nIt = currentElem->nodesIterator();
5597 while( nIt->more() ) {
5598 currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
5599 if(currentNode == prevNode)
5600 currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
5601 aNodesList.push_back(currentNode);
5603 //case of the closed mesh
5604 if(currentNode == theN1) {
5609 conn = nbEdgeConnectivity(currentNode);
5611 return EXTR_PATH_NOT_EDGE;
5612 }else if( conn == 1 && nbEdges > 0 ) {
5617 prevNode = currentNode;
5618 aItE = currentNode->GetInverseElementIterator();
5619 currentElem = aItE->next();
5620 if( currentElem == prevElem)
5621 currentElem = aItE->next();
5622 nIt = currentElem->nodesIterator();
5623 prevElem = currentElem;
5629 if(nbEdges != totalNbEdges)
5630 return EXTR_PATH_NOT_EDGE;
5632 TopTools_SequenceOfShape Edges;
5633 double x1,x2,y1,y2,z1,z2;
5634 list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
5635 int startNid = theN1->GetID();
5636 for(int i = 1; i < aNodesList.size(); i++) {
5637 x1 = aNodesList[i-1]->X();x2 = aNodesList[i]->X();
5638 y1 = aNodesList[i-1]->Y();y2 = aNodesList[i]->Y();
5639 z1 = aNodesList[i-1]->Z();z2 = aNodesList[i]->Z();
5640 TopoDS_Edge e = BRepBuilderAPI_MakeEdge(gp_Pnt(x1,y1,z1),gp_Pnt(x2,y2,z2));
5641 list<SMESH_MeshEditor_PathPoint> LPP;
5643 MakeEdgePathPoints(aPrms, e, (aNodesList[i-1]->GetID()==startNid), LPP);
5644 LLPPs.push_back(LPP);
5645 if( aNodesList[i-1]->GetID() == startNid ) startNid = aNodesList[i]->GetID();
5646 else startNid = aNodesList[i-1]->GetID();
5650 list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
5651 list<SMESH_MeshEditor_PathPoint> firstList = *itLLPP;
5652 list<SMESH_MeshEditor_PathPoint>::iterator itPP = firstList.begin();
5653 for(; itPP!=firstList.end(); itPP++) {
5654 fullList.push_back( *itPP );
5657 SMESH_MeshEditor_PathPoint PP1 = fullList.back();
5658 SMESH_MeshEditor_PathPoint PP2;
5659 fullList.pop_back();
5661 for(; itLLPP!=LLPPs.end(); itLLPP++) {
5662 list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
5663 itPP = currList.begin();
5664 PP2 = currList.front();
5665 gp_Dir D1 = PP1.Tangent();
5666 gp_Dir D2 = PP2.Tangent();
5667 gp_Dir Dnew( gp_Vec( (D1.X()+D2.X())/2, (D1.Y()+D2.Y())/2,
5668 (D1.Z()+D2.Z())/2 ) );
5669 PP1.SetTangent(Dnew);
5670 fullList.push_back(PP1);
5672 for(; itPP!=currList.end(); itPP++) {
5673 fullList.push_back( *itPP );
5675 PP1 = fullList.back();
5676 fullList.pop_back();
5678 fullList.push_back(PP1);
5680 } // Sub-shape for the Pattern must be an Edge or Wire
5681 else if( aS.ShapeType() == TopAbs_EDGE ) {
5682 aTrackEdge = TopoDS::Edge( aS );
5683 // the Edge must not be degenerated
5684 if ( SMESH_Algo::isDegenerated( aTrackEdge ) )
5685 return EXTR_BAD_PATH_SHAPE;
5686 TopExp::Vertices( aTrackEdge, aV1, aV2 );
5687 const SMDS_MeshNode* aN1 = SMESH_Algo::VertexNode( aV1, pMeshDS );
5688 const SMDS_MeshNode* aN2 = SMESH_Algo::VertexNode( aV2, pMeshDS );
5689 // starting node must be aN1 or aN2
5690 if ( !( aN1 == theN1 || aN2 == theN1 ) )
5691 return EXTR_BAD_STARTING_NODE;
5692 aItN = pMeshDS->nodesIterator();
5693 while ( aItN->more() ) {
5694 const SMDS_MeshNode* pNode = aItN->next();
5695 if( pNode==aN1 || pNode==aN2 ) continue;
5696 const SMDS_EdgePosition* pEPos =
5697 static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
5698 double aT = pEPos->GetUParameter();
5699 aPrms.push_back( aT );
5701 //Extrusion_Error err =
5702 MakeEdgePathPoints(aPrms, aTrackEdge, (aN1==theN1), fullList);
5704 else if( aS.ShapeType() == TopAbs_WIRE ) {
5705 list< SMESH_subMesh* > LSM;
5706 TopTools_SequenceOfShape Edges;
5707 TopExp_Explorer eExp(aS, TopAbs_EDGE);
5708 for(; eExp.More(); eExp.Next()) {
5709 TopoDS_Edge E = TopoDS::Edge( eExp.Current() );
5710 if( SMESH_Algo::isDegenerated(E) ) continue;
5711 SMESH_subMesh* SM = theTrack->GetSubMesh(E);
5717 list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
5718 TopoDS_Vertex aVprev;
5719 TColStd_MapOfInteger UsedNums;
5720 int NbEdges = Edges.Length();
5722 for(; i<=NbEdges; i++) {
5724 list< SMESH_subMesh* >::iterator itLSM = LSM.begin();
5725 for(; itLSM!=LSM.end(); itLSM++) {
5727 if(UsedNums.Contains(k)) continue;
5728 aTrackEdge = TopoDS::Edge( Edges.Value(k) );
5729 SMESH_subMesh* locTrack = *itLSM;
5730 SMESHDS_SubMesh* locMeshDS = locTrack->GetSubMeshDS();
5731 TopExp::Vertices( aTrackEdge, aV1, aV2 );
5732 bool aN1isOK = false, aN2isOK = false;
5733 if ( aVprev.IsNull() ) {
5734 // if previous vertex is not yet defined, it means that we in the beginning of wire
5735 // and we have to find initial vertex corresponding to starting node theN1
5736 const SMDS_MeshNode* aN1 = SMESH_Algo::VertexNode( aV1, pMeshDS );
5737 const SMDS_MeshNode* aN2 = SMESH_Algo::VertexNode( aV2, pMeshDS );
5738 // starting node must be aN1 or aN2
5739 aN1isOK = ( aN1 && aN1 == theN1 );
5740 aN2isOK = ( aN2 && aN2 == theN1 );
5743 // we have specified ending vertex of the previous edge on the previous iteration
5744 // and we have just to check that it corresponds to any vertex in current segment
5745 aN1isOK = aVprev.IsSame( aV1 );
5746 aN2isOK = aVprev.IsSame( aV2 );
5748 if ( !aN1isOK && !aN2isOK ) continue;
5749 // 2. Collect parameters on the track edge
5751 aItN = locMeshDS->GetNodes();
5752 while ( aItN->more() ) {
5753 const SMDS_MeshNode* pNode = aItN->next();
5754 const SMDS_EdgePosition* pEPos =
5755 static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
5756 double aT = pEPos->GetUParameter();
5757 aPrms.push_back( aT );
5759 list<SMESH_MeshEditor_PathPoint> LPP;
5760 //Extrusion_Error err =
5761 MakeEdgePathPoints(aPrms, aTrackEdge, aN1isOK, LPP);
5762 LLPPs.push_back(LPP);
5764 // update startN for search following egde
5765 if ( aN1isOK ) aVprev = aV2;
5770 list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
5771 list<SMESH_MeshEditor_PathPoint>& firstList = *itLLPP;
5772 fullList.splice( fullList.end(), firstList );
5774 SMESH_MeshEditor_PathPoint PP1 = fullList.back();
5775 fullList.pop_back();
5777 for(; itLLPP!=LLPPs.end(); itLLPP++) {
5778 list<SMESH_MeshEditor_PathPoint>& currList = *itLLPP;
5779 SMESH_MeshEditor_PathPoint PP2 = currList.front();
5780 gp_Dir D1 = PP1.Tangent();
5781 gp_Dir D2 = PP2.Tangent();
5782 gp_Dir Dnew( ( D1.XYZ() + D2.XYZ() ) / 2 );
5783 PP1.SetTangent(Dnew);
5784 fullList.push_back(PP1);
5785 fullList.splice( fullList.end(), currList, ++currList.begin(), currList.end() );
5786 PP1 = fullList.back();
5787 fullList.pop_back();
5789 // if wire not closed
5790 fullList.push_back(PP1);
5794 return EXTR_BAD_PATH_SHAPE;
5797 return MakeExtrElements(theElements, fullList, theHasAngles, theAngles, theLinearVariation,
5798 theHasRefPoint, theRefPoint, theMakeGroups);
5802 //=======================================================================
5803 //function : MakeEdgePathPoints
5804 //purpose : auxilary for ExtrusionAlongTrack
5805 //=======================================================================
5806 SMESH_MeshEditor::Extrusion_Error
5807 SMESH_MeshEditor::MakeEdgePathPoints(std::list<double>& aPrms,
5808 const TopoDS_Edge& aTrackEdge,
5810 list<SMESH_MeshEditor_PathPoint>& LPP)
5812 Standard_Real aTx1, aTx2, aL2, aTolVec, aTolVec2;
5814 aTolVec2=aTolVec*aTolVec;
5816 TopoDS_Vertex aV1, aV2;
5817 TopExp::Vertices( aTrackEdge, aV1, aV2 );
5818 aT1=BRep_Tool::Parameter( aV1, aTrackEdge );
5819 aT2=BRep_Tool::Parameter( aV2, aTrackEdge );
5820 // 2. Collect parameters on the track edge
5821 aPrms.push_front( aT1 );
5822 aPrms.push_back( aT2 );
5825 if( FirstIsStart ) {
5836 SMESH_MeshEditor_PathPoint aPP;
5837 Handle(Geom_Curve) aC3D = BRep_Tool::Curve( aTrackEdge, aTx1, aTx2 );
5838 std::list<double>::iterator aItD = aPrms.begin();
5839 for(; aItD != aPrms.end(); ++aItD) {
5843 aC3D->D1( aT, aP3D, aVec );
5844 aL2 = aVec.SquareMagnitude();
5845 if ( aL2 < aTolVec2 )
5846 return EXTR_CANT_GET_TANGENT;
5847 gp_Dir aTgt( aVec );
5849 aPP.SetTangent( aTgt );
5850 aPP.SetParameter( aT );
5857 //=======================================================================
5858 //function : MakeExtrElements
5859 //purpose : auxilary for ExtrusionAlongTrack
5860 //=======================================================================
5861 SMESH_MeshEditor::Extrusion_Error
5862 SMESH_MeshEditor::MakeExtrElements(TIDSortedElemSet& theElements,
5863 list<SMESH_MeshEditor_PathPoint>& fullList,
5864 const bool theHasAngles,
5865 list<double>& theAngles,
5866 const bool theLinearVariation,
5867 const bool theHasRefPoint,
5868 const gp_Pnt& theRefPoint,
5869 const bool theMakeGroups)
5871 const int aNbTP = fullList.size();
5873 if( theHasAngles && !theAngles.empty() && theLinearVariation )
5874 LinearAngleVariation(aNbTP-1, theAngles);
5875 // fill vector of path points with angles
5876 vector<SMESH_MeshEditor_PathPoint> aPPs;
5877 list<SMESH_MeshEditor_PathPoint>::iterator itPP = fullList.begin();
5878 list<double>::iterator itAngles = theAngles.begin();
5879 aPPs.push_back( *itPP++ );
5880 for( ; itPP != fullList.end(); itPP++) {
5881 aPPs.push_back( *itPP );
5882 if ( theHasAngles && itAngles != theAngles.end() )
5883 aPPs.back().SetAngle( *itAngles++ );
5886 TNodeOfNodeListMap mapNewNodes;
5887 TElemOfVecOfNnlmiMap mapElemNewNodes;
5888 TTElemOfElemListMap newElemsMap;
5889 TIDSortedElemSet::iterator itElem;
5890 // source elements for each generated one
5891 SMESH_SequenceOfElemPtr srcElems, srcNodes;
5893 // 3. Center of rotation aV0
5894 gp_Pnt aV0 = theRefPoint;
5895 if ( !theHasRefPoint )
5897 gp_XYZ aGC( 0.,0.,0. );
5898 TIDSortedElemSet newNodes;
5900 itElem = theElements.begin();
5901 for ( ; itElem != theElements.end(); itElem++ ) {
5902 const SMDS_MeshElement* elem = *itElem;
5904 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5905 while ( itN->more() ) {
5906 const SMDS_MeshElement* node = itN->next();
5907 if ( newNodes.insert( node ).second )
5908 aGC += SMESH_TNodeXYZ( node );
5911 aGC /= newNodes.size();
5913 } // if (!theHasRefPoint) {
5915 // 4. Processing the elements
5916 SMESHDS_Mesh* aMesh = GetMeshDS();
5918 for ( itElem = theElements.begin(); itElem != theElements.end(); itElem++ ) {
5919 // check element type
5920 const SMDS_MeshElement* elem = *itElem;
5921 SMDSAbs_ElementType aTypeE = elem->GetType();
5922 if ( !elem || ( aTypeE != SMDSAbs_Face && aTypeE != SMDSAbs_Edge ) )
5925 vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5926 newNodesItVec.reserve( elem->NbNodes() );
5928 // loop on elem nodes
5930 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5931 while ( itN->more() )
5934 // check if a node has been already processed
5935 const SMDS_MeshNode* node =
5936 static_cast<const SMDS_MeshNode*>( itN->next() );
5937 TNodeOfNodeListMap::iterator nIt = mapNewNodes.find( node );
5938 if ( nIt == mapNewNodes.end() ) {
5939 nIt = mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5940 list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5943 Standard_Real aAngle1x, aAngleT1T0, aTolAng;
5944 gp_Pnt aP0x, aP1x, aPN0, aPN1, aV0x, aV1x;
5945 gp_Ax1 anAx1, anAxT1T0;
5946 gp_Dir aDT1x, aDT0x, aDT1T0;
5951 aPN0 = SMESH_TNodeXYZ( node );
5953 const SMESH_MeshEditor_PathPoint& aPP0 = aPPs[0];
5955 aDT0x= aPP0.Tangent();
5956 //cout<<"j = 0 PP: Pnt("<<aP0x.X()<<","<<aP0x.Y()<<","<<aP0x.Z()<<")"<<endl;
5958 for ( int j = 1; j < aNbTP; ++j ) {
5959 const SMESH_MeshEditor_PathPoint& aPP1 = aPPs[j];
5961 aDT1x = aPP1.Tangent();
5962 aAngle1x = aPP1.Angle();
5964 gp_Trsf aTrsf, aTrsfRot, aTrsfRotT1T0;
5966 gp_Vec aV01x( aP0x, aP1x );
5967 aTrsf.SetTranslation( aV01x );
5970 aV1x = aV0x.Transformed( aTrsf );
5971 aPN1 = aPN0.Transformed( aTrsf );
5973 // rotation 1 [ T1,T0 ]
5974 aAngleT1T0=-aDT1x.Angle( aDT0x );
5975 if (fabs(aAngleT1T0) > aTolAng) {
5977 anAxT1T0.SetLocation( aV1x );
5978 anAxT1T0.SetDirection( aDT1T0 );
5979 aTrsfRotT1T0.SetRotation( anAxT1T0, aAngleT1T0 );
5981 aPN1 = aPN1.Transformed( aTrsfRotT1T0 );
5985 if ( theHasAngles ) {
5986 anAx1.SetLocation( aV1x );
5987 anAx1.SetDirection( aDT1x );
5988 aTrsfRot.SetRotation( anAx1, aAngle1x );
5990 aPN1 = aPN1.Transformed( aTrsfRot );
5994 //MESSAGE("elem->IsQuadratic " << elem->IsQuadratic() << " " << elem->IsMediumNode(node));
5995 if( elem->IsQuadratic() && !elem->IsMediumNode(node) ) {
5996 // create additional node
5997 double x = ( aPN1.X() + aPN0.X() )/2.;
5998 double y = ( aPN1.Y() + aPN0.Y() )/2.;
5999 double z = ( aPN1.Z() + aPN0.Z() )/2.;
6000 const SMDS_MeshNode* newNode = aMesh->AddNode(x,y,z);
6001 myLastCreatedNodes.Append(newNode);
6002 srcNodes.Append( node );
6003 listNewNodes.push_back( newNode );
6005 const SMDS_MeshNode* newNode = aMesh->AddNode( aPN1.X(), aPN1.Y(), aPN1.Z() );
6006 myLastCreatedNodes.Append(newNode);
6007 srcNodes.Append( node );
6008 listNewNodes.push_back( newNode );
6018 // if current elem is quadratic and current node is not medium
6019 // we have to check - may be it is needed to insert additional nodes
6020 if( elem->IsQuadratic() && !elem->IsMediumNode(node) ) {
6021 list< const SMDS_MeshNode* > & listNewNodes = nIt->second;
6022 if(listNewNodes.size()==aNbTP-1) {
6023 vector<const SMDS_MeshNode*> aNodes(2*(aNbTP-1));
6024 gp_XYZ P(node->X(), node->Y(), node->Z());
6025 list< const SMDS_MeshNode* >::iterator it = listNewNodes.begin();
6027 for(i=0; i<aNbTP-1; i++) {
6028 const SMDS_MeshNode* N = *it;
6029 double x = ( N->X() + P.X() )/2.;
6030 double y = ( N->Y() + P.Y() )/2.;
6031 double z = ( N->Z() + P.Z() )/2.;
6032 const SMDS_MeshNode* newN = aMesh->AddNode(x,y,z);
6033 srcNodes.Append( node );
6034 myLastCreatedNodes.Append(newN);
6037 P = gp_XYZ(N->X(),N->Y(),N->Z());
6039 listNewNodes.clear();
6040 for(i=0; i<2*(aNbTP-1); i++) {
6041 listNewNodes.push_back(aNodes[i]);
6047 newNodesItVec.push_back( nIt );
6049 // make new elements
6050 //sweepElement( aMesh, elem, newNodesItVec, newElemsMap[elem],
6051 // newNodesItVec[0]->second.size(), myLastCreatedElems );
6052 sweepElement( elem, newNodesItVec, newElemsMap[elem], aNbTP-1, srcElems );
6055 makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElements, aNbTP-1, srcElems );
6057 if ( theMakeGroups )
6058 generateGroups( srcNodes, srcElems, "extruded");
6064 //=======================================================================
6065 //function : LinearAngleVariation
6066 //purpose : auxilary for ExtrusionAlongTrack
6067 //=======================================================================
6068 void SMESH_MeshEditor::LinearAngleVariation(const int nbSteps,
6069 list<double>& Angles)
6071 int nbAngles = Angles.size();
6072 if( nbSteps > nbAngles ) {
6073 vector<double> theAngles(nbAngles);
6074 list<double>::iterator it = Angles.begin();
6076 for(; it!=Angles.end(); it++) {
6078 theAngles[i] = (*it);
6081 double rAn2St = double( nbAngles ) / double( nbSteps );
6082 double angPrev = 0, angle;
6083 for ( int iSt = 0; iSt < nbSteps; ++iSt ) {
6084 double angCur = rAn2St * ( iSt+1 );
6085 double angCurFloor = floor( angCur );
6086 double angPrevFloor = floor( angPrev );
6087 if ( angPrevFloor == angCurFloor )
6088 angle = rAn2St * theAngles[ int( angCurFloor ) ];
6090 int iP = int( angPrevFloor );
6091 double angPrevCeil = ceil(angPrev);
6092 angle = ( angPrevCeil - angPrev ) * theAngles[ iP ];
6094 int iC = int( angCurFloor );
6095 if ( iC < nbAngles )
6096 angle += ( angCur - angCurFloor ) * theAngles[ iC ];
6098 iP = int( angPrevCeil );
6100 angle += theAngles[ iC ];
6102 res.push_back(angle);
6107 for(; it!=res.end(); it++)
6108 Angles.push_back( *it );
6113 //================================================================================
6115 * \brief Move or copy theElements applying theTrsf to their nodes
6116 * \param theElems - elements to transform, if theElems is empty then apply to all mesh nodes
6117 * \param theTrsf - transformation to apply
6118 * \param theCopy - if true, create translated copies of theElems
6119 * \param theMakeGroups - if true and theCopy, create translated groups
6120 * \param theTargetMesh - mesh to copy translated elements into
6121 * \return SMESH_MeshEditor::PGroupIDs - list of ids of created groups
6123 //================================================================================
6125 SMESH_MeshEditor::PGroupIDs
6126 SMESH_MeshEditor::Transform (TIDSortedElemSet & theElems,
6127 const gp_Trsf& theTrsf,
6129 const bool theMakeGroups,
6130 SMESH_Mesh* theTargetMesh)
6132 myLastCreatedElems.Clear();
6133 myLastCreatedNodes.Clear();
6135 bool needReverse = false;
6136 string groupPostfix;
6137 switch ( theTrsf.Form() ) {
6139 MESSAGE("gp_PntMirror");
6141 groupPostfix = "mirrored";
6144 MESSAGE("gp_Ax1Mirror");
6145 groupPostfix = "mirrored";
6148 MESSAGE("gp_Ax2Mirror");
6150 groupPostfix = "mirrored";
6153 MESSAGE("gp_Rotation");
6154 groupPostfix = "rotated";
6156 case gp_Translation:
6157 MESSAGE("gp_Translation");
6158 groupPostfix = "translated";
6161 MESSAGE("gp_Scale");
6162 groupPostfix = "scaled";
6164 case gp_CompoundTrsf: // different scale by axis
6165 MESSAGE("gp_CompoundTrsf");
6166 groupPostfix = "scaled";
6170 needReverse = false;
6171 groupPostfix = "transformed";
6174 SMESH_MeshEditor targetMeshEditor( theTargetMesh );
6175 SMESHDS_Mesh* aTgtMesh = theTargetMesh ? theTargetMesh->GetMeshDS() : 0;
6176 SMESHDS_Mesh* aMesh = GetMeshDS();
6179 // map old node to new one
6180 TNodeNodeMap nodeMap;
6182 // elements sharing moved nodes; those of them which have all
6183 // nodes mirrored but are not in theElems are to be reversed
6184 TIDSortedElemSet inverseElemSet;
6186 // source elements for each generated one
6187 SMESH_SequenceOfElemPtr srcElems, srcNodes;
6189 // issue 021015: EDF 1578 SMESH: Free nodes are removed when translating a mesh
6190 TIDSortedElemSet orphanNode;
6192 if ( theElems.empty() ) // transform the whole mesh
6195 SMDS_ElemIteratorPtr eIt = aMesh->elementsIterator();
6196 while ( eIt->more() ) theElems.insert( eIt->next() );
6198 SMDS_NodeIteratorPtr nIt = aMesh->nodesIterator();
6199 while ( nIt->more() )
6201 const SMDS_MeshNode* node = nIt->next();
6202 if ( node->NbInverseElements() == 0)
6203 orphanNode.insert( node );
6207 // loop on elements to transform nodes : first orphan nodes then elems
6208 TIDSortedElemSet::iterator itElem;
6209 TIDSortedElemSet *elements[] = {&orphanNode, &theElems };
6210 for (int i=0; i<2; i++)
6211 for ( itElem = elements[i]->begin(); itElem != elements[i]->end(); itElem++ ) {
6212 const SMDS_MeshElement* elem = *itElem;
6216 // loop on elem nodes
6217 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6218 while ( itN->more() ) {
6220 const SMDS_MeshNode* node = cast2Node( itN->next() );
6221 // check if a node has been already transformed
6222 pair<TNodeNodeMap::iterator,bool> n2n_isnew =
6223 nodeMap.insert( make_pair ( node, node ));
6224 if ( !n2n_isnew.second )
6228 coord[0] = node->X();
6229 coord[1] = node->Y();
6230 coord[2] = node->Z();
6231 theTrsf.Transforms( coord[0], coord[1], coord[2] );
6232 if ( theTargetMesh ) {
6233 const SMDS_MeshNode * newNode = aTgtMesh->AddNode( coord[0], coord[1], coord[2] );
6234 n2n_isnew.first->second = newNode;
6235 myLastCreatedNodes.Append(newNode);
6236 srcNodes.Append( node );
6238 else if ( theCopy ) {
6239 const SMDS_MeshNode * newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
6240 n2n_isnew.first->second = newNode;
6241 myLastCreatedNodes.Append(newNode);
6242 srcNodes.Append( node );
6245 aMesh->MoveNode( node, coord[0], coord[1], coord[2] );
6246 // node position on shape becomes invalid
6247 const_cast< SMDS_MeshNode* > ( node )->SetPosition
6248 ( SMDS_SpacePosition::originSpacePosition() );
6251 // keep inverse elements
6252 if ( !theCopy && !theTargetMesh && needReverse ) {
6253 SMDS_ElemIteratorPtr invElemIt = node->GetInverseElementIterator();
6254 while ( invElemIt->more() ) {
6255 const SMDS_MeshElement* iel = invElemIt->next();
6256 inverseElemSet.insert( iel );
6262 // either create new elements or reverse mirrored ones
6263 if ( !theCopy && !needReverse && !theTargetMesh )
6266 TIDSortedElemSet::iterator invElemIt = inverseElemSet.begin();
6267 for ( ; invElemIt != inverseElemSet.end(); invElemIt++ )
6268 theElems.insert( *invElemIt );
6270 // Replicate or reverse elements
6272 std::vector<int> iForw;
6273 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
6275 const SMDS_MeshElement* elem = *itElem;
6276 if ( !elem ) continue;
6278 SMDSAbs_GeometryType geomType = elem->GetGeomType();
6279 int nbNodes = elem->NbNodes();
6280 if ( geomType == SMDSGeom_NONE ) continue; // node
6282 switch ( geomType ) {
6284 case SMDSGeom_POLYGON: // ---------------------- polygon
6286 vector<const SMDS_MeshNode*> poly_nodes (nbNodes);
6288 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6289 while (itN->more()) {
6290 const SMDS_MeshNode* node =
6291 static_cast<const SMDS_MeshNode*>(itN->next());
6292 TNodeNodeMap::iterator nodeMapIt = nodeMap.find(node);
6293 if (nodeMapIt == nodeMap.end())
6294 break; // not all nodes transformed
6296 // reverse mirrored faces and volumes
6297 poly_nodes[nbNodes - iNode - 1] = (*nodeMapIt).second;
6299 poly_nodes[iNode] = (*nodeMapIt).second;
6303 if ( iNode != nbNodes )
6304 continue; // not all nodes transformed
6306 if ( theTargetMesh ) {
6307 myLastCreatedElems.Append(aTgtMesh->AddPolygonalFace(poly_nodes));
6308 srcElems.Append( elem );
6310 else if ( theCopy ) {
6311 myLastCreatedElems.Append(aMesh->AddPolygonalFace(poly_nodes));
6312 srcElems.Append( elem );
6315 aMesh->ChangePolygonNodes(elem, poly_nodes);
6320 case SMDSGeom_POLYHEDRA: // ------------------ polyhedral volume
6322 const SMDS_VtkVolume* aPolyedre =
6323 dynamic_cast<const SMDS_VtkVolume*>( elem );
6325 MESSAGE("Warning: bad volumic element");
6329 vector<const SMDS_MeshNode*> poly_nodes; poly_nodes.reserve( nbNodes );
6330 vector<int> quantities; quantities.reserve( nbNodes );
6332 bool allTransformed = true;
6333 int nbFaces = aPolyedre->NbFaces();
6334 for (int iface = 1; iface <= nbFaces && allTransformed; iface++) {
6335 int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
6336 for (int inode = 1; inode <= nbFaceNodes && allTransformed; inode++) {
6337 const SMDS_MeshNode* node = aPolyedre->GetFaceNode(iface, inode);
6338 TNodeNodeMap::iterator nodeMapIt = nodeMap.find(node);
6339 if (nodeMapIt == nodeMap.end()) {
6340 allTransformed = false; // not all nodes transformed
6342 poly_nodes.push_back((*nodeMapIt).second);
6344 if ( needReverse && allTransformed )
6345 std::reverse( poly_nodes.end() - nbFaceNodes, poly_nodes.end() );
6347 quantities.push_back(nbFaceNodes);
6349 if ( !allTransformed )
6350 continue; // not all nodes transformed
6352 if ( theTargetMesh ) {
6353 myLastCreatedElems.Append(aTgtMesh->AddPolyhedralVolume(poly_nodes, quantities));
6354 srcElems.Append( elem );
6356 else if ( theCopy ) {
6357 myLastCreatedElems.Append(aMesh->AddPolyhedralVolume(poly_nodes, quantities));
6358 srcElems.Append( elem );
6361 aMesh->ChangePolyhedronNodes(elem, poly_nodes, quantities);
6366 case SMDSGeom_BALL: // -------------------- Ball
6368 if ( !theCopy && !theTargetMesh ) continue;
6370 TNodeNodeMap::iterator nodeMapIt = nodeMap.find( elem->GetNode(0) );
6371 if (nodeMapIt == nodeMap.end())
6372 continue; // not all nodes transformed
6374 double diameter = static_cast<const SMDS_BallElement*>(elem)->GetDiameter();
6375 if ( theTargetMesh ) {
6376 myLastCreatedElems.Append(aTgtMesh->AddBall( nodeMapIt->second, diameter ));
6377 srcElems.Append( elem );
6380 myLastCreatedElems.Append(aMesh->AddBall( nodeMapIt->second, diameter ));
6381 srcElems.Append( elem );
6386 default: // ----------------------- Regular elements
6388 while ( iForw.size() < nbNodes ) iForw.push_back( iForw.size() );
6389 const std::vector<int>& iRev = SMDS_MeshCell::reverseSmdsOrder( elem->GetEntityType() );
6390 const std::vector<int>& i = needReverse ? iRev : iForw;
6392 // find transformed nodes
6393 vector<const SMDS_MeshNode*> nodes(nbNodes);
6395 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6396 while ( itN->more() ) {
6397 const SMDS_MeshNode* node =
6398 static_cast<const SMDS_MeshNode*>( itN->next() );
6399 TNodeNodeMap::iterator nodeMapIt = nodeMap.find( node );
6400 if ( nodeMapIt == nodeMap.end() )
6401 break; // not all nodes transformed
6402 nodes[ i [ iNode++ ]] = (*nodeMapIt).second;
6404 if ( iNode != nbNodes )
6405 continue; // not all nodes transformed
6407 if ( theTargetMesh ) {
6408 if ( SMDS_MeshElement* copy =
6409 targetMeshEditor.AddElement( nodes, elem->GetType(), elem->IsPoly() )) {
6410 myLastCreatedElems.Append( copy );
6411 srcElems.Append( elem );
6414 else if ( theCopy ) {
6415 if ( AddElement( nodes, elem->GetType(), elem->IsPoly() ))
6416 srcElems.Append( elem );
6419 // reverse element as it was reversed by transformation
6421 aMesh->ChangeElementNodes( elem, &nodes[0], nbNodes );
6423 } // switch ( geomType )
6425 } // loop on elements
6427 PGroupIDs newGroupIDs;
6429 if ( ( theMakeGroups && theCopy ) ||
6430 ( theMakeGroups && theTargetMesh ) )
6431 newGroupIDs = generateGroups( srcNodes, srcElems, groupPostfix, theTargetMesh );
6436 //=======================================================================
6438 * \brief Create groups of elements made during transformation
6439 * \param nodeGens - nodes making corresponding myLastCreatedNodes
6440 * \param elemGens - elements making corresponding myLastCreatedElems
6441 * \param postfix - to append to names of new groups
6443 //=======================================================================
6445 SMESH_MeshEditor::PGroupIDs
6446 SMESH_MeshEditor::generateGroups(const SMESH_SequenceOfElemPtr& nodeGens,
6447 const SMESH_SequenceOfElemPtr& elemGens,
6448 const std::string& postfix,
6449 SMESH_Mesh* targetMesh)
6451 PGroupIDs newGroupIDs( new list<int> );
6452 SMESH_Mesh* mesh = targetMesh ? targetMesh : GetMesh();
6454 // Sort existing groups by types and collect their names
6456 // to store an old group and a generated new ones
6458 using boost::make_tuple;
6459 typedef tuple< SMESHDS_GroupBase*, SMESHDS_Group*, SMESHDS_Group* > TOldNewGroup;
6460 vector< list< TOldNewGroup > > groupsByType( SMDSAbs_NbElementTypes );
6461 vector< TOldNewGroup* > orderedOldNewGroups; // in order of old groups
6463 set< string > groupNames;
6465 SMESH_Mesh::GroupIteratorPtr groupIt = GetMesh()->GetGroups();
6466 if ( !groupIt->more() ) return newGroupIDs;
6468 int newGroupID = mesh->GetGroupIds().back()+1;
6469 while ( groupIt->more() )
6471 SMESH_Group * group = groupIt->next();
6472 if ( !group ) continue;
6473 SMESHDS_GroupBase* groupDS = group->GetGroupDS();
6474 if ( !groupDS || groupDS->IsEmpty() ) continue;
6475 groupNames.insert ( group->GetName() );
6476 groupDS->SetStoreName( group->GetName() );
6477 const SMDSAbs_ElementType type = groupDS->GetType();
6478 SMESHDS_Group* newGroup = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
6479 SMESHDS_Group* newTopGroup = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
6480 groupsByType[ type ].push_back( make_tuple( groupDS, newGroup, newTopGroup ));
6481 orderedOldNewGroups.push_back( & groupsByType[ type ].back() );
6484 // Loop on nodes and elements to add them in new groups
6486 for ( int isNodes = 0; isNodes < 2; ++isNodes )
6488 const SMESH_SequenceOfElemPtr& gens = isNodes ? nodeGens : elemGens;
6489 const SMESH_SequenceOfElemPtr& elems = isNodes ? myLastCreatedNodes : myLastCreatedElems;
6490 if ( gens.Length() != elems.Length() )
6491 throw SALOME_Exception("SMESH_MeshEditor::generateGroups(): invalid args");
6493 // loop on created elements
6494 for (int iElem = 1; iElem <= elems.Length(); ++iElem )
6496 const SMDS_MeshElement* sourceElem = gens( iElem );
6497 if ( !sourceElem ) {
6498 MESSAGE("generateGroups(): NULL source element");
6501 list< TOldNewGroup > & groupsOldNew = groupsByType[ sourceElem->GetType() ];
6502 if ( groupsOldNew.empty() ) { // no groups of this type at all
6503 while ( iElem < gens.Length() && gens( iElem+1 ) == sourceElem )
6504 ++iElem; // skip all elements made by sourceElem
6507 // collect all elements made by the iElem-th sourceElem
6508 list< const SMDS_MeshElement* > resultElems;
6509 if ( const SMDS_MeshElement* resElem = elems( iElem ))
6510 if ( resElem != sourceElem )
6511 resultElems.push_back( resElem );
6512 while ( iElem < gens.Length() && gens( iElem+1 ) == sourceElem )
6513 if ( const SMDS_MeshElement* resElem = elems( ++iElem ))
6514 if ( resElem != sourceElem )
6515 resultElems.push_back( resElem );
6517 // there must be a top element
6518 const SMDS_MeshElement* topElem = 0;
6521 topElem = resultElems.back();
6522 resultElems.pop_back();
6526 list< const SMDS_MeshElement* >::reverse_iterator resElemIt = resultElems.rbegin();
6527 for ( ; resElemIt != resultElems.rend() ; ++resElemIt )
6528 if ( (*resElemIt)->GetType() == sourceElem->GetType() )
6530 topElem = *resElemIt;
6531 resultElems.erase( --(resElemIt.base()) ); // erase *resElemIt
6536 // add resultElems to groups originted from ones the sourceElem belongs to
6537 list< TOldNewGroup >::iterator gOldNew, gLast = groupsOldNew.end();
6538 for ( gOldNew = groupsOldNew.begin(); gOldNew != gLast; ++gOldNew )
6540 SMESHDS_GroupBase* oldGroup = gOldNew->get<0>();
6541 if ( oldGroup->Contains( sourceElem )) // sourceElem is in oldGroup
6543 // fill in a new group
6544 SMDS_MeshGroup & newGroup = gOldNew->get<1>()->SMDSGroup();
6545 list< const SMDS_MeshElement* >::iterator resLast = resultElems.end(), resElemIt;
6546 for ( resElemIt = resultElems.begin(); resElemIt != resLast; ++resElemIt )
6547 newGroup.Add( *resElemIt );
6549 // fill a "top" group
6552 SMDS_MeshGroup & newTopGroup = gOldNew->get<2>()->SMDSGroup();
6553 newTopGroup.Add( topElem );
6557 } // loop on created elements
6558 }// loop on nodes and elements
6560 // Create new SMESH_Groups from SMESHDS_Groups and remove empty SMESHDS_Groups
6562 list<int> topGrouIds;
6563 for ( size_t i = 0; i < orderedOldNewGroups.size(); ++i )
6565 SMESHDS_GroupBase* oldGroupDS = orderedOldNewGroups[i]->get<0>();
6566 SMESHDS_Group* newGroups[2] = { orderedOldNewGroups[i]->get<1>(),
6567 orderedOldNewGroups[i]->get<2>() };
6568 const int nbNewGroups = !newGroups[0]->IsEmpty() + !newGroups[1]->IsEmpty();
6569 for ( int is2nd = 0; is2nd < 2; ++is2nd )
6571 SMESHDS_Group* newGroupDS = newGroups[ is2nd ];
6572 if ( newGroupDS->IsEmpty() )
6574 mesh->GetMeshDS()->RemoveGroup( newGroupDS );
6579 newGroupDS->SetType( newGroupDS->GetElements()->next()->GetType() );
6582 const bool isTop = ( nbNewGroups == 2 &&
6583 newGroupDS->GetType() == oldGroupDS->GetType() &&
6586 string name = oldGroupDS->GetStoreName();
6587 if ( !targetMesh ) {
6588 string suffix = ( isTop ? "top": postfix.c_str() );
6592 while ( !groupNames.insert( name ).second ) // name exists
6593 name = SMESH_Comment( oldGroupDS->GetStoreName() ) << "_" << suffix << "_" << nb++;
6598 newGroupDS->SetStoreName( name.c_str() );
6600 // make a SMESH_Groups
6601 mesh->AddGroup( newGroupDS );
6603 topGrouIds.push_back( newGroupDS->GetID() );
6605 newGroupIDs->push_back( newGroupDS->GetID() );
6609 newGroupIDs->splice( newGroupIDs->end(), topGrouIds );
6614 //================================================================================
6616 * \brief Return list of group of nodes close to each other within theTolerance
6617 * Search among theNodes or in the whole mesh if theNodes is empty using
6618 * an Octree algorithm
6620 //================================================================================
6622 void SMESH_MeshEditor::FindCoincidentNodes (TIDSortedNodeSet & theNodes,
6623 const double theTolerance,
6624 TListOfListOfNodes & theGroupsOfNodes)
6626 myLastCreatedElems.Clear();
6627 myLastCreatedNodes.Clear();
6629 if ( theNodes.empty() )
6630 { // get all nodes in the mesh
6631 SMDS_NodeIteratorPtr nIt = GetMeshDS()->nodesIterator(/*idInceasingOrder=*/true);
6632 while ( nIt->more() )
6633 theNodes.insert( theNodes.end(),nIt->next());
6636 SMESH_OctreeNode::FindCoincidentNodes ( theNodes, &theGroupsOfNodes, theTolerance);
6639 //=======================================================================
6640 //function : SimplifyFace
6642 //=======================================================================
6644 int SMESH_MeshEditor::SimplifyFace (const vector<const SMDS_MeshNode *>& faceNodes,
6645 vector<const SMDS_MeshNode *>& poly_nodes,
6646 vector<int>& quantities) const
6648 int nbNodes = faceNodes.size();
6653 set<const SMDS_MeshNode*> nodeSet;
6655 // get simple seq of nodes
6656 //const SMDS_MeshNode* simpleNodes[ nbNodes ];
6657 vector<const SMDS_MeshNode*> simpleNodes( nbNodes );
6658 int iSimple = 0, nbUnique = 0;
6660 simpleNodes[iSimple++] = faceNodes[0];
6662 for (int iCur = 1; iCur < nbNodes; iCur++) {
6663 if (faceNodes[iCur] != simpleNodes[iSimple - 1]) {
6664 simpleNodes[iSimple++] = faceNodes[iCur];
6665 if (nodeSet.insert( faceNodes[iCur] ).second)
6669 int nbSimple = iSimple;
6670 if (simpleNodes[nbSimple - 1] == simpleNodes[0]) {
6680 bool foundLoop = (nbSimple > nbUnique);
6683 set<const SMDS_MeshNode*> loopSet;
6684 for (iSimple = 0; iSimple < nbSimple && !foundLoop; iSimple++) {
6685 const SMDS_MeshNode* n = simpleNodes[iSimple];
6686 if (!loopSet.insert( n ).second) {
6690 int iC = 0, curLast = iSimple;
6691 for (; iC < curLast; iC++) {
6692 if (simpleNodes[iC] == n) break;
6694 int loopLen = curLast - iC;
6696 // create sub-element
6698 quantities.push_back(loopLen);
6699 for (; iC < curLast; iC++) {
6700 poly_nodes.push_back(simpleNodes[iC]);
6703 // shift the rest nodes (place from the first loop position)
6704 for (iC = curLast + 1; iC < nbSimple; iC++) {
6705 simpleNodes[iC - loopLen] = simpleNodes[iC];
6707 nbSimple -= loopLen;
6710 } // for (iSimple = 0; iSimple < nbSimple; iSimple++)
6711 } // while (foundLoop)
6715 quantities.push_back(iSimple);
6716 for (int i = 0; i < iSimple; i++)
6717 poly_nodes.push_back(simpleNodes[i]);
6723 //=======================================================================
6724 //function : MergeNodes
6725 //purpose : In each group, the cdr of nodes are substituted by the first one
6727 //=======================================================================
6729 void SMESH_MeshEditor::MergeNodes (TListOfListOfNodes & theGroupsOfNodes)
6731 MESSAGE("MergeNodes");
6732 myLastCreatedElems.Clear();
6733 myLastCreatedNodes.Clear();
6735 SMESHDS_Mesh* aMesh = GetMeshDS();
6737 TNodeNodeMap nodeNodeMap; // node to replace - new node
6738 set<const SMDS_MeshElement*> elems; // all elements with changed nodes
6739 list< int > rmElemIds, rmNodeIds;
6741 // Fill nodeNodeMap and elems
6743 TListOfListOfNodes::iterator grIt = theGroupsOfNodes.begin();
6744 for ( ; grIt != theGroupsOfNodes.end(); grIt++ ) {
6745 list<const SMDS_MeshNode*>& nodes = *grIt;
6746 list<const SMDS_MeshNode*>::iterator nIt = nodes.begin();
6747 const SMDS_MeshNode* nToKeep = *nIt;
6748 //MESSAGE("node to keep " << nToKeep->GetID());
6749 for ( ++nIt; nIt != nodes.end(); nIt++ ) {
6750 const SMDS_MeshNode* nToRemove = *nIt;
6751 nodeNodeMap.insert( TNodeNodeMap::value_type( nToRemove, nToKeep ));
6752 if ( nToRemove != nToKeep ) {
6753 //MESSAGE(" node to remove " << nToRemove->GetID());
6754 rmNodeIds.push_back( nToRemove->GetID() );
6755 AddToSameGroups( nToKeep, nToRemove, aMesh );
6756 // set _alwaysComputed to a sub-mesh of VERTEX to enable mesh computing
6757 // after MergeNodes() w/o creating node in place of merged ones.
6758 const SMDS_PositionPtr& pos = nToRemove->GetPosition();
6759 if ( pos && pos->GetTypeOfPosition() == SMDS_TOP_VERTEX )
6760 if ( SMESH_subMesh* sm = myMesh->GetSubMeshContaining( nToRemove->getshapeId() ))
6761 sm->SetIsAlwaysComputed( true );
6764 SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
6765 while ( invElemIt->more() ) {
6766 const SMDS_MeshElement* elem = invElemIt->next();
6771 // Change element nodes or remove an element
6773 set<const SMDS_MeshElement*>::iterator eIt = elems.begin();
6774 for ( ; eIt != elems.end(); eIt++ ) {
6775 const SMDS_MeshElement* elem = *eIt;
6776 //MESSAGE(" ---- inverse elem on node to remove " << elem->GetID());
6777 int nbNodes = elem->NbNodes();
6778 int aShapeId = FindShape( elem );
6780 set<const SMDS_MeshNode*> nodeSet;
6781 vector< const SMDS_MeshNode*> curNodes( nbNodes ), uniqueNodes( nbNodes );
6782 int iUnique = 0, iCur = 0, nbRepl = 0;
6783 vector<int> iRepl( nbNodes );
6785 // get new seq of nodes
6786 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6787 while ( itN->more() ) {
6788 const SMDS_MeshNode* n =
6789 static_cast<const SMDS_MeshNode*>( itN->next() );
6791 TNodeNodeMap::iterator nnIt = nodeNodeMap.find( n );
6792 if ( nnIt != nodeNodeMap.end() ) { // n sticks
6794 // BUG 0020185: begin
6796 bool stopRecur = false;
6797 set<const SMDS_MeshNode*> nodesRecur;
6798 nodesRecur.insert(n);
6799 while (!stopRecur) {
6800 TNodeNodeMap::iterator nnIt_i = nodeNodeMap.find( n );
6801 if ( nnIt_i != nodeNodeMap.end() ) { // n sticks
6802 n = (*nnIt_i).second;
6803 if (!nodesRecur.insert(n).second) {
6804 // error: recursive dependancy
6814 curNodes[ iCur ] = n;
6815 bool isUnique = nodeSet.insert( n ).second;
6817 uniqueNodes[ iUnique++ ] = n;
6819 iRepl[ nbRepl++ ] = iCur;
6823 // Analyse element topology after replacement
6826 int nbUniqueNodes = nodeSet.size();
6827 //MESSAGE("nbNodes nbUniqueNodes " << nbNodes << " " << nbUniqueNodes);
6828 if ( nbNodes != nbUniqueNodes ) { // some nodes stick
6829 // Polygons and Polyhedral volumes
6830 if (elem->IsPoly()) {
6832 if (elem->GetType() == SMDSAbs_Face) {
6834 vector<const SMDS_MeshNode *> face_nodes (nbNodes);
6836 for (; inode < nbNodes; inode++) {
6837 face_nodes[inode] = curNodes[inode];
6840 vector<const SMDS_MeshNode *> polygons_nodes;
6841 vector<int> quantities;
6842 int nbNew = SimplifyFace(face_nodes, polygons_nodes, quantities);
6845 for (int iface = 0; iface < nbNew; iface++) {
6846 int nbNodes = quantities[iface];
6847 vector<const SMDS_MeshNode *> poly_nodes (nbNodes);
6848 for (int ii = 0; ii < nbNodes; ii++, inode++) {
6849 poly_nodes[ii] = polygons_nodes[inode];
6851 SMDS_MeshElement* newElem = aMesh->AddPolygonalFace(poly_nodes);
6852 myLastCreatedElems.Append(newElem);
6854 aMesh->SetMeshElementOnShape(newElem, aShapeId);
6857 MESSAGE("ChangeElementNodes MergeNodes Polygon");
6858 //aMesh->ChangeElementNodes(elem, &polygons_nodes[inode], quantities[nbNew - 1]);
6859 vector<const SMDS_MeshNode *> polynodes(polygons_nodes.begin()+inode,polygons_nodes.end());
6861 if (nbNew > 0) quid = nbNew - 1;
6862 vector<int> newquant(quantities.begin()+quid, quantities.end());
6863 const SMDS_MeshElement* newElem = 0;
6864 newElem = aMesh->AddPolyhedralVolume(polynodes, newquant);
6865 myLastCreatedElems.Append(newElem);
6866 if ( aShapeId && newElem )
6867 aMesh->SetMeshElementOnShape( newElem, aShapeId );
6868 rmElemIds.push_back(elem->GetID());
6871 rmElemIds.push_back(elem->GetID());
6875 else if (elem->GetType() == SMDSAbs_Volume) {
6876 // Polyhedral volume
6877 if (nbUniqueNodes < 4) {
6878 rmElemIds.push_back(elem->GetID());
6881 // each face has to be analyzed in order to check volume validity
6882 const SMDS_VtkVolume* aPolyedre =
6883 dynamic_cast<const SMDS_VtkVolume*>( elem );
6885 int nbFaces = aPolyedre->NbFaces();
6887 vector<const SMDS_MeshNode *> poly_nodes;
6888 vector<int> quantities;
6890 for (int iface = 1; iface <= nbFaces; iface++) {
6891 int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
6892 vector<const SMDS_MeshNode *> faceNodes (nbFaceNodes);
6894 for (int inode = 1; inode <= nbFaceNodes; inode++) {
6895 const SMDS_MeshNode * faceNode = aPolyedre->GetFaceNode(iface, inode);
6896 TNodeNodeMap::iterator nnIt = nodeNodeMap.find(faceNode);
6897 if (nnIt != nodeNodeMap.end()) { // faceNode sticks
6898 faceNode = (*nnIt).second;
6900 faceNodes[inode - 1] = faceNode;
6903 SimplifyFace(faceNodes, poly_nodes, quantities);
6906 if (quantities.size() > 3) {
6907 // to be done: remove coincident faces
6910 if (quantities.size() > 3)
6912 MESSAGE("ChangeElementNodes MergeNodes Polyhedron");
6913 //aMesh->ChangePolyhedronNodes(elem, poly_nodes, quantities);
6914 const SMDS_MeshElement* newElem = 0;
6915 newElem = aMesh->AddPolyhedralVolume(poly_nodes, quantities);
6916 myLastCreatedElems.Append(newElem);
6917 if ( aShapeId && newElem )
6918 aMesh->SetMeshElementOnShape( newElem, aShapeId );
6919 rmElemIds.push_back(elem->GetID());
6923 rmElemIds.push_back(elem->GetID());
6934 // TODO not all the possible cases are solved. Find something more generic?
6935 switch ( nbNodes ) {
6936 case 2: ///////////////////////////////////// EDGE
6937 isOk = false; break;
6938 case 3: ///////////////////////////////////// TRIANGLE
6939 isOk = false; break;
6941 if ( elem->GetType() == SMDSAbs_Volume ) // TETRAHEDRON
6943 else { //////////////////////////////////// QUADRANGLE
6944 if ( nbUniqueNodes < 3 )
6946 else if ( nbRepl == 2 && iRepl[ 1 ] - iRepl[ 0 ] == 2 )
6947 isOk = false; // opposite nodes stick
6948 //MESSAGE("isOk " << isOk);
6951 case 6: ///////////////////////////////////// PENTAHEDRON
6952 if ( nbUniqueNodes == 4 ) {
6953 // ---------------------------------> tetrahedron
6955 iRepl[ 0 ] > 2 && iRepl[ 1 ] > 2 && iRepl[ 2 ] > 2 ) {
6956 // all top nodes stick: reverse a bottom
6957 uniqueNodes[ 0 ] = curNodes [ 1 ];
6958 uniqueNodes[ 1 ] = curNodes [ 0 ];
6960 else if (nbRepl == 3 &&
6961 iRepl[ 0 ] < 3 && iRepl[ 1 ] < 3 && iRepl[ 2 ] < 3 ) {
6962 // all bottom nodes stick: set a top before
6963 uniqueNodes[ 3 ] = uniqueNodes [ 0 ];
6964 uniqueNodes[ 0 ] = curNodes [ 3 ];
6965 uniqueNodes[ 1 ] = curNodes [ 4 ];
6966 uniqueNodes[ 2 ] = curNodes [ 5 ];
6968 else if (nbRepl == 4 &&
6969 iRepl[ 2 ] - iRepl [ 0 ] == 3 && iRepl[ 3 ] - iRepl [ 1 ] == 3 ) {
6970 // a lateral face turns into a line: reverse a bottom
6971 uniqueNodes[ 0 ] = curNodes [ 1 ];
6972 uniqueNodes[ 1 ] = curNodes [ 0 ];
6977 else if ( nbUniqueNodes == 5 ) {
6978 // PENTAHEDRON --------------------> 2 tetrahedrons
6979 if ( nbRepl == 2 && iRepl[ 1 ] - iRepl [ 0 ] == 3 ) {
6980 // a bottom node sticks with a linked top one
6982 SMDS_MeshElement* newElem =
6983 aMesh->AddVolume(curNodes[ 3 ],
6986 curNodes[ iRepl[ 0 ] == 2 ? 1 : 2 ]);
6987 myLastCreatedElems.Append(newElem);
6989 aMesh->SetMeshElementOnShape( newElem, aShapeId );
6990 // 2. : reverse a bottom
6991 uniqueNodes[ 0 ] = curNodes [ 1 ];
6992 uniqueNodes[ 1 ] = curNodes [ 0 ];
7002 if(elem->IsQuadratic()) { // Quadratic quadrangle
7014 MESSAGE("nbRepl=2: " << iRepl[0] << " " << iRepl[1]);
7017 MESSAGE("nbRepl=3: " << iRepl[0] << " " << iRepl[1] << " " << iRepl[2]);
7019 if( iRepl[0]==0 && iRepl[1]==1 && iRepl[2]==4 ) {
7020 uniqueNodes[0] = curNodes[0];
7021 uniqueNodes[1] = curNodes[2];
7022 uniqueNodes[2] = curNodes[3];
7023 uniqueNodes[3] = curNodes[5];
7024 uniqueNodes[4] = curNodes[6];
7025 uniqueNodes[5] = curNodes[7];
7028 if( iRepl[0]==0 && iRepl[1]==3 && iRepl[2]==7 ) {
7029 uniqueNodes[0] = curNodes[0];
7030 uniqueNodes[1] = curNodes[1];
7031 uniqueNodes[2] = curNodes[2];
7032 uniqueNodes[3] = curNodes[4];
7033 uniqueNodes[4] = curNodes[5];
7034 uniqueNodes[5] = curNodes[6];
7037 if( iRepl[0]==0 && iRepl[1]==4 && iRepl[2]==7 ) {
7038 uniqueNodes[0] = curNodes[1];
7039 uniqueNodes[1] = curNodes[2];
7040 uniqueNodes[2] = curNodes[3];
7041 uniqueNodes[3] = curNodes[5];
7042 uniqueNodes[4] = curNodes[6];
7043 uniqueNodes[5] = curNodes[0];
7046 if( iRepl[0]==1 && iRepl[1]==2 && iRepl[2]==5 ) {
7047 uniqueNodes[0] = curNodes[0];
7048 uniqueNodes[1] = curNodes[1];
7049 uniqueNodes[2] = curNodes[3];
7050 uniqueNodes[3] = curNodes[4];
7051 uniqueNodes[4] = curNodes[6];
7052 uniqueNodes[5] = curNodes[7];
7055 if( iRepl[0]==1 && iRepl[1]==4 && iRepl[2]==5 ) {
7056 uniqueNodes[0] = curNodes[0];
7057 uniqueNodes[1] = curNodes[2];
7058 uniqueNodes[2] = curNodes[3];
7059 uniqueNodes[3] = curNodes[1];
7060 uniqueNodes[4] = curNodes[6];
7061 uniqueNodes[5] = curNodes[7];
7064 if( iRepl[0]==2 && iRepl[1]==3 && iRepl[2]==6 ) {
7065 uniqueNodes[0] = curNodes[0];
7066 uniqueNodes[1] = curNodes[1];
7067 uniqueNodes[2] = curNodes[2];
7068 uniqueNodes[3] = curNodes[4];
7069 uniqueNodes[4] = curNodes[5];
7070 uniqueNodes[5] = curNodes[7];
7073 if( iRepl[0]==2 && iRepl[1]==5 && iRepl[2]==6 ) {
7074 uniqueNodes[0] = curNodes[0];
7075 uniqueNodes[1] = curNodes[1];
7076 uniqueNodes[2] = curNodes[3];
7077 uniqueNodes[3] = curNodes[4];
7078 uniqueNodes[4] = curNodes[2];
7079 uniqueNodes[5] = curNodes[7];
7082 if( iRepl[0]==3 && iRepl[1]==6 && iRepl[2]==7 ) {
7083 uniqueNodes[0] = curNodes[0];
7084 uniqueNodes[1] = curNodes[1];
7085 uniqueNodes[2] = curNodes[2];
7086 uniqueNodes[3] = curNodes[4];
7087 uniqueNodes[4] = curNodes[5];
7088 uniqueNodes[5] = curNodes[3];
7093 MESSAGE("nbRepl=4: " << iRepl[0] << " " << iRepl[1] << " " << iRepl[2] << " " << iRepl[3]);
7096 MESSAGE("nbRepl=5: " << iRepl[0] << " " << iRepl[1] << " " << iRepl[2] << " " << iRepl[3] << " " << iRepl[4]);
7100 //////////////////////////////////// HEXAHEDRON
7102 SMDS_VolumeTool hexa (elem);
7103 hexa.SetExternalNormal();
7104 if ( nbUniqueNodes == 4 && nbRepl == 4 ) {
7105 //////////////////////// HEX ---> 1 tetrahedron
7106 for ( int iFace = 0; iFace < 6; iFace++ ) {
7107 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7108 if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7109 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7110 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7111 // one face turns into a point ...
7112 int iOppFace = hexa.GetOppFaceIndex( iFace );
7113 ind = hexa.GetFaceNodesIndices( iOppFace );
7115 for ( iCur = 0; iCur < 4 && nbStick < 2; iCur++ ) {
7116 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7119 if ( nbStick == 1 ) {
7120 // ... and the opposite one - into a triangle.
7122 ind = hexa.GetFaceNodesIndices( iFace );
7123 uniqueNodes[ 3 ] = curNodes[ind[ 0 ]];
7130 else if ( nbUniqueNodes == 6 && nbRepl == 2 ) {
7131 //////////////////////// HEX ---> 1 prism
7132 int nbTria = 0, iTria[3];
7133 const int *ind; // indices of face nodes
7134 // look for triangular faces
7135 for ( int iFace = 0; iFace < 6 && nbTria < 3; iFace++ ) {
7136 ind = hexa.GetFaceNodesIndices( iFace );
7137 TIDSortedNodeSet faceNodes;
7138 for ( iCur = 0; iCur < 4; iCur++ )
7139 faceNodes.insert( curNodes[ind[iCur]] );
7140 if ( faceNodes.size() == 3 )
7141 iTria[ nbTria++ ] = iFace;
7143 // check if triangles are opposite
7144 if ( nbTria == 2 && iTria[0] == hexa.GetOppFaceIndex( iTria[1] ))
7147 // set nodes of the bottom triangle
7148 ind = hexa.GetFaceNodesIndices( iTria[ 0 ]);
7150 for ( iCur = 0; iCur < 4; iCur++ )
7151 if ( ind[iCur] != iRepl[0] && ind[iCur] != iRepl[1])
7152 indB.push_back( ind[iCur] );
7153 if ( !hexa.IsForward() )
7154 std::swap( indB[0], indB[2] );
7155 for ( iCur = 0; iCur < 3; iCur++ )
7156 uniqueNodes[ iCur ] = curNodes[indB[iCur]];
7157 // set nodes of the top triangle
7158 const int *indT = hexa.GetFaceNodesIndices( iTria[ 1 ]);
7159 for ( iCur = 0; iCur < 3; ++iCur )
7160 for ( int j = 0; j < 4; ++j )
7161 if ( hexa.IsLinked( indB[ iCur ], indT[ j ] ))
7163 uniqueNodes[ iCur + 3 ] = curNodes[ indT[ j ]];
7169 else if (nbUniqueNodes == 5 && nbRepl == 4 ) {
7170 //////////////////// HEXAHEDRON ---> 2 tetrahedrons
7171 for ( int iFace = 0; iFace < 6; iFace++ ) {
7172 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7173 if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7174 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7175 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7176 // one face turns into a point ...
7177 int iOppFace = hexa.GetOppFaceIndex( iFace );
7178 ind = hexa.GetFaceNodesIndices( iOppFace );
7180 iUnique = 2; // reverse a tetrahedron 1 bottom
7181 for ( iCur = 0; iCur < 4 && nbStick == 0; iCur++ ) {
7182 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7184 else if ( iUnique >= 0 )
7185 uniqueNodes[ iUnique-- ] = curNodes[ind[ iCur ]];
7187 if ( nbStick == 0 ) {
7188 // ... and the opposite one is a quadrangle
7190 const int* indTop = hexa.GetFaceNodesIndices( iFace );
7191 uniqueNodes[ 3 ] = curNodes[indTop[ 0 ]];
7194 SMDS_MeshElement* newElem =
7195 aMesh->AddVolume(curNodes[ind[ 0 ]],
7198 curNodes[indTop[ 0 ]]);
7199 myLastCreatedElems.Append(newElem);
7201 aMesh->SetMeshElementOnShape( newElem, aShapeId );
7208 else if ( nbUniqueNodes == 6 && nbRepl == 4 ) {
7209 ////////////////// HEXAHEDRON ---> 2 tetrahedrons or 1 prism
7210 // find indices of quad and tri faces
7211 int iQuadFace[ 6 ], iTriFace[ 6 ], nbQuad = 0, nbTri = 0, iFace;
7212 for ( iFace = 0; iFace < 6; iFace++ ) {
7213 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7215 for ( iCur = 0; iCur < 4; iCur++ )
7216 nodeSet.insert( curNodes[ind[ iCur ]] );
7217 nbUniqueNodes = nodeSet.size();
7218 if ( nbUniqueNodes == 3 )
7219 iTriFace[ nbTri++ ] = iFace;
7220 else if ( nbUniqueNodes == 4 )
7221 iQuadFace[ nbQuad++ ] = iFace;
7223 if (nbQuad == 2 && nbTri == 4 &&
7224 hexa.GetOppFaceIndex( iQuadFace[ 0 ] ) == iQuadFace[ 1 ]) {
7225 // 2 opposite quadrangles stuck with a diagonal;
7226 // sample groups of merged indices: (0-4)(2-6)
7227 // --------------------------------------------> 2 tetrahedrons
7228 const int *ind1 = hexa.GetFaceNodesIndices( iQuadFace[ 0 ]); // indices of quad1 nodes
7229 const int *ind2 = hexa.GetFaceNodesIndices( iQuadFace[ 1 ]);
7230 int i0, i1d, i2, i3d, i0t, i2t; // d-daigonal, t-top
7231 if (curNodes[ind1[ 0 ]] == curNodes[ind2[ 0 ]] &&
7232 curNodes[ind1[ 2 ]] == curNodes[ind2[ 2 ]]) {
7233 // stuck with 0-2 diagonal
7241 else if (curNodes[ind1[ 1 ]] == curNodes[ind2[ 3 ]] &&
7242 curNodes[ind1[ 3 ]] == curNodes[ind2[ 1 ]]) {
7243 // stuck with 1-3 diagonal
7255 uniqueNodes[ 0 ] = curNodes [ i0 ];
7256 uniqueNodes[ 1 ] = curNodes [ i1d ];
7257 uniqueNodes[ 2 ] = curNodes [ i3d ];
7258 uniqueNodes[ 3 ] = curNodes [ i0t ];
7261 SMDS_MeshElement* newElem = aMesh->AddVolume(curNodes[ i1d ],
7265 myLastCreatedElems.Append(newElem);
7267 aMesh->SetMeshElementOnShape( newElem, aShapeId );
7270 else if (( nbTri == 2 && nbQuad == 3 ) || // merged (0-4)(1-5)
7271 ( nbTri == 4 && nbQuad == 2 )) { // merged (7-4)(1-5)
7272 // --------------------------------------------> prism
7273 // find 2 opposite triangles
7275 for ( iFace = 0; iFace + 1 < nbTri; iFace++ ) {
7276 if ( hexa.GetOppFaceIndex( iTriFace[ iFace ] ) == iTriFace[ iFace + 1 ]) {
7277 // find indices of kept and replaced nodes
7278 // and fill unique nodes of 2 opposite triangles
7279 const int *ind1 = hexa.GetFaceNodesIndices( iTriFace[ iFace ]);
7280 const int *ind2 = hexa.GetFaceNodesIndices( iTriFace[ iFace + 1 ]);
7281 const SMDS_MeshNode** hexanodes = hexa.GetNodes();
7282 // fill unique nodes
7285 for ( iCur = 0; iCur < 4 && isOk; iCur++ ) {
7286 const SMDS_MeshNode* n = curNodes[ind1[ iCur ]];
7287 const SMDS_MeshNode* nInit = hexanodes[ind1[ iCur ]];
7289 // iCur of a linked node of the opposite face (make normals co-directed):
7290 int iCurOpp = ( iCur == 1 || iCur == 3 ) ? 4 - iCur : iCur;
7291 // check that correspondent corners of triangles are linked
7292 if ( !hexa.IsLinked( ind1[ iCur ], ind2[ iCurOpp ] ))
7295 uniqueNodes[ iUnique ] = n;
7296 uniqueNodes[ iUnique + 3 ] = curNodes[ind2[ iCurOpp ]];
7305 } // if ( nbUniqueNodes == 6 && nbRepl == 4 )
7308 MESSAGE("MergeNodes() removes hexahedron "<< elem);
7315 } // switch ( nbNodes )
7317 } // if ( nbNodes != nbUniqueNodes ) // some nodes stick
7319 if ( isOk ) { // the elem remains valid after sticking nodes
7320 if (elem->IsPoly() && elem->GetType() == SMDSAbs_Volume)
7322 // Change nodes of polyedre
7323 const SMDS_VtkVolume* aPolyedre =
7324 dynamic_cast<const SMDS_VtkVolume*>( elem );
7326 int nbFaces = aPolyedre->NbFaces();
7328 vector<const SMDS_MeshNode *> poly_nodes;
7329 vector<int> quantities (nbFaces);
7331 for (int iface = 1; iface <= nbFaces; iface++) {
7332 int inode, nbFaceNodes = aPolyedre->NbFaceNodes(iface);
7333 quantities[iface - 1] = nbFaceNodes;
7335 for (inode = 1; inode <= nbFaceNodes; inode++) {
7336 const SMDS_MeshNode* curNode = aPolyedre->GetFaceNode(iface, inode);
7338 TNodeNodeMap::iterator nnIt = nodeNodeMap.find( curNode );
7339 if (nnIt != nodeNodeMap.end()) { // curNode sticks
7340 curNode = (*nnIt).second;
7342 poly_nodes.push_back(curNode);
7345 aMesh->ChangePolyhedronNodes( elem, poly_nodes, quantities );
7348 else // replace non-polyhedron elements
7350 const SMDSAbs_ElementType etyp = elem->GetType();
7351 const int elemId = elem->GetID();
7352 const bool isPoly = (elem->GetEntityType() == SMDSEntity_Polygon);
7353 uniqueNodes.resize(nbUniqueNodes);
7355 SMESHDS_SubMesh * sm = aShapeId > 0 ? aMesh->MeshElements(aShapeId) : 0;
7357 aMesh->RemoveFreeElement(elem, sm, /*fromGroups=*/false);
7358 SMDS_MeshElement* newElem = this->AddElement(uniqueNodes, etyp, isPoly, elemId);
7359 if ( sm && newElem )
7360 sm->AddElement( newElem );
7361 if ( elem != newElem )
7362 ReplaceElemInGroups( elem, newElem, aMesh );
7366 // Remove invalid regular element or invalid polygon
7367 rmElemIds.push_back( elem->GetID() );
7370 } // loop on elements
7372 // Remove bad elements, then equal nodes (order important)
7374 Remove( rmElemIds, false );
7375 Remove( rmNodeIds, true );
7380 // ========================================================
7381 // class : SortableElement
7382 // purpose : allow sorting elements basing on their nodes
7383 // ========================================================
7384 class SortableElement : public set <const SMDS_MeshElement*>
7388 SortableElement( const SMDS_MeshElement* theElem )
7391 SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator();
7392 while ( nodeIt->more() )
7393 this->insert( nodeIt->next() );
7396 const SMDS_MeshElement* Get() const
7399 void Set(const SMDS_MeshElement* e) const
7404 mutable const SMDS_MeshElement* myElem;
7407 //=======================================================================
7408 //function : FindEqualElements
7409 //purpose : Return list of group of elements built on the same nodes.
7410 // Search among theElements or in the whole mesh if theElements is empty
7411 //=======================================================================
7413 void SMESH_MeshEditor::FindEqualElements(TIDSortedElemSet & theElements,
7414 TListOfListOfElementsID & theGroupsOfElementsID)
7416 myLastCreatedElems.Clear();
7417 myLastCreatedNodes.Clear();
7419 typedef map< SortableElement, int > TMapOfNodeSet;
7420 typedef list<int> TGroupOfElems;
7422 if ( theElements.empty() )
7423 { // get all elements in the mesh
7424 SMDS_ElemIteratorPtr eIt = GetMeshDS()->elementsIterator();
7425 while ( eIt->more() )
7426 theElements.insert( theElements.end(), eIt->next());
7429 vector< TGroupOfElems > arrayOfGroups;
7430 TGroupOfElems groupOfElems;
7431 TMapOfNodeSet mapOfNodeSet;
7433 TIDSortedElemSet::iterator elemIt = theElements.begin();
7434 for ( int i = 0, j=0; elemIt != theElements.end(); ++elemIt, ++j ) {
7435 const SMDS_MeshElement* curElem = *elemIt;
7436 SortableElement SE(curElem);
7439 pair< TMapOfNodeSet::iterator, bool> pp = mapOfNodeSet.insert(make_pair(SE, i));
7440 if( !(pp.second) ) {
7441 TMapOfNodeSet::iterator& itSE = pp.first;
7442 ind = (*itSE).second;
7443 arrayOfGroups[ind].push_back(curElem->GetID());
7446 groupOfElems.clear();
7447 groupOfElems.push_back(curElem->GetID());
7448 arrayOfGroups.push_back(groupOfElems);
7453 vector< TGroupOfElems >::iterator groupIt = arrayOfGroups.begin();
7454 for ( ; groupIt != arrayOfGroups.end(); ++groupIt ) {
7455 groupOfElems = *groupIt;
7456 if ( groupOfElems.size() > 1 ) {
7457 groupOfElems.sort();
7458 theGroupsOfElementsID.push_back(groupOfElems);
7463 //=======================================================================
7464 //function : MergeElements
7465 //purpose : In each given group, substitute all elements by the first one.
7466 //=======================================================================
7468 void SMESH_MeshEditor::MergeElements(TListOfListOfElementsID & theGroupsOfElementsID)
7470 myLastCreatedElems.Clear();
7471 myLastCreatedNodes.Clear();
7473 typedef list<int> TListOfIDs;
7474 TListOfIDs rmElemIds; // IDs of elems to remove
7476 SMESHDS_Mesh* aMesh = GetMeshDS();
7478 TListOfListOfElementsID::iterator groupsIt = theGroupsOfElementsID.begin();
7479 while ( groupsIt != theGroupsOfElementsID.end() ) {
7480 TListOfIDs& aGroupOfElemID = *groupsIt;
7481 aGroupOfElemID.sort();
7482 int elemIDToKeep = aGroupOfElemID.front();
7483 const SMDS_MeshElement* elemToKeep = aMesh->FindElement(elemIDToKeep);
7484 aGroupOfElemID.pop_front();
7485 TListOfIDs::iterator idIt = aGroupOfElemID.begin();
7486 while ( idIt != aGroupOfElemID.end() ) {
7487 int elemIDToRemove = *idIt;
7488 const SMDS_MeshElement* elemToRemove = aMesh->FindElement(elemIDToRemove);
7489 // add the kept element in groups of removed one (PAL15188)
7490 AddToSameGroups( elemToKeep, elemToRemove, aMesh );
7491 rmElemIds.push_back( elemIDToRemove );
7497 Remove( rmElemIds, false );
7500 //=======================================================================
7501 //function : MergeEqualElements
7502 //purpose : Remove all but one of elements built on the same nodes.
7503 //=======================================================================
7505 void SMESH_MeshEditor::MergeEqualElements()
7507 TIDSortedElemSet aMeshElements; /* empty input ==
7508 to merge equal elements in the whole mesh */
7509 TListOfListOfElementsID aGroupsOfElementsID;
7510 FindEqualElements(aMeshElements, aGroupsOfElementsID);
7511 MergeElements(aGroupsOfElementsID);
7514 //=======================================================================
7515 //function : findAdjacentFace
7517 //=======================================================================
7519 static const SMDS_MeshElement* findAdjacentFace(const SMDS_MeshNode* n1,
7520 const SMDS_MeshNode* n2,
7521 const SMDS_MeshElement* elem)
7523 TIDSortedElemSet elemSet, avoidSet;
7525 avoidSet.insert ( elem );
7526 return SMESH_MeshAlgos::FindFaceInSet( n1, n2, elemSet, avoidSet );
7529 //=======================================================================
7530 //function : FindFreeBorder
7532 //=======================================================================
7534 #define ControlFreeBorder SMESH::Controls::FreeEdges::IsFreeEdge
7536 bool SMESH_MeshEditor::FindFreeBorder (const SMDS_MeshNode* theFirstNode,
7537 const SMDS_MeshNode* theSecondNode,
7538 const SMDS_MeshNode* theLastNode,
7539 list< const SMDS_MeshNode* > & theNodes,
7540 list< const SMDS_MeshElement* >& theFaces)
7542 if ( !theFirstNode || !theSecondNode )
7544 // find border face between theFirstNode and theSecondNode
7545 const SMDS_MeshElement* curElem = findAdjacentFace( theFirstNode, theSecondNode, 0 );
7549 theFaces.push_back( curElem );
7550 theNodes.push_back( theFirstNode );
7551 theNodes.push_back( theSecondNode );
7553 //vector<const SMDS_MeshNode*> nodes;
7554 const SMDS_MeshNode *nIgnore = theFirstNode, *nStart = theSecondNode;
7555 TIDSortedElemSet foundElems;
7556 bool needTheLast = ( theLastNode != 0 );
7558 while ( nStart != theLastNode ) {
7559 if ( nStart == theFirstNode )
7560 return !needTheLast;
7562 // find all free border faces sharing form nStart
7564 list< const SMDS_MeshElement* > curElemList;
7565 list< const SMDS_MeshNode* > nStartList;
7566 SMDS_ElemIteratorPtr invElemIt = nStart->GetInverseElementIterator(SMDSAbs_Face);
7567 while ( invElemIt->more() ) {
7568 const SMDS_MeshElement* e = invElemIt->next();
7569 if ( e == curElem || foundElems.insert( e ).second ) {
7571 int iNode = 0, nbNodes = e->NbNodes();
7572 //const SMDS_MeshNode* nodes[nbNodes+1];
7573 vector<const SMDS_MeshNode*> nodes(nbNodes+1);
7575 if(e->IsQuadratic()) {
7576 const SMDS_VtkFace* F =
7577 dynamic_cast<const SMDS_VtkFace*>(e);
7578 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
7579 // use special nodes iterator
7580 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
7581 while( anIter->more() ) {
7582 nodes[ iNode++ ] = cast2Node(anIter->next());
7586 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
7587 while ( nIt->more() )
7588 nodes[ iNode++ ] = static_cast<const SMDS_MeshNode*>( nIt->next() );
7590 nodes[ iNode ] = nodes[ 0 ];
7592 for ( iNode = 0; iNode < nbNodes; iNode++ )
7593 if (((nodes[ iNode ] == nStart && nodes[ iNode + 1] != nIgnore ) ||
7594 (nodes[ iNode + 1] == nStart && nodes[ iNode ] != nIgnore )) &&
7595 ControlFreeBorder( &nodes[ iNode ], e->GetID() ))
7597 nStartList.push_back( nodes[ iNode + ( nodes[ iNode ] == nStart ? 1 : 0 )]);
7598 curElemList.push_back( e );
7602 // analyse the found
7604 int nbNewBorders = curElemList.size();
7605 if ( nbNewBorders == 0 ) {
7606 // no free border furthermore
7607 return !needTheLast;
7609 else if ( nbNewBorders == 1 ) {
7610 // one more element found
7612 nStart = nStartList.front();
7613 curElem = curElemList.front();
7614 theFaces.push_back( curElem );
7615 theNodes.push_back( nStart );
7618 // several continuations found
7619 list< const SMDS_MeshElement* >::iterator curElemIt;
7620 list< const SMDS_MeshNode* >::iterator nStartIt;
7621 // check if one of them reached the last node
7622 if ( needTheLast ) {
7623 for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
7624 curElemIt!= curElemList.end();
7625 curElemIt++, nStartIt++ )
7626 if ( *nStartIt == theLastNode ) {
7627 theFaces.push_back( *curElemIt );
7628 theNodes.push_back( *nStartIt );
7632 // find the best free border by the continuations
7633 list<const SMDS_MeshNode*> contNodes[ 2 ], *cNL;
7634 list<const SMDS_MeshElement*> contFaces[ 2 ], *cFL;
7635 for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
7636 curElemIt!= curElemList.end();
7637 curElemIt++, nStartIt++ )
7639 cNL = & contNodes[ contNodes[0].empty() ? 0 : 1 ];
7640 cFL = & contFaces[ contFaces[0].empty() ? 0 : 1 ];
7641 // find one more free border
7642 if ( ! SMESH_MeshEditor::FindFreeBorder( nStart, *nStartIt, theLastNode, *cNL, *cFL )) {
7646 else if ( !contNodes[0].empty() && !contNodes[1].empty() ) {
7647 // choice: clear a worse one
7648 int iLongest = ( contNodes[0].size() < contNodes[1].size() ? 1 : 0 );
7649 int iWorse = ( needTheLast ? 1 - iLongest : iLongest );
7650 contNodes[ iWorse ].clear();
7651 contFaces[ iWorse ].clear();
7654 if ( contNodes[0].empty() && contNodes[1].empty() )
7657 // append the best free border
7658 cNL = & contNodes[ contNodes[0].empty() ? 1 : 0 ];
7659 cFL = & contFaces[ contFaces[0].empty() ? 1 : 0 ];
7660 theNodes.pop_back(); // remove nIgnore
7661 theNodes.pop_back(); // remove nStart
7662 theFaces.pop_back(); // remove curElem
7663 list< const SMDS_MeshNode* >::iterator nIt = cNL->begin();
7664 list< const SMDS_MeshElement* >::iterator fIt = cFL->begin();
7665 for ( ; nIt != cNL->end(); nIt++ ) theNodes.push_back( *nIt );
7666 for ( ; fIt != cFL->end(); fIt++ ) theFaces.push_back( *fIt );
7669 } // several continuations found
7670 } // while ( nStart != theLastNode )
7675 //=======================================================================
7676 //function : CheckFreeBorderNodes
7677 //purpose : Return true if the tree nodes are on a free border
7678 //=======================================================================
7680 bool SMESH_MeshEditor::CheckFreeBorderNodes(const SMDS_MeshNode* theNode1,
7681 const SMDS_MeshNode* theNode2,
7682 const SMDS_MeshNode* theNode3)
7684 list< const SMDS_MeshNode* > nodes;
7685 list< const SMDS_MeshElement* > faces;
7686 return FindFreeBorder( theNode1, theNode2, theNode3, nodes, faces);
7689 //=======================================================================
7690 //function : SewFreeBorder
7692 //=======================================================================
7694 SMESH_MeshEditor::Sew_Error
7695 SMESH_MeshEditor::SewFreeBorder (const SMDS_MeshNode* theBordFirstNode,
7696 const SMDS_MeshNode* theBordSecondNode,
7697 const SMDS_MeshNode* theBordLastNode,
7698 const SMDS_MeshNode* theSideFirstNode,
7699 const SMDS_MeshNode* theSideSecondNode,
7700 const SMDS_MeshNode* theSideThirdNode,
7701 const bool theSideIsFreeBorder,
7702 const bool toCreatePolygons,
7703 const bool toCreatePolyedrs)
7705 myLastCreatedElems.Clear();
7706 myLastCreatedNodes.Clear();
7708 MESSAGE("::SewFreeBorder()");
7709 Sew_Error aResult = SEW_OK;
7711 // ====================================
7712 // find side nodes and elements
7713 // ====================================
7715 list< const SMDS_MeshNode* > nSide[ 2 ];
7716 list< const SMDS_MeshElement* > eSide[ 2 ];
7717 list< const SMDS_MeshNode* >::iterator nIt[ 2 ];
7718 list< const SMDS_MeshElement* >::iterator eIt[ 2 ];
7722 if (!FindFreeBorder(theBordFirstNode,theBordSecondNode,theBordLastNode,
7723 nSide[0], eSide[0])) {
7724 MESSAGE(" Free Border 1 not found " );
7725 aResult = SEW_BORDER1_NOT_FOUND;
7727 if (theSideIsFreeBorder) {
7730 if (!FindFreeBorder(theSideFirstNode, theSideSecondNode, theSideThirdNode,
7731 nSide[1], eSide[1])) {
7732 MESSAGE(" Free Border 2 not found " );
7733 aResult = ( aResult != SEW_OK ? SEW_BOTH_BORDERS_NOT_FOUND : SEW_BORDER2_NOT_FOUND );
7736 if ( aResult != SEW_OK )
7739 if (!theSideIsFreeBorder) {
7743 // -------------------------------------------------------------------------
7745 // 1. If nodes to merge are not coincident, move nodes of the free border
7746 // from the coord sys defined by the direction from the first to last
7747 // nodes of the border to the correspondent sys of the side 2
7748 // 2. On the side 2, find the links most co-directed with the correspondent
7749 // links of the free border
7750 // -------------------------------------------------------------------------
7752 // 1. Since sewing may break if there are volumes to split on the side 2,
7753 // we wont move nodes but just compute new coordinates for them
7754 typedef map<const SMDS_MeshNode*, gp_XYZ> TNodeXYZMap;
7755 TNodeXYZMap nBordXYZ;
7756 list< const SMDS_MeshNode* >& bordNodes = nSide[ 0 ];
7757 list< const SMDS_MeshNode* >::iterator nBordIt;
7759 gp_XYZ Pb1( theBordFirstNode->X(), theBordFirstNode->Y(), theBordFirstNode->Z() );
7760 gp_XYZ Pb2( theBordLastNode->X(), theBordLastNode->Y(), theBordLastNode->Z() );
7761 gp_XYZ Ps1( theSideFirstNode->X(), theSideFirstNode->Y(), theSideFirstNode->Z() );
7762 gp_XYZ Ps2( theSideSecondNode->X(), theSideSecondNode->Y(), theSideSecondNode->Z() );
7763 double tol2 = 1.e-8;
7764 gp_Vec Vbs1( Pb1 - Ps1 ),Vbs2( Pb2 - Ps2 );
7765 if ( Vbs1.SquareMagnitude() > tol2 || Vbs2.SquareMagnitude() > tol2 ) {
7766 // Need node movement.
7768 // find X and Z axes to create trsf
7769 gp_Vec Zb( Pb1 - Pb2 ), Zs( Ps1 - Ps2 );
7771 if ( X.SquareMagnitude() <= gp::Resolution() * gp::Resolution() )
7773 X = gp_Ax2( gp::Origin(), Zb ).XDirection();
7776 gp_Ax3 toBordAx( Pb1, Zb, X );
7777 gp_Ax3 fromSideAx( Ps1, Zs, X );
7778 gp_Ax3 toGlobalAx( gp::Origin(), gp::DZ(), gp::DX() );
7780 gp_Trsf toBordSys, fromSide2Sys;
7781 toBordSys.SetTransformation( toBordAx );
7782 fromSide2Sys.SetTransformation( fromSideAx, toGlobalAx );
7783 fromSide2Sys.SetScaleFactor( Zs.Magnitude() / Zb.Magnitude() );
7786 for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
7787 const SMDS_MeshNode* n = *nBordIt;
7788 gp_XYZ xyz( n->X(),n->Y(),n->Z() );
7789 toBordSys.Transforms( xyz );
7790 fromSide2Sys.Transforms( xyz );
7791 nBordXYZ.insert( TNodeXYZMap::value_type( n, xyz ));
7795 // just insert nodes XYZ in the nBordXYZ map
7796 for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
7797 const SMDS_MeshNode* n = *nBordIt;
7798 nBordXYZ.insert( TNodeXYZMap::value_type( n, gp_XYZ( n->X(),n->Y(),n->Z() )));
7802 // 2. On the side 2, find the links most co-directed with the correspondent
7803 // links of the free border
7805 list< const SMDS_MeshElement* >& sideElems = eSide[ 1 ];
7806 list< const SMDS_MeshNode* >& sideNodes = nSide[ 1 ];
7807 sideNodes.push_back( theSideFirstNode );
7809 bool hasVolumes = false;
7810 LinkID_Gen aLinkID_Gen( GetMeshDS() );
7811 set<long> foundSideLinkIDs, checkedLinkIDs;
7812 SMDS_VolumeTool volume;
7813 //const SMDS_MeshNode* faceNodes[ 4 ];
7815 const SMDS_MeshNode* sideNode;
7816 const SMDS_MeshElement* sideElem;
7817 const SMDS_MeshNode* prevSideNode = theSideFirstNode;
7818 const SMDS_MeshNode* prevBordNode = theBordFirstNode;
7819 nBordIt = bordNodes.begin();
7821 // border node position and border link direction to compare with
7822 gp_XYZ bordPos = nBordXYZ[ *nBordIt ];
7823 gp_XYZ bordDir = bordPos - nBordXYZ[ prevBordNode ];
7824 // choose next side node by link direction or by closeness to
7825 // the current border node:
7826 bool searchByDir = ( *nBordIt != theBordLastNode );
7828 // find the next node on the Side 2
7830 double maxDot = -DBL_MAX, minDist = DBL_MAX;
7832 checkedLinkIDs.clear();
7833 gp_XYZ prevXYZ( prevSideNode->X(), prevSideNode->Y(), prevSideNode->Z() );
7835 // loop on inverse elements of current node (prevSideNode) on the Side 2
7836 SMDS_ElemIteratorPtr invElemIt = prevSideNode->GetInverseElementIterator();
7837 while ( invElemIt->more() )
7839 const SMDS_MeshElement* elem = invElemIt->next();
7840 // prepare data for a loop on links coming to prevSideNode, of a face or a volume
7841 int iPrevNode, iNode = 0, nbNodes = elem->NbNodes();
7842 vector< const SMDS_MeshNode* > faceNodes( nbNodes, (const SMDS_MeshNode*)0 );
7843 bool isVolume = volume.Set( elem );
7844 const SMDS_MeshNode** nodes = isVolume ? volume.GetNodes() : & faceNodes[0];
7845 if ( isVolume ) // --volume
7847 else if ( elem->GetType()==SMDSAbs_Face ) { // --face
7848 // retrieve all face nodes and find iPrevNode - an index of the prevSideNode
7849 if(elem->IsQuadratic()) {
7850 const SMDS_VtkFace* F =
7851 dynamic_cast<const SMDS_VtkFace*>(elem);
7852 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
7853 // use special nodes iterator
7854 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
7855 while( anIter->more() ) {
7856 nodes[ iNode ] = cast2Node(anIter->next());
7857 if ( nodes[ iNode++ ] == prevSideNode )
7858 iPrevNode = iNode - 1;
7862 SMDS_ElemIteratorPtr nIt = elem->nodesIterator();
7863 while ( nIt->more() ) {
7864 nodes[ iNode ] = cast2Node( nIt->next() );
7865 if ( nodes[ iNode++ ] == prevSideNode )
7866 iPrevNode = iNode - 1;
7869 // there are 2 links to check
7874 // loop on links, to be precise, on the second node of links
7875 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
7876 const SMDS_MeshNode* n = nodes[ iNode ];
7878 if ( !volume.IsLinked( n, prevSideNode ))
7882 if ( iNode ) // a node before prevSideNode
7883 n = nodes[ iPrevNode == 0 ? elem->NbNodes() - 1 : iPrevNode - 1 ];
7884 else // a node after prevSideNode
7885 n = nodes[ iPrevNode + 1 == elem->NbNodes() ? 0 : iPrevNode + 1 ];
7887 // check if this link was already used
7888 long iLink = aLinkID_Gen.GetLinkID( prevSideNode, n );
7889 bool isJustChecked = !checkedLinkIDs.insert( iLink ).second;
7890 if (!isJustChecked &&
7891 foundSideLinkIDs.find( iLink ) == foundSideLinkIDs.end() )
7893 // test a link geometrically
7894 gp_XYZ nextXYZ ( n->X(), n->Y(), n->Z() );
7895 bool linkIsBetter = false;
7896 double dot = 0.0, dist = 0.0;
7897 if ( searchByDir ) { // choose most co-directed link
7898 dot = bordDir * ( nextXYZ - prevXYZ ).Normalized();
7899 linkIsBetter = ( dot > maxDot );
7901 else { // choose link with the node closest to bordPos
7902 dist = ( nextXYZ - bordPos ).SquareModulus();
7903 linkIsBetter = ( dist < minDist );
7905 if ( linkIsBetter ) {
7914 } // loop on inverse elements of prevSideNode
7917 MESSAGE(" Cant find path by links of the Side 2 ");
7918 return SEW_BAD_SIDE_NODES;
7920 sideNodes.push_back( sideNode );
7921 sideElems.push_back( sideElem );
7922 foundSideLinkIDs.insert ( linkID );
7923 prevSideNode = sideNode;
7925 if ( *nBordIt == theBordLastNode )
7926 searchByDir = false;
7928 // find the next border link to compare with
7929 gp_XYZ sidePos( sideNode->X(), sideNode->Y(), sideNode->Z() );
7930 searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
7931 // move to next border node if sideNode is before forward border node (bordPos)
7932 while ( *nBordIt != theBordLastNode && !searchByDir ) {
7933 prevBordNode = *nBordIt;
7935 bordPos = nBordXYZ[ *nBordIt ];
7936 bordDir = bordPos - nBordXYZ[ prevBordNode ];
7937 searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
7941 while ( sideNode != theSideSecondNode );
7943 if ( hasVolumes && sideNodes.size () != bordNodes.size() && !toCreatePolyedrs) {
7944 MESSAGE("VOLUME SPLITTING IS FORBIDDEN");
7945 return SEW_VOLUMES_TO_SPLIT; // volume splitting is forbidden
7947 } // end nodes search on the side 2
7949 // ============================
7950 // sew the border to the side 2
7951 // ============================
7953 int nbNodes[] = { nSide[0].size(), nSide[1].size() };
7954 int maxNbNodes = Max( nbNodes[0], nbNodes[1] );
7956 TListOfListOfNodes nodeGroupsToMerge;
7957 if ( nbNodes[0] == nbNodes[1] ||
7958 ( theSideIsFreeBorder && !theSideThirdNode)) {
7960 // all nodes are to be merged
7962 for (nIt[0] = nSide[0].begin(), nIt[1] = nSide[1].begin();
7963 nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end();
7964 nIt[0]++, nIt[1]++ )
7966 nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
7967 nodeGroupsToMerge.back().push_back( *nIt[1] ); // to keep
7968 nodeGroupsToMerge.back().push_back( *nIt[0] ); // to remove
7973 // insert new nodes into the border and the side to get equal nb of segments
7975 // get normalized parameters of nodes on the borders
7976 //double param[ 2 ][ maxNbNodes ];
7978 param[0] = new double [ maxNbNodes ];
7979 param[1] = new double [ maxNbNodes ];
7981 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
7982 list< const SMDS_MeshNode* >& nodes = nSide[ iBord ];
7983 list< const SMDS_MeshNode* >::iterator nIt = nodes.begin();
7984 const SMDS_MeshNode* nPrev = *nIt;
7985 double bordLength = 0;
7986 for ( iNode = 0; nIt != nodes.end(); nIt++, iNode++ ) { // loop on border nodes
7987 const SMDS_MeshNode* nCur = *nIt;
7988 gp_XYZ segment (nCur->X() - nPrev->X(),
7989 nCur->Y() - nPrev->Y(),
7990 nCur->Z() - nPrev->Z());
7991 double segmentLen = segment.Modulus();
7992 bordLength += segmentLen;
7993 param[ iBord ][ iNode ] = bordLength;
7996 // normalize within [0,1]
7997 for ( iNode = 0; iNode < nbNodes[ iBord ]; iNode++ ) {
7998 param[ iBord ][ iNode ] /= bordLength;
8002 // loop on border segments
8003 const SMDS_MeshNode *nPrev[ 2 ] = { 0, 0 };
8004 int i[ 2 ] = { 0, 0 };
8005 nIt[0] = nSide[0].begin(); eIt[0] = eSide[0].begin();
8006 nIt[1] = nSide[1].begin(); eIt[1] = eSide[1].begin();
8008 TElemOfNodeListMap insertMap;
8009 TElemOfNodeListMap::iterator insertMapIt;
8011 // key: elem to insert nodes into
8012 // value: 2 nodes to insert between + nodes to be inserted
8014 bool next[ 2 ] = { false, false };
8016 // find min adjacent segment length after sewing
8017 double nextParam = 10., prevParam = 0;
8018 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8019 if ( i[ iBord ] + 1 < nbNodes[ iBord ])
8020 nextParam = Min( nextParam, param[iBord][ i[iBord] + 1 ]);
8021 if ( i[ iBord ] > 0 )
8022 prevParam = Max( prevParam, param[iBord][ i[iBord] - 1 ]);
8024 double minParam = Min( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8025 double maxParam = Max( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8026 double minSegLen = Min( nextParam - minParam, maxParam - prevParam );
8028 // choose to insert or to merge nodes
8029 double du = param[ 1 ][ i[1] ] - param[ 0 ][ i[0] ];
8030 if ( Abs( du ) <= minSegLen * 0.2 ) {
8033 nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8034 const SMDS_MeshNode* n0 = *nIt[0];
8035 const SMDS_MeshNode* n1 = *nIt[1];
8036 nodeGroupsToMerge.back().push_back( n1 );
8037 nodeGroupsToMerge.back().push_back( n0 );
8038 // position of node of the border changes due to merge
8039 param[ 0 ][ i[0] ] += du;
8040 // move n1 for the sake of elem shape evaluation during insertion.
8041 // n1 will be removed by MergeNodes() anyway
8042 const_cast<SMDS_MeshNode*>( n0 )->setXYZ( n1->X(), n1->Y(), n1->Z() );
8043 next[0] = next[1] = true;
8048 int intoBord = ( du < 0 ) ? 0 : 1;
8049 const SMDS_MeshElement* elem = *eIt[ intoBord ];
8050 const SMDS_MeshNode* n1 = nPrev[ intoBord ];
8051 const SMDS_MeshNode* n2 = *nIt[ intoBord ];
8052 const SMDS_MeshNode* nIns = *nIt[ 1 - intoBord ];
8053 if ( intoBord == 1 ) {
8054 // move node of the border to be on a link of elem of the side
8055 gp_XYZ p1 (n1->X(), n1->Y(), n1->Z());
8056 gp_XYZ p2 (n2->X(), n2->Y(), n2->Z());
8057 double ratio = du / ( param[ 1 ][ i[1] ] - param[ 1 ][ i[1]-1 ]);
8058 gp_XYZ p = p2 * ( 1 - ratio ) + p1 * ratio;
8059 GetMeshDS()->MoveNode( nIns, p.X(), p.Y(), p.Z() );
8061 insertMapIt = insertMap.find( elem );
8062 bool notFound = ( insertMapIt == insertMap.end() );
8063 bool otherLink = ( !notFound && (*insertMapIt).second.front() != n1 );
8065 // insert into another link of the same element:
8066 // 1. perform insertion into the other link of the elem
8067 list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8068 const SMDS_MeshNode* n12 = nodeList.front(); nodeList.pop_front();
8069 const SMDS_MeshNode* n22 = nodeList.front(); nodeList.pop_front();
8070 InsertNodesIntoLink( elem, n12, n22, nodeList, toCreatePolygons );
8071 // 2. perform insertion into the link of adjacent faces
8073 const SMDS_MeshElement* adjElem = findAdjacentFace( n12, n22, elem );
8075 InsertNodesIntoLink( adjElem, n12, n22, nodeList, toCreatePolygons );
8079 if (toCreatePolyedrs) {
8080 // perform insertion into the links of adjacent volumes
8081 UpdateVolumes(n12, n22, nodeList);
8083 // 3. find an element appeared on n1 and n2 after the insertion
8084 insertMap.erase( elem );
8085 elem = findAdjacentFace( n1, n2, 0 );
8087 if ( notFound || otherLink ) {
8088 // add element and nodes of the side into the insertMap
8089 insertMapIt = insertMap.insert
8090 ( TElemOfNodeListMap::value_type( elem, list<const SMDS_MeshNode*>() )).first;
8091 (*insertMapIt).second.push_back( n1 );
8092 (*insertMapIt).second.push_back( n2 );
8094 // add node to be inserted into elem
8095 (*insertMapIt).second.push_back( nIns );
8096 next[ 1 - intoBord ] = true;
8099 // go to the next segment
8100 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8101 if ( next[ iBord ] ) {
8102 if ( i[ iBord ] != 0 && eIt[ iBord ] != eSide[ iBord ].end())
8104 nPrev[ iBord ] = *nIt[ iBord ];
8105 nIt[ iBord ]++; i[ iBord ]++;
8109 while ( nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end());
8111 // perform insertion of nodes into elements
8113 for (insertMapIt = insertMap.begin();
8114 insertMapIt != insertMap.end();
8117 const SMDS_MeshElement* elem = (*insertMapIt).first;
8118 list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8119 const SMDS_MeshNode* n1 = nodeList.front(); nodeList.pop_front();
8120 const SMDS_MeshNode* n2 = nodeList.front(); nodeList.pop_front();
8122 InsertNodesIntoLink( elem, n1, n2, nodeList, toCreatePolygons );
8124 if ( !theSideIsFreeBorder ) {
8125 // look for and insert nodes into the faces adjacent to elem
8127 const SMDS_MeshElement* adjElem = findAdjacentFace( n1, n2, elem );
8129 InsertNodesIntoLink( adjElem, n1, n2, nodeList, toCreatePolygons );
8134 if (toCreatePolyedrs) {
8135 // perform insertion into the links of adjacent volumes
8136 UpdateVolumes(n1, n2, nodeList);
8142 } // end: insert new nodes
8144 MergeNodes ( nodeGroupsToMerge );
8149 //=======================================================================
8150 //function : InsertNodesIntoLink
8151 //purpose : insert theNodesToInsert into theFace between theBetweenNode1
8152 // and theBetweenNode2 and split theElement
8153 //=======================================================================
8155 void SMESH_MeshEditor::InsertNodesIntoLink(const SMDS_MeshElement* theFace,
8156 const SMDS_MeshNode* theBetweenNode1,
8157 const SMDS_MeshNode* theBetweenNode2,
8158 list<const SMDS_MeshNode*>& theNodesToInsert,
8159 const bool toCreatePoly)
8161 if ( theFace->GetType() != SMDSAbs_Face ) return;
8163 // find indices of 2 link nodes and of the rest nodes
8164 int iNode = 0, il1, il2, i3, i4;
8165 il1 = il2 = i3 = i4 = -1;
8166 //const SMDS_MeshNode* nodes[ theFace->NbNodes() ];
8167 vector<const SMDS_MeshNode*> nodes( theFace->NbNodes() );
8169 if(theFace->IsQuadratic()) {
8170 const SMDS_VtkFace* F =
8171 dynamic_cast<const SMDS_VtkFace*>(theFace);
8172 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8173 // use special nodes iterator
8174 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8175 while( anIter->more() ) {
8176 const SMDS_MeshNode* n = cast2Node(anIter->next());
8177 if ( n == theBetweenNode1 )
8179 else if ( n == theBetweenNode2 )
8185 nodes[ iNode++ ] = n;
8189 SMDS_ElemIteratorPtr nodeIt = theFace->nodesIterator();
8190 while ( nodeIt->more() ) {
8191 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8192 if ( n == theBetweenNode1 )
8194 else if ( n == theBetweenNode2 )
8200 nodes[ iNode++ ] = n;
8203 if ( il1 < 0 || il2 < 0 || i3 < 0 )
8206 // arrange link nodes to go one after another regarding the face orientation
8207 bool reverse = ( Abs( il2 - il1 ) == 1 ? il2 < il1 : il1 < il2 );
8208 list<const SMDS_MeshNode *> aNodesToInsert = theNodesToInsert;
8213 aNodesToInsert.reverse();
8215 // check that not link nodes of a quadrangles are in good order
8216 int nbFaceNodes = theFace->NbNodes();
8217 if ( nbFaceNodes == 4 && i4 - i3 != 1 ) {
8223 if (toCreatePoly || theFace->IsPoly()) {
8226 vector<const SMDS_MeshNode *> poly_nodes (nbFaceNodes + aNodesToInsert.size());
8228 // add nodes of face up to first node of link
8231 if(theFace->IsQuadratic()) {
8232 const SMDS_VtkFace* F =
8233 dynamic_cast<const SMDS_VtkFace*>(theFace);
8234 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8235 // use special nodes iterator
8236 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8237 while( anIter->more() && !isFLN ) {
8238 const SMDS_MeshNode* n = cast2Node(anIter->next());
8239 poly_nodes[iNode++] = n;
8240 if (n == nodes[il1]) {
8244 // add nodes to insert
8245 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8246 for (; nIt != aNodesToInsert.end(); nIt++) {
8247 poly_nodes[iNode++] = *nIt;
8249 // add nodes of face starting from last node of link
8250 while ( anIter->more() ) {
8251 poly_nodes[iNode++] = cast2Node(anIter->next());
8255 SMDS_ElemIteratorPtr nodeIt = theFace->nodesIterator();
8256 while ( nodeIt->more() && !isFLN ) {
8257 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8258 poly_nodes[iNode++] = n;
8259 if (n == nodes[il1]) {
8263 // add nodes to insert
8264 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8265 for (; nIt != aNodesToInsert.end(); nIt++) {
8266 poly_nodes[iNode++] = *nIt;
8268 // add nodes of face starting from last node of link
8269 while ( nodeIt->more() ) {
8270 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8271 poly_nodes[iNode++] = n;
8275 // edit or replace the face
8276 SMESHDS_Mesh *aMesh = GetMeshDS();
8278 if (theFace->IsPoly()) {
8279 aMesh->ChangePolygonNodes(theFace, poly_nodes);
8282 int aShapeId = FindShape( theFace );
8284 SMDS_MeshElement* newElem = aMesh->AddPolygonalFace(poly_nodes);
8285 myLastCreatedElems.Append(newElem);
8286 if ( aShapeId && newElem )
8287 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8289 aMesh->RemoveElement(theFace);
8294 SMESHDS_Mesh *aMesh = GetMeshDS();
8295 if( !theFace->IsQuadratic() ) {
8297 // put aNodesToInsert between theBetweenNode1 and theBetweenNode2
8298 int nbLinkNodes = 2 + aNodesToInsert.size();
8299 //const SMDS_MeshNode* linkNodes[ nbLinkNodes ];
8300 vector<const SMDS_MeshNode*> linkNodes( nbLinkNodes );
8301 linkNodes[ 0 ] = nodes[ il1 ];
8302 linkNodes[ nbLinkNodes - 1 ] = nodes[ il2 ];
8303 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8304 for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
8305 linkNodes[ iNode++ ] = *nIt;
8307 // decide how to split a quadrangle: compare possible variants
8308 // and choose which of splits to be a quadrangle
8309 int i1, i2, iSplit, nbSplits = nbLinkNodes - 1, iBestQuad;
8310 if ( nbFaceNodes == 3 ) {
8311 iBestQuad = nbSplits;
8314 else if ( nbFaceNodes == 4 ) {
8315 SMESH::Controls::NumericalFunctorPtr aCrit( new SMESH::Controls::AspectRatio);
8316 double aBestRate = DBL_MAX;
8317 for ( int iQuad = 0; iQuad < nbSplits; iQuad++ ) {
8319 double aBadRate = 0;
8320 // evaluate elements quality
8321 for ( iSplit = 0; iSplit < nbSplits; iSplit++ ) {
8322 if ( iSplit == iQuad ) {
8323 SMDS_FaceOfNodes quad (linkNodes[ i1++ ],
8327 aBadRate += getBadRate( &quad, aCrit );
8330 SMDS_FaceOfNodes tria (linkNodes[ i1++ ],
8332 nodes[ iSplit < iQuad ? i4 : i3 ]);
8333 aBadRate += getBadRate( &tria, aCrit );
8337 if ( aBadRate < aBestRate ) {
8339 aBestRate = aBadRate;
8344 // create new elements
8345 int aShapeId = FindShape( theFace );
8348 for ( iSplit = 0; iSplit < nbSplits - 1; iSplit++ ) {
8349 SMDS_MeshElement* newElem = 0;
8350 if ( iSplit == iBestQuad )
8351 newElem = aMesh->AddFace (linkNodes[ i1++ ],
8356 newElem = aMesh->AddFace (linkNodes[ i1++ ],
8358 nodes[ iSplit < iBestQuad ? i4 : i3 ]);
8359 myLastCreatedElems.Append(newElem);
8360 if ( aShapeId && newElem )
8361 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8364 // change nodes of theFace
8365 const SMDS_MeshNode* newNodes[ 4 ];
8366 newNodes[ 0 ] = linkNodes[ i1 ];
8367 newNodes[ 1 ] = linkNodes[ i2 ];
8368 newNodes[ 2 ] = nodes[ iSplit >= iBestQuad ? i3 : i4 ];
8369 newNodes[ 3 ] = nodes[ i4 ];
8370 //aMesh->ChangeElementNodes( theFace, newNodes, iSplit == iBestQuad ? 4 : 3 );
8371 const SMDS_MeshElement* newElem = 0;
8372 if (iSplit == iBestQuad)
8373 newElem = aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2], newNodes[3] );
8375 newElem = aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2] );
8376 myLastCreatedElems.Append(newElem);
8377 if ( aShapeId && newElem )
8378 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8379 } // end if(!theFace->IsQuadratic())
8380 else { // theFace is quadratic
8381 // we have to split theFace on simple triangles and one simple quadrangle
8383 int nbshift = tmp*2;
8384 // shift nodes in nodes[] by nbshift
8386 for(i=0; i<nbshift; i++) {
8387 const SMDS_MeshNode* n = nodes[0];
8388 for(j=0; j<nbFaceNodes-1; j++) {
8389 nodes[j] = nodes[j+1];
8391 nodes[nbFaceNodes-1] = n;
8393 il1 = il1 - nbshift;
8394 // now have to insert nodes between n0 and n1 or n1 and n2 (see below)
8395 // n0 n1 n2 n0 n1 n2
8396 // +-----+-----+ +-----+-----+
8405 // create new elements
8406 int aShapeId = FindShape( theFace );
8409 if(nbFaceNodes==6) { // quadratic triangle
8410 SMDS_MeshElement* newElem =
8411 aMesh->AddFace(nodes[3],nodes[4],nodes[5]);
8412 myLastCreatedElems.Append(newElem);
8413 if ( aShapeId && newElem )
8414 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8415 if(theFace->IsMediumNode(nodes[il1])) {
8416 // create quadrangle
8417 newElem = aMesh->AddFace(nodes[0],nodes[1],nodes[3],nodes[5]);
8418 myLastCreatedElems.Append(newElem);
8419 if ( aShapeId && newElem )
8420 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8426 // create quadrangle
8427 newElem = aMesh->AddFace(nodes[1],nodes[2],nodes[3],nodes[5]);
8428 myLastCreatedElems.Append(newElem);
8429 if ( aShapeId && newElem )
8430 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8436 else { // nbFaceNodes==8 - quadratic quadrangle
8437 SMDS_MeshElement* newElem =
8438 aMesh->AddFace(nodes[3],nodes[4],nodes[5]);
8439 myLastCreatedElems.Append(newElem);
8440 if ( aShapeId && newElem )
8441 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8442 newElem = aMesh->AddFace(nodes[5],nodes[6],nodes[7]);
8443 myLastCreatedElems.Append(newElem);
8444 if ( aShapeId && newElem )
8445 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8446 newElem = aMesh->AddFace(nodes[5],nodes[7],nodes[3]);
8447 myLastCreatedElems.Append(newElem);
8448 if ( aShapeId && newElem )
8449 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8450 if(theFace->IsMediumNode(nodes[il1])) {
8451 // create quadrangle
8452 newElem = aMesh->AddFace(nodes[0],nodes[1],nodes[3],nodes[7]);
8453 myLastCreatedElems.Append(newElem);
8454 if ( aShapeId && newElem )
8455 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8461 // create quadrangle
8462 newElem = aMesh->AddFace(nodes[1],nodes[2],nodes[3],nodes[7]);
8463 myLastCreatedElems.Append(newElem);
8464 if ( aShapeId && newElem )
8465 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8471 // create needed triangles using n1,n2,n3 and inserted nodes
8472 int nbn = 2 + aNodesToInsert.size();
8473 //const SMDS_MeshNode* aNodes[nbn];
8474 vector<const SMDS_MeshNode*> aNodes(nbn);
8475 aNodes[0] = nodes[n1];
8476 aNodes[nbn-1] = nodes[n2];
8477 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8478 for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
8479 aNodes[iNode++] = *nIt;
8481 for(i=1; i<nbn; i++) {
8482 SMDS_MeshElement* newElem =
8483 aMesh->AddFace(aNodes[i-1],aNodes[i],nodes[n3]);
8484 myLastCreatedElems.Append(newElem);
8485 if ( aShapeId && newElem )
8486 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8490 aMesh->RemoveElement(theFace);
8493 //=======================================================================
8494 //function : UpdateVolumes
8496 //=======================================================================
8497 void SMESH_MeshEditor::UpdateVolumes (const SMDS_MeshNode* theBetweenNode1,
8498 const SMDS_MeshNode* theBetweenNode2,
8499 list<const SMDS_MeshNode*>& theNodesToInsert)
8501 myLastCreatedElems.Clear();
8502 myLastCreatedNodes.Clear();
8504 SMDS_ElemIteratorPtr invElemIt = theBetweenNode1->GetInverseElementIterator(SMDSAbs_Volume);
8505 while (invElemIt->more()) { // loop on inverse elements of theBetweenNode1
8506 const SMDS_MeshElement* elem = invElemIt->next();
8508 // check, if current volume has link theBetweenNode1 - theBetweenNode2
8509 SMDS_VolumeTool aVolume (elem);
8510 if (!aVolume.IsLinked(theBetweenNode1, theBetweenNode2))
8513 // insert new nodes in all faces of the volume, sharing link theBetweenNode1 - theBetweenNode2
8514 int iface, nbFaces = aVolume.NbFaces();
8515 vector<const SMDS_MeshNode *> poly_nodes;
8516 vector<int> quantities (nbFaces);
8518 for (iface = 0; iface < nbFaces; iface++) {
8519 int nbFaceNodes = aVolume.NbFaceNodes(iface), nbInserted = 0;
8520 // faceNodes will contain (nbFaceNodes + 1) nodes, last = first
8521 const SMDS_MeshNode** faceNodes = aVolume.GetFaceNodes(iface);
8523 for (int inode = 0; inode < nbFaceNodes; inode++) {
8524 poly_nodes.push_back(faceNodes[inode]);
8526 if (nbInserted == 0) {
8527 if (faceNodes[inode] == theBetweenNode1) {
8528 if (faceNodes[inode + 1] == theBetweenNode2) {
8529 nbInserted = theNodesToInsert.size();
8531 // add nodes to insert
8532 list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.begin();
8533 for (; nIt != theNodesToInsert.end(); nIt++) {
8534 poly_nodes.push_back(*nIt);
8538 else if (faceNodes[inode] == theBetweenNode2) {
8539 if (faceNodes[inode + 1] == theBetweenNode1) {
8540 nbInserted = theNodesToInsert.size();
8542 // add nodes to insert in reversed order
8543 list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.end();
8545 for (; nIt != theNodesToInsert.begin(); nIt--) {
8546 poly_nodes.push_back(*nIt);
8548 poly_nodes.push_back(*nIt);
8555 quantities[iface] = nbFaceNodes + nbInserted;
8558 // Replace or update the volume
8559 SMESHDS_Mesh *aMesh = GetMeshDS();
8561 if (elem->IsPoly()) {
8562 aMesh->ChangePolyhedronNodes(elem, poly_nodes, quantities);
8566 int aShapeId = FindShape( elem );
8568 SMDS_MeshElement* newElem =
8569 aMesh->AddPolyhedralVolume(poly_nodes, quantities);
8570 myLastCreatedElems.Append(newElem);
8571 if (aShapeId && newElem)
8572 aMesh->SetMeshElementOnShape(newElem, aShapeId);
8574 aMesh->RemoveElement(elem);
8581 //================================================================================
8583 * \brief Transform any volume into data of SMDSEntity_Polyhedra
8585 //================================================================================
8587 void volumeToPolyhedron( const SMDS_MeshElement* elem,
8588 vector<const SMDS_MeshNode *> & nodes,
8589 vector<int> & nbNodeInFaces )
8592 nbNodeInFaces.clear();
8593 SMDS_VolumeTool vTool ( elem );
8594 for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
8596 const SMDS_MeshNode** fNodes = vTool.GetFaceNodes( iF );
8597 nodes.insert( nodes.end(), fNodes, fNodes + vTool.NbFaceNodes( iF ));
8598 nbNodeInFaces.push_back( vTool.NbFaceNodes( iF ));
8603 //=======================================================================
8605 * \brief Convert elements contained in a submesh to quadratic
8606 * \return int - nb of checked elements
8608 //=======================================================================
8610 int SMESH_MeshEditor::convertElemToQuadratic(SMESHDS_SubMesh * theSm,
8611 SMESH_MesherHelper& theHelper,
8612 const bool theForce3d)
8615 if( !theSm ) return nbElem;
8617 vector<int> nbNodeInFaces;
8618 vector<const SMDS_MeshNode *> nodes;
8619 SMDS_ElemIteratorPtr ElemItr = theSm->GetElements();
8620 while(ElemItr->more())
8623 const SMDS_MeshElement* elem = ElemItr->next();
8624 if( !elem ) continue;
8626 // analyse a necessity of conversion
8627 const SMDSAbs_ElementType aType = elem->GetType();
8628 if ( aType < SMDSAbs_Edge || aType > SMDSAbs_Volume )
8630 const SMDSAbs_EntityType aGeomType = elem->GetEntityType();
8631 bool hasCentralNodes = false;
8632 if ( elem->IsQuadratic() )
8635 switch ( aGeomType ) {
8636 case SMDSEntity_Quad_Triangle:
8637 case SMDSEntity_Quad_Quadrangle:
8638 case SMDSEntity_Quad_Hexa:
8639 alreadyOK = !theHelper.GetIsBiQuadratic(); break;
8641 case SMDSEntity_BiQuad_Triangle:
8642 case SMDSEntity_BiQuad_Quadrangle:
8643 case SMDSEntity_TriQuad_Hexa:
8644 alreadyOK = theHelper.GetIsBiQuadratic();
8645 hasCentralNodes = true;
8650 // take into account already present modium nodes
8652 case SMDSAbs_Volume:
8653 theHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( elem )); break;
8655 theHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( elem )); break;
8657 theHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( elem )); break;
8663 // get elem data needed to re-create it
8665 const int id = elem->GetID();
8666 const int nbNodes = elem->NbCornerNodes();
8667 nodes.assign(elem->begin_nodes(), elem->end_nodes());
8668 if ( aGeomType == SMDSEntity_Polyhedra )
8669 nbNodeInFaces = static_cast<const SMDS_VtkVolume* >( elem )->GetQuantities();
8670 else if ( aGeomType == SMDSEntity_Hexagonal_Prism )
8671 volumeToPolyhedron( elem, nodes, nbNodeInFaces );
8673 // remove a linear element
8674 GetMeshDS()->RemoveFreeElement(elem, theSm, /*fromGroups=*/false);
8676 // remove central nodes of biquadratic elements (biquad->quad convertion)
8677 if ( hasCentralNodes )
8678 for ( size_t i = nbNodes * 2; i < nodes.size(); ++i )
8679 if ( nodes[i]->NbInverseElements() == 0 )
8680 GetMeshDS()->RemoveFreeNode( nodes[i], theSm, /*fromGroups=*/true );
8682 const SMDS_MeshElement* NewElem = 0;
8688 NewElem = theHelper.AddEdge(nodes[0], nodes[1], id, theForce3d);
8696 NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
8699 NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
8702 NewElem = theHelper.AddPolygonalFace(nodes, id, theForce3d);
8706 case SMDSAbs_Volume :
8710 case SMDSEntity_Tetra:
8711 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
8713 case SMDSEntity_Pyramid:
8714 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], id, theForce3d);
8716 case SMDSEntity_Penta:
8717 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], nodes[5], id, theForce3d);
8719 case SMDSEntity_Hexa:
8720 case SMDSEntity_Quad_Hexa:
8721 case SMDSEntity_TriQuad_Hexa:
8722 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
8723 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
8725 case SMDSEntity_Hexagonal_Prism:
8727 NewElem = theHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
8734 ReplaceElemInGroups( elem, NewElem, GetMeshDS());
8735 if( NewElem && NewElem->getshapeId() < 1 )
8736 theSm->AddElement( NewElem );
8740 //=======================================================================
8741 //function : ConvertToQuadratic
8743 //=======================================================================
8745 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d, const bool theToBiQuad)
8747 SMESHDS_Mesh* meshDS = GetMeshDS();
8749 SMESH_MesherHelper aHelper(*myMesh);
8751 aHelper.SetIsQuadratic( true );
8752 aHelper.SetIsBiQuadratic( theToBiQuad );
8753 aHelper.SetElementsOnShape(true);
8754 aHelper.ToFixNodeParameters( true );
8756 // convert elements assigned to sub-meshes
8757 int nbCheckedElems = 0;
8758 if ( myMesh->HasShapeToMesh() )
8760 if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
8762 SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
8763 while ( smIt->more() ) {
8764 SMESH_subMesh* sm = smIt->next();
8765 if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() ) {
8766 aHelper.SetSubShape( sm->GetSubShape() );
8767 nbCheckedElems += convertElemToQuadratic(smDS, aHelper, theForce3d);
8773 // convert elements NOT assigned to sub-meshes
8774 int totalNbElems = meshDS->NbEdges() + meshDS->NbFaces() + meshDS->NbVolumes();
8775 if ( nbCheckedElems < totalNbElems ) // not all elements are in sub-meshes
8777 aHelper.SetElementsOnShape(false);
8778 SMESHDS_SubMesh *smDS = 0;
8781 SMDS_EdgeIteratorPtr aEdgeItr = meshDS->edgesIterator();
8782 while( aEdgeItr->more() )
8784 const SMDS_MeshEdge* edge = aEdgeItr->next();
8785 if ( !edge->IsQuadratic() )
8787 int id = edge->GetID();
8788 const SMDS_MeshNode* n1 = edge->GetNode(0);
8789 const SMDS_MeshNode* n2 = edge->GetNode(1);
8791 meshDS->RemoveFreeElement(edge, smDS, /*fromGroups=*/false);
8793 const SMDS_MeshEdge* NewEdge = aHelper.AddEdge(n1, n2, id, theForce3d);
8794 ReplaceElemInGroups( edge, NewEdge, GetMeshDS());
8798 aHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( edge ));
8803 SMDS_FaceIteratorPtr aFaceItr = meshDS->facesIterator();
8804 while( aFaceItr->more() )
8806 const SMDS_MeshFace* face = aFaceItr->next();
8807 if ( !face ) continue;
8809 const SMDSAbs_EntityType type = face->GetEntityType();
8813 case SMDSEntity_Quad_Triangle:
8814 case SMDSEntity_Quad_Quadrangle:
8815 alreadyOK = !theToBiQuad;
8816 aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
8818 case SMDSEntity_BiQuad_Triangle:
8819 case SMDSEntity_BiQuad_Quadrangle:
8820 alreadyOK = theToBiQuad;
8821 aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
8823 default: alreadyOK = false;
8828 const int id = face->GetID();
8829 vector<const SMDS_MeshNode *> nodes ( face->begin_nodes(), face->end_nodes());
8831 meshDS->RemoveFreeElement(face, smDS, /*fromGroups=*/false);
8833 SMDS_MeshFace * NewFace = 0;
8836 case SMDSEntity_Triangle:
8837 case SMDSEntity_Quad_Triangle:
8838 case SMDSEntity_BiQuad_Triangle:
8839 NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
8840 if ( nodes.size() == 7 && nodes[6]->NbInverseElements() == 0 ) // rm a central node
8841 GetMeshDS()->RemoveFreeNode( nodes[6], /*sm=*/0, /*fromGroups=*/true );
8844 case SMDSEntity_Quadrangle:
8845 case SMDSEntity_Quad_Quadrangle:
8846 case SMDSEntity_BiQuad_Quadrangle:
8847 NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
8848 if ( nodes.size() == 9 && nodes[8]->NbInverseElements() == 0 ) // rm a central node
8849 GetMeshDS()->RemoveFreeNode( nodes[8], /*sm=*/0, /*fromGroups=*/true );
8853 NewFace = aHelper.AddPolygonalFace(nodes, id, theForce3d);
8855 ReplaceElemInGroups( face, NewFace, GetMeshDS());
8859 vector<int> nbNodeInFaces;
8860 SMDS_VolumeIteratorPtr aVolumeItr = meshDS->volumesIterator();
8861 while(aVolumeItr->more())
8863 const SMDS_MeshVolume* volume = aVolumeItr->next();
8864 if ( !volume ) continue;
8866 const SMDSAbs_EntityType type = volume->GetEntityType();
8867 if ( volume->IsQuadratic() )
8872 case SMDSEntity_Quad_Hexa: alreadyOK = !theToBiQuad; break;
8873 case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; break;
8874 default: alreadyOK = true;
8878 aHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( volume ));
8882 const int id = volume->GetID();
8883 vector<const SMDS_MeshNode *> nodes (volume->begin_nodes(), volume->end_nodes());
8884 if ( type == SMDSEntity_Polyhedra )
8885 nbNodeInFaces = static_cast<const SMDS_VtkVolume* >(volume)->GetQuantities();
8886 else if ( type == SMDSEntity_Hexagonal_Prism )
8887 volumeToPolyhedron( volume, nodes, nbNodeInFaces );
8889 meshDS->RemoveFreeElement(volume, smDS, /*fromGroups=*/false);
8891 SMDS_MeshVolume * NewVolume = 0;
8894 case SMDSEntity_Tetra:
8895 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d );
8897 case SMDSEntity_Hexa:
8898 case SMDSEntity_Quad_Hexa:
8899 case SMDSEntity_TriQuad_Hexa:
8900 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
8901 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
8902 for ( size_t i = 20; i < nodes.size(); ++i ) // rm central nodes
8903 if ( nodes[i]->NbInverseElements() == 0 )
8904 GetMeshDS()->RemoveFreeNode( nodes[i], /*sm=*/0, /*fromGroups=*/true );
8906 case SMDSEntity_Pyramid:
8907 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
8908 nodes[3], nodes[4], id, theForce3d);
8910 case SMDSEntity_Penta:
8911 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
8912 nodes[3], nodes[4], nodes[5], id, theForce3d);
8914 case SMDSEntity_Hexagonal_Prism:
8916 NewVolume = aHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
8918 ReplaceElemInGroups(volume, NewVolume, meshDS);
8923 { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
8924 // aHelper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
8925 // aHelper.FixQuadraticElements(myError);
8926 SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
8930 //================================================================================
8932 * \brief Makes given elements quadratic
8933 * \param theForce3d - if true, the medium nodes will be placed in the middle of link
8934 * \param theElements - elements to make quadratic
8936 //================================================================================
8938 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d,
8939 TIDSortedElemSet& theElements,
8940 const bool theToBiQuad)
8942 if ( theElements.empty() ) return;
8944 // we believe that all theElements are of the same type
8945 const SMDSAbs_ElementType elemType = (*theElements.begin())->GetType();
8947 // get all nodes shared by theElements
8948 TIDSortedNodeSet allNodes;
8949 TIDSortedElemSet::iterator eIt = theElements.begin();
8950 for ( ; eIt != theElements.end(); ++eIt )
8951 allNodes.insert( (*eIt)->begin_nodes(), (*eIt)->end_nodes() );
8953 // complete theElements with elements of lower dim whose all nodes are in allNodes
8955 TIDSortedElemSet quadAdjacentElems [ SMDSAbs_NbElementTypes ]; // quadratic adjacent elements
8956 TIDSortedElemSet checkedAdjacentElems [ SMDSAbs_NbElementTypes ];
8957 TIDSortedNodeSet::iterator nIt = allNodes.begin();
8958 for ( ; nIt != allNodes.end(); ++nIt )
8960 const SMDS_MeshNode* n = *nIt;
8961 SMDS_ElemIteratorPtr invIt = n->GetInverseElementIterator();
8962 while ( invIt->more() )
8964 const SMDS_MeshElement* e = invIt->next();
8965 const SMDSAbs_ElementType type = e->GetType();
8966 if ( e->IsQuadratic() )
8968 quadAdjacentElems[ type ].insert( e );
8971 switch ( e->GetEntityType() ) {
8972 case SMDSEntity_Quad_Triangle:
8973 case SMDSEntity_Quad_Quadrangle:
8974 case SMDSEntity_Quad_Hexa: alreadyOK = !theToBiQuad; break;
8975 case SMDSEntity_BiQuad_Triangle:
8976 case SMDSEntity_BiQuad_Quadrangle:
8977 case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; break;
8978 default: alreadyOK = true;
8983 if ( type >= elemType )
8984 continue; // same type or more complex linear element
8986 if ( !checkedAdjacentElems[ type ].insert( e ).second )
8987 continue; // e is already checked
8991 SMDS_NodeIteratorPtr nodeIt = e->nodeIterator();
8992 while ( nodeIt->more() && allIn )
8993 allIn = allNodes.count( nodeIt->next() );
8995 theElements.insert(e );
8999 SMESH_MesherHelper helper(*myMesh);
9000 helper.SetIsQuadratic( true );
9001 helper.SetIsBiQuadratic( theToBiQuad );
9003 // add links of quadratic adjacent elements to the helper
9005 if ( !quadAdjacentElems[SMDSAbs_Edge].empty() )
9006 for ( eIt = quadAdjacentElems[SMDSAbs_Edge].begin();
9007 eIt != quadAdjacentElems[SMDSAbs_Edge].end(); ++eIt )
9009 helper.AddTLinks( static_cast< const SMDS_MeshEdge*> (*eIt) );
9011 if ( !quadAdjacentElems[SMDSAbs_Face].empty() )
9012 for ( eIt = quadAdjacentElems[SMDSAbs_Face].begin();
9013 eIt != quadAdjacentElems[SMDSAbs_Face].end(); ++eIt )
9015 helper.AddTLinks( static_cast< const SMDS_MeshFace*> (*eIt) );
9017 if ( !quadAdjacentElems[SMDSAbs_Volume].empty() )
9018 for ( eIt = quadAdjacentElems[SMDSAbs_Volume].begin();
9019 eIt != quadAdjacentElems[SMDSAbs_Volume].end(); ++eIt )
9021 helper.AddTLinks( static_cast< const SMDS_MeshVolume*> (*eIt) );
9024 // make quadratic (or bi-tri-quadratic) elements instead of linear ones
9026 SMESHDS_Mesh* meshDS = GetMeshDS();
9027 SMESHDS_SubMesh* smDS = 0;
9028 for ( eIt = theElements.begin(); eIt != theElements.end(); ++eIt )
9030 const SMDS_MeshElement* elem = *eIt;
9033 int nbCentralNodes = 0;
9034 switch ( elem->GetEntityType() ) {
9035 // linear convertible
9036 case SMDSEntity_Edge:
9037 case SMDSEntity_Triangle:
9038 case SMDSEntity_Quadrangle:
9039 case SMDSEntity_Tetra:
9040 case SMDSEntity_Pyramid:
9041 case SMDSEntity_Hexa:
9042 case SMDSEntity_Penta: alreadyOK = false; nbCentralNodes = 0; break;
9043 // quadratic that can become bi-quadratic
9044 case SMDSEntity_Quad_Triangle:
9045 case SMDSEntity_Quad_Quadrangle:
9046 case SMDSEntity_Quad_Hexa: alreadyOK =!theToBiQuad; nbCentralNodes = 0; break;
9048 case SMDSEntity_BiQuad_Triangle:
9049 case SMDSEntity_BiQuad_Quadrangle: alreadyOK = theToBiQuad; nbCentralNodes = 1; break;
9050 case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; nbCentralNodes = 7; break;
9052 default: alreadyOK = true;
9054 if ( alreadyOK ) continue;
9056 const SMDSAbs_ElementType type = elem->GetType();
9057 const int id = elem->GetID();
9058 const int nbNodes = elem->NbCornerNodes();
9059 vector<const SMDS_MeshNode *> nodes ( elem->begin_nodes(), elem->end_nodes());
9061 helper.SetSubShape( elem->getshapeId() );
9063 if ( !smDS || !smDS->Contains( elem ))
9064 smDS = meshDS->MeshElements( elem->getshapeId() );
9065 meshDS->RemoveFreeElement(elem, smDS, /*fromGroups=*/false);
9067 SMDS_MeshElement * newElem = 0;
9070 case 4: // cases for most frequently used element types go first (for optimization)
9071 if ( type == SMDSAbs_Volume )
9072 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9074 newElem = helper.AddFace (nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9077 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9078 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9081 newElem = helper.AddFace (nodes[0], nodes[1], nodes[2], id, theForce3d);
9084 newElem = helper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9087 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9088 nodes[4], id, theForce3d);
9091 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9092 nodes[4], nodes[5], id, theForce3d);
9096 ReplaceElemInGroups( elem, newElem, meshDS);
9097 if( newElem && smDS )
9098 smDS->AddElement( newElem );
9100 // remove central nodes
9101 for ( size_t i = nodes.size() - nbCentralNodes; i < nodes.size(); ++i )
9102 if ( nodes[i]->NbInverseElements() == 0 )
9103 meshDS->RemoveFreeNode( nodes[i], smDS, /*fromGroups=*/true );
9105 } // loop on theElements
9108 { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9109 // helper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9110 // helper.FixQuadraticElements( myError );
9111 SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
9115 //=======================================================================
9117 * \brief Convert quadratic elements to linear ones and remove quadratic nodes
9118 * \return int - nb of checked elements
9120 //=======================================================================
9122 int SMESH_MeshEditor::removeQuadElem(SMESHDS_SubMesh * theSm,
9123 SMDS_ElemIteratorPtr theItr,
9124 const int theShapeID)
9127 SMESHDS_Mesh* meshDS = GetMeshDS();
9129 while( theItr->more() )
9131 const SMDS_MeshElement* elem = theItr->next();
9133 if( elem && elem->IsQuadratic())
9135 int id = elem->GetID();
9136 int nbCornerNodes = elem->NbCornerNodes();
9137 SMDSAbs_ElementType aType = elem->GetType();
9139 vector<const SMDS_MeshNode *> nodes( elem->begin_nodes(), elem->end_nodes() );
9141 //remove a quadratic element
9142 if ( !theSm || !theSm->Contains( elem ))
9143 theSm = meshDS->MeshElements( elem->getshapeId() );
9144 meshDS->RemoveFreeElement( elem, theSm, /*fromGroups=*/false );
9146 // remove medium nodes
9147 for ( unsigned i = nbCornerNodes; i < nodes.size(); ++i )
9148 if ( nodes[i]->NbInverseElements() == 0 )
9149 meshDS->RemoveFreeNode( nodes[i], theSm );
9151 // add a linear element
9152 nodes.resize( nbCornerNodes );
9153 SMDS_MeshElement * newElem = AddElement( nodes, aType, false, id );
9154 ReplaceElemInGroups(elem, newElem, meshDS);
9155 if( theSm && newElem )
9156 theSm->AddElement( newElem );
9162 //=======================================================================
9163 //function : ConvertFromQuadratic
9165 //=======================================================================
9167 bool SMESH_MeshEditor::ConvertFromQuadratic()
9169 int nbCheckedElems = 0;
9170 if ( myMesh->HasShapeToMesh() )
9172 if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9174 SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9175 while ( smIt->more() ) {
9176 SMESH_subMesh* sm = smIt->next();
9177 if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() )
9178 nbCheckedElems += removeQuadElem( smDS, smDS->GetElements(), sm->GetId() );
9184 GetMeshDS()->NbEdges() + GetMeshDS()->NbFaces() + GetMeshDS()->NbVolumes();
9185 if ( nbCheckedElems < totalNbElems ) // not all elements are in submeshes
9187 SMESHDS_SubMesh *aSM = 0;
9188 removeQuadElem( aSM, GetMeshDS()->elementsIterator(), 0 );
9196 //================================================================================
9198 * \brief Return true if all medium nodes of the element are in the node set
9200 //================================================================================
9202 bool allMediumNodesIn(const SMDS_MeshElement* elem, TIDSortedNodeSet& nodeSet )
9204 for ( int i = elem->NbCornerNodes(); i < elem->NbNodes(); ++i )
9205 if ( !nodeSet.count( elem->GetNode(i) ))
9211 //================================================================================
9213 * \brief Makes given elements linear
9215 //================================================================================
9217 void SMESH_MeshEditor::ConvertFromQuadratic(TIDSortedElemSet& theElements)
9219 if ( theElements.empty() ) return;
9221 // collect IDs of medium nodes of theElements; some of these nodes will be removed
9222 set<int> mediumNodeIDs;
9223 TIDSortedElemSet::iterator eIt = theElements.begin();
9224 for ( ; eIt != theElements.end(); ++eIt )
9226 const SMDS_MeshElement* e = *eIt;
9227 for ( int i = e->NbCornerNodes(); i < e->NbNodes(); ++i )
9228 mediumNodeIDs.insert( e->GetNode(i)->GetID() );
9231 // replace given elements by linear ones
9232 SMDS_ElemIteratorPtr elemIt = elemSetIterator( theElements );
9233 removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9235 // we need to convert remaining elements whose all medium nodes are in mediumNodeIDs
9236 // except those elements sharing medium nodes of quadratic element whose medium nodes
9237 // are not all in mediumNodeIDs
9239 // get remaining medium nodes
9240 TIDSortedNodeSet mediumNodes;
9241 set<int>::iterator nIdsIt = mediumNodeIDs.begin();
9242 for ( ; nIdsIt != mediumNodeIDs.end(); ++nIdsIt )
9243 if ( const SMDS_MeshNode* n = GetMeshDS()->FindNode( *nIdsIt ))
9244 mediumNodes.insert( mediumNodes.end(), n );
9246 // find more quadratic elements to convert
9247 TIDSortedElemSet moreElemsToConvert;
9248 TIDSortedNodeSet::iterator nIt = mediumNodes.begin();
9249 for ( ; nIt != mediumNodes.end(); ++nIt )
9251 SMDS_ElemIteratorPtr invIt = (*nIt)->GetInverseElementIterator();
9252 while ( invIt->more() )
9254 const SMDS_MeshElement* e = invIt->next();
9255 if ( e->IsQuadratic() && allMediumNodesIn( e, mediumNodes ))
9257 // find a more complex element including e and
9258 // whose medium nodes are not in mediumNodes
9259 bool complexFound = false;
9260 for ( int type = e->GetType() + 1; type < SMDSAbs_0DElement; ++type )
9262 SMDS_ElemIteratorPtr invIt2 =
9263 (*nIt)->GetInverseElementIterator( SMDSAbs_ElementType( type ));
9264 while ( invIt2->more() )
9266 const SMDS_MeshElement* eComplex = invIt2->next();
9267 if ( eComplex->IsQuadratic() && !allMediumNodesIn( eComplex, mediumNodes))
9269 int nbCommonNodes = SMESH_MeshAlgos::GetCommonNodes( e, eComplex ).size();
9270 if ( nbCommonNodes == e->NbNodes())
9272 complexFound = true;
9273 type = SMDSAbs_NbElementTypes; // to quit from the outer loop
9279 if ( !complexFound )
9280 moreElemsToConvert.insert( e );
9284 elemIt = elemSetIterator( moreElemsToConvert );
9285 removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9288 //=======================================================================
9289 //function : SewSideElements
9291 //=======================================================================
9293 SMESH_MeshEditor::Sew_Error
9294 SMESH_MeshEditor::SewSideElements (TIDSortedElemSet& theSide1,
9295 TIDSortedElemSet& theSide2,
9296 const SMDS_MeshNode* theFirstNode1,
9297 const SMDS_MeshNode* theFirstNode2,
9298 const SMDS_MeshNode* theSecondNode1,
9299 const SMDS_MeshNode* theSecondNode2)
9301 myLastCreatedElems.Clear();
9302 myLastCreatedNodes.Clear();
9304 MESSAGE ("::::SewSideElements()");
9305 if ( theSide1.size() != theSide2.size() )
9306 return SEW_DIFF_NB_OF_ELEMENTS;
9308 Sew_Error aResult = SEW_OK;
9310 // 1. Build set of faces representing each side
9311 // 2. Find which nodes of the side 1 to merge with ones on the side 2
9312 // 3. Replace nodes in elements of the side 1 and remove replaced nodes
9314 // =======================================================================
9315 // 1. Build set of faces representing each side:
9316 // =======================================================================
9317 // a. build set of nodes belonging to faces
9318 // b. complete set of faces: find missing faces whose nodes are in set of nodes
9319 // c. create temporary faces representing side of volumes if correspondent
9320 // face does not exist
9322 SMESHDS_Mesh* aMesh = GetMeshDS();
9323 // TODO algoritm not OK with vtkUnstructuredGrid: 2 meshes can't share nodes
9324 //SMDS_Mesh aTmpFacesMesh; // try to use the same mesh
9325 TIDSortedElemSet faceSet1, faceSet2;
9326 set<const SMDS_MeshElement*> volSet1, volSet2;
9327 set<const SMDS_MeshNode*> nodeSet1, nodeSet2;
9328 TIDSortedElemSet * faceSetPtr[] = { &faceSet1, &faceSet2 };
9329 set<const SMDS_MeshElement*> * volSetPtr[] = { &volSet1, &volSet2 };
9330 set<const SMDS_MeshNode*> * nodeSetPtr[] = { &nodeSet1, &nodeSet2 };
9331 TIDSortedElemSet * elemSetPtr[] = { &theSide1, &theSide2 };
9332 int iSide, iFace, iNode;
9334 list<const SMDS_MeshElement* > tempFaceList;
9335 for ( iSide = 0; iSide < 2; iSide++ ) {
9336 set<const SMDS_MeshNode*> * nodeSet = nodeSetPtr[ iSide ];
9337 TIDSortedElemSet * elemSet = elemSetPtr[ iSide ];
9338 TIDSortedElemSet * faceSet = faceSetPtr[ iSide ];
9339 set<const SMDS_MeshElement*> * volSet = volSetPtr [ iSide ];
9340 set<const SMDS_MeshElement*>::iterator vIt;
9341 TIDSortedElemSet::iterator eIt;
9342 set<const SMDS_MeshNode*>::iterator nIt;
9344 // check that given nodes belong to given elements
9345 const SMDS_MeshNode* n1 = ( iSide == 0 ) ? theFirstNode1 : theFirstNode2;
9346 const SMDS_MeshNode* n2 = ( iSide == 0 ) ? theSecondNode1 : theSecondNode2;
9347 int firstIndex = -1, secondIndex = -1;
9348 for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9349 const SMDS_MeshElement* elem = *eIt;
9350 if ( firstIndex < 0 ) firstIndex = elem->GetNodeIndex( n1 );
9351 if ( secondIndex < 0 ) secondIndex = elem->GetNodeIndex( n2 );
9352 if ( firstIndex > -1 && secondIndex > -1 ) break;
9354 if ( firstIndex < 0 || secondIndex < 0 ) {
9355 // we can simply return until temporary faces created
9356 return (iSide == 0 ) ? SEW_BAD_SIDE1_NODES : SEW_BAD_SIDE2_NODES;
9359 // -----------------------------------------------------------
9360 // 1a. Collect nodes of existing faces
9361 // and build set of face nodes in order to detect missing
9362 // faces corresponding to sides of volumes
9363 // -----------------------------------------------------------
9365 set< set <const SMDS_MeshNode*> > setOfFaceNodeSet;
9367 // loop on the given element of a side
9368 for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9369 //const SMDS_MeshElement* elem = *eIt;
9370 const SMDS_MeshElement* elem = *eIt;
9371 if ( elem->GetType() == SMDSAbs_Face ) {
9372 faceSet->insert( elem );
9373 set <const SMDS_MeshNode*> faceNodeSet;
9374 SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
9375 while ( nodeIt->more() ) {
9376 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9377 nodeSet->insert( n );
9378 faceNodeSet.insert( n );
9380 setOfFaceNodeSet.insert( faceNodeSet );
9382 else if ( elem->GetType() == SMDSAbs_Volume )
9383 volSet->insert( elem );
9385 // ------------------------------------------------------------------------------
9386 // 1b. Complete set of faces: find missing faces whose nodes are in set of nodes
9387 // ------------------------------------------------------------------------------
9389 for ( nIt = nodeSet->begin(); nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
9390 SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
9391 while ( fIt->more() ) { // loop on faces sharing a node
9392 const SMDS_MeshElement* f = fIt->next();
9393 if ( faceSet->find( f ) == faceSet->end() ) {
9394 // check if all nodes are in nodeSet and
9395 // complete setOfFaceNodeSet if they are
9396 set <const SMDS_MeshNode*> faceNodeSet;
9397 SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
9398 bool allInSet = true;
9399 while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
9400 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9401 if ( nodeSet->find( n ) == nodeSet->end() )
9404 faceNodeSet.insert( n );
9407 faceSet->insert( f );
9408 setOfFaceNodeSet.insert( faceNodeSet );
9414 // -------------------------------------------------------------------------
9415 // 1c. Create temporary faces representing sides of volumes if correspondent
9416 // face does not exist
9417 // -------------------------------------------------------------------------
9419 if ( !volSet->empty() ) {
9420 //int nodeSetSize = nodeSet->size();
9422 // loop on given volumes
9423 for ( vIt = volSet->begin(); vIt != volSet->end(); vIt++ ) {
9424 SMDS_VolumeTool vol (*vIt);
9425 // loop on volume faces: find free faces
9426 // --------------------------------------
9427 list<const SMDS_MeshElement* > freeFaceList;
9428 for ( iFace = 0; iFace < vol.NbFaces(); iFace++ ) {
9429 if ( !vol.IsFreeFace( iFace ))
9431 // check if there is already a face with same nodes in a face set
9432 const SMDS_MeshElement* aFreeFace = 0;
9433 const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iFace );
9434 int nbNodes = vol.NbFaceNodes( iFace );
9435 set <const SMDS_MeshNode*> faceNodeSet;
9436 vol.GetFaceNodes( iFace, faceNodeSet );
9437 bool isNewFace = setOfFaceNodeSet.insert( faceNodeSet ).second;
9439 // no such a face is given but it still can exist, check it
9440 vector<const SMDS_MeshNode *> nodes ( fNodes, fNodes + nbNodes);
9441 aFreeFace = aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false );
9444 // create a temporary face
9445 if ( nbNodes == 3 ) {
9446 //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2] );
9447 aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2] );
9449 else if ( nbNodes == 4 ) {
9450 //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
9451 aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
9454 vector<const SMDS_MeshNode *> poly_nodes ( fNodes, & fNodes[nbNodes]);
9455 //aFreeFace = aTmpFacesMesh.AddPolygonalFace(poly_nodes);
9456 aFreeFace = aMesh->AddPolygonalFace(poly_nodes);
9459 tempFaceList.push_back( aFreeFace );
9463 freeFaceList.push_back( aFreeFace );
9465 } // loop on faces of a volume
9467 // choose one of several free faces of a volume
9468 // --------------------------------------------
9469 if ( freeFaceList.size() > 1 ) {
9470 // choose a face having max nb of nodes shared by other elems of a side
9471 int maxNbNodes = -1;
9472 list<const SMDS_MeshElement* >::iterator fIt = freeFaceList.begin();
9473 while ( fIt != freeFaceList.end() ) { // loop on free faces
9474 int nbSharedNodes = 0;
9475 SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
9476 while ( nodeIt->more() ) { // loop on free face nodes
9477 const SMDS_MeshNode* n =
9478 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9479 SMDS_ElemIteratorPtr invElemIt = n->GetInverseElementIterator();
9480 while ( invElemIt->more() ) {
9481 const SMDS_MeshElement* e = invElemIt->next();
9482 nbSharedNodes += faceSet->count( e );
9483 nbSharedNodes += elemSet->count( e );
9486 if ( nbSharedNodes > maxNbNodes ) {
9487 maxNbNodes = nbSharedNodes;
9488 freeFaceList.erase( freeFaceList.begin(), fIt++ );
9490 else if ( nbSharedNodes == maxNbNodes ) {
9494 freeFaceList.erase( fIt++ ); // here fIt++ occurs before erase
9497 if ( freeFaceList.size() > 1 )
9499 // could not choose one face, use another way
9500 // choose a face most close to the bary center of the opposite side
9501 gp_XYZ aBC( 0., 0., 0. );
9502 set <const SMDS_MeshNode*> addedNodes;
9503 TIDSortedElemSet * elemSet2 = elemSetPtr[ 1 - iSide ];
9504 eIt = elemSet2->begin();
9505 for ( eIt = elemSet2->begin(); eIt != elemSet2->end(); eIt++ ) {
9506 SMDS_ElemIteratorPtr nodeIt = (*eIt)->nodesIterator();
9507 while ( nodeIt->more() ) { // loop on free face nodes
9508 const SMDS_MeshNode* n =
9509 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9510 if ( addedNodes.insert( n ).second )
9511 aBC += gp_XYZ( n->X(),n->Y(),n->Z() );
9514 aBC /= addedNodes.size();
9515 double minDist = DBL_MAX;
9516 fIt = freeFaceList.begin();
9517 while ( fIt != freeFaceList.end() ) { // loop on free faces
9519 SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
9520 while ( nodeIt->more() ) { // loop on free face nodes
9521 const SMDS_MeshNode* n =
9522 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9523 gp_XYZ p( n->X(),n->Y(),n->Z() );
9524 dist += ( aBC - p ).SquareModulus();
9526 if ( dist < minDist ) {
9528 freeFaceList.erase( freeFaceList.begin(), fIt++ );
9531 fIt = freeFaceList.erase( fIt++ );
9534 } // choose one of several free faces of a volume
9536 if ( freeFaceList.size() == 1 ) {
9537 const SMDS_MeshElement* aFreeFace = freeFaceList.front();
9538 faceSet->insert( aFreeFace );
9539 // complete a node set with nodes of a found free face
9540 // for ( iNode = 0; iNode < ; iNode++ )
9541 // nodeSet->insert( fNodes[ iNode ] );
9544 } // loop on volumes of a side
9546 // // complete a set of faces if new nodes in a nodeSet appeared
9547 // // ----------------------------------------------------------
9548 // if ( nodeSetSize != nodeSet->size() ) {
9549 // for ( ; nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
9550 // SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
9551 // while ( fIt->more() ) { // loop on faces sharing a node
9552 // const SMDS_MeshElement* f = fIt->next();
9553 // if ( faceSet->find( f ) == faceSet->end() ) {
9554 // // check if all nodes are in nodeSet and
9555 // // complete setOfFaceNodeSet if they are
9556 // set <const SMDS_MeshNode*> faceNodeSet;
9557 // SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
9558 // bool allInSet = true;
9559 // while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
9560 // const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9561 // if ( nodeSet->find( n ) == nodeSet->end() )
9562 // allInSet = false;
9564 // faceNodeSet.insert( n );
9566 // if ( allInSet ) {
9567 // faceSet->insert( f );
9568 // setOfFaceNodeSet.insert( faceNodeSet );
9574 } // Create temporary faces, if there are volumes given
9577 if ( faceSet1.size() != faceSet2.size() ) {
9578 // delete temporary faces: they are in reverseElements of actual nodes
9579 // SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
9580 // while ( tmpFaceIt->more() )
9581 // aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
9582 // list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
9583 // for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
9584 // aMesh->RemoveElement(*tmpFaceIt);
9585 MESSAGE("Diff nb of faces");
9586 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9589 // ============================================================
9590 // 2. Find nodes to merge:
9591 // bind a node to remove to a node to put instead
9592 // ============================================================
9594 TNodeNodeMap nReplaceMap; // bind a node to remove to a node to put instead
9595 if ( theFirstNode1 != theFirstNode2 )
9596 nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
9597 if ( theSecondNode1 != theSecondNode2 )
9598 nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
9600 LinkID_Gen aLinkID_Gen( GetMeshDS() );
9601 set< long > linkIdSet; // links to process
9602 linkIdSet.insert( aLinkID_Gen.GetLinkID( theFirstNode1, theSecondNode1 ));
9604 typedef pair< const SMDS_MeshNode*, const SMDS_MeshNode* > NLink;
9605 list< NLink > linkList[2];
9606 linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
9607 linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
9608 // loop on links in linkList; find faces by links and append links
9609 // of the found faces to linkList
9610 list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
9611 for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ )
9613 NLink link[] = { *linkIt[0], *linkIt[1] };
9614 long linkID = aLinkID_Gen.GetLinkID( link[0].first, link[0].second );
9615 if ( !linkIdSet.count( linkID ) )
9618 // by links, find faces in the face sets,
9619 // and find indices of link nodes in the found faces;
9620 // in a face set, there is only one or no face sharing a link
9621 // ---------------------------------------------------------------
9623 const SMDS_MeshElement* face[] = { 0, 0 };
9624 vector<const SMDS_MeshNode*> fnodes[2];
9625 int iLinkNode[2][2];
9626 TIDSortedElemSet avoidSet;
9627 for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
9628 const SMDS_MeshNode* n1 = link[iSide].first;
9629 const SMDS_MeshNode* n2 = link[iSide].second;
9630 //cout << "Side " << iSide << " ";
9631 //cout << "L( " << n1->GetID() << ", " << n2->GetID() << " ) " << endl;
9632 // find a face by two link nodes
9633 face[ iSide ] = SMESH_MeshAlgos::FindFaceInSet( n1, n2,
9634 *faceSetPtr[ iSide ], avoidSet,
9635 &iLinkNode[iSide][0],
9636 &iLinkNode[iSide][1] );
9639 //cout << " F " << face[ iSide]->GetID() <<endl;
9640 faceSetPtr[ iSide ]->erase( face[ iSide ]);
9641 // put face nodes to fnodes
9642 if ( face[ iSide ]->IsQuadratic() )
9644 // use interlaced nodes iterator
9645 const SMDS_VtkFace* F = dynamic_cast<const SMDS_VtkFace*>( face[ iSide ]);
9646 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
9647 SMDS_ElemIteratorPtr nIter = F->interlacedNodesElemIterator();
9648 while ( nIter->more() )
9649 fnodes[ iSide ].push_back( cast2Node( nIter->next() ));
9653 fnodes[ iSide ].assign( face[ iSide ]->begin_nodes(),
9654 face[ iSide ]->end_nodes() );
9656 fnodes[ iSide ].push_back( fnodes[ iSide ].front());
9660 // check similarity of elements of the sides
9661 if (aResult == SEW_OK && (( face[0] && !face[1] ) || ( !face[0] && face[1] ))) {
9662 MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
9663 if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
9664 aResult = ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
9667 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9669 break; // do not return because it's necessary to remove tmp faces
9672 // set nodes to merge
9673 // -------------------
9675 if ( face[0] && face[1] ) {
9676 const int nbNodes = face[0]->NbNodes();
9677 if ( nbNodes != face[1]->NbNodes() ) {
9678 MESSAGE("Diff nb of face nodes");
9679 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9680 break; // do not return because it s necessary to remove tmp faces
9682 bool reverse[] = { false, false }; // order of nodes in the link
9683 for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
9684 // analyse link orientation in faces
9685 int i1 = iLinkNode[ iSide ][ 0 ];
9686 int i2 = iLinkNode[ iSide ][ 1 ];
9687 reverse[ iSide ] = Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1;
9689 int di1 = reverse[0] ? -1 : +1, i1 = iLinkNode[0][1] + di1;
9690 int di2 = reverse[1] ? -1 : +1, i2 = iLinkNode[1][1] + di2;
9691 for ( int i = nbNodes - 2; i > 0; --i, i1 += di1, i2 += di2 )
9693 nReplaceMap.insert ( make_pair ( fnodes[0][ ( i1 + nbNodes ) % nbNodes ],
9694 fnodes[1][ ( i2 + nbNodes ) % nbNodes ]));
9697 // add other links of the faces to linkList
9698 // -----------------------------------------
9700 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
9701 linkID = aLinkID_Gen.GetLinkID( fnodes[0][iNode], fnodes[0][iNode+1] );
9702 pair< set<long>::iterator, bool > iter_isnew = linkIdSet.insert( linkID );
9703 if ( !iter_isnew.second ) { // already in a set: no need to process
9704 linkIdSet.erase( iter_isnew.first );
9706 else // new in set == encountered for the first time: add
9708 const SMDS_MeshNode* n1 = fnodes[0][ iNode ];
9709 const SMDS_MeshNode* n2 = fnodes[0][ iNode + 1];
9710 linkList[0].push_back ( NLink( n1, n2 ));
9711 linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
9716 if ( faceSetPtr[0]->empty() || faceSetPtr[1]->empty() )
9719 } // loop on link lists
9721 if ( aResult == SEW_OK &&
9722 ( //linkIt[0] != linkList[0].end() ||
9723 !faceSetPtr[0]->empty() || !faceSetPtr[1]->empty() )) {
9724 MESSAGE( (linkIt[0] != linkList[0].end()) <<" "<< (faceSetPtr[0]->empty()) <<
9725 " " << (faceSetPtr[1]->empty()));
9726 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9729 // ====================================================================
9730 // 3. Replace nodes in elements of the side 1 and remove replaced nodes
9731 // ====================================================================
9733 // delete temporary faces
9734 // SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
9735 // while ( tmpFaceIt->more() )
9736 // aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
9737 list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
9738 for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
9739 aMesh->RemoveElement(*tmpFaceIt);
9741 if ( aResult != SEW_OK)
9744 list< int > nodeIDsToRemove/*, elemIDsToRemove*/;
9745 // loop on nodes replacement map
9746 TNodeNodeMap::iterator nReplaceMapIt = nReplaceMap.begin(), nnIt;
9747 for ( ; nReplaceMapIt != nReplaceMap.end(); nReplaceMapIt++ )
9748 if ( (*nReplaceMapIt).first != (*nReplaceMapIt).second ) {
9749 const SMDS_MeshNode* nToRemove = (*nReplaceMapIt).first;
9750 nodeIDsToRemove.push_back( nToRemove->GetID() );
9751 // loop on elements sharing nToRemove
9752 SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
9753 while ( invElemIt->more() ) {
9754 const SMDS_MeshElement* e = invElemIt->next();
9755 // get a new suite of nodes: make replacement
9756 int nbReplaced = 0, i = 0, nbNodes = e->NbNodes();
9757 vector< const SMDS_MeshNode*> nodes( nbNodes );
9758 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
9759 while ( nIt->more() ) {
9760 const SMDS_MeshNode* n =
9761 static_cast<const SMDS_MeshNode*>( nIt->next() );
9762 nnIt = nReplaceMap.find( n );
9763 if ( nnIt != nReplaceMap.end() ) {
9769 // if ( nbReplaced == nbNodes && e->GetType() == SMDSAbs_Face )
9770 // elemIDsToRemove.push_back( e->GetID() );
9774 SMDSAbs_ElementType etyp = e->GetType();
9775 SMDS_MeshElement* newElem = this->AddElement(nodes, etyp, false);
9778 myLastCreatedElems.Append(newElem);
9779 AddToSameGroups(newElem, e, aMesh);
9780 int aShapeId = e->getshapeId();
9783 aMesh->SetMeshElementOnShape( newElem, aShapeId );
9786 aMesh->RemoveElement(e);
9791 Remove( nodeIDsToRemove, true );
9796 //================================================================================
9798 * \brief Find corresponding nodes in two sets of faces
9799 * \param theSide1 - first face set
9800 * \param theSide2 - second first face
9801 * \param theFirstNode1 - a boundary node of set 1
9802 * \param theFirstNode2 - a node of set 2 corresponding to theFirstNode1
9803 * \param theSecondNode1 - a boundary node of set 1 linked with theFirstNode1
9804 * \param theSecondNode2 - a node of set 2 corresponding to theSecondNode1
9805 * \param nReplaceMap - output map of corresponding nodes
9806 * \return bool - is a success or not
9808 //================================================================================
9811 //#define DEBUG_MATCHING_NODES
9814 SMESH_MeshEditor::Sew_Error
9815 SMESH_MeshEditor::FindMatchingNodes(set<const SMDS_MeshElement*>& theSide1,
9816 set<const SMDS_MeshElement*>& theSide2,
9817 const SMDS_MeshNode* theFirstNode1,
9818 const SMDS_MeshNode* theFirstNode2,
9819 const SMDS_MeshNode* theSecondNode1,
9820 const SMDS_MeshNode* theSecondNode2,
9821 TNodeNodeMap & nReplaceMap)
9823 set<const SMDS_MeshElement*> * faceSetPtr[] = { &theSide1, &theSide2 };
9825 nReplaceMap.clear();
9826 if ( theFirstNode1 != theFirstNode2 )
9827 nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
9828 if ( theSecondNode1 != theSecondNode2 )
9829 nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
9831 set< SMESH_TLink > linkSet; // set of nodes where order of nodes is ignored
9832 linkSet.insert( SMESH_TLink( theFirstNode1, theSecondNode1 ));
9834 list< NLink > linkList[2];
9835 linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
9836 linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
9838 // loop on links in linkList; find faces by links and append links
9839 // of the found faces to linkList
9840 list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
9841 for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ ) {
9842 NLink link[] = { *linkIt[0], *linkIt[1] };
9843 if ( linkSet.find( link[0] ) == linkSet.end() )
9846 // by links, find faces in the face sets,
9847 // and find indices of link nodes in the found faces;
9848 // in a face set, there is only one or no face sharing a link
9849 // ---------------------------------------------------------------
9851 const SMDS_MeshElement* face[] = { 0, 0 };
9852 list<const SMDS_MeshNode*> notLinkNodes[2];
9853 //bool reverse[] = { false, false }; // order of notLinkNodes
9855 for ( int iSide = 0; iSide < 2; iSide++ ) // loop on 2 sides
9857 const SMDS_MeshNode* n1 = link[iSide].first;
9858 const SMDS_MeshNode* n2 = link[iSide].second;
9859 set<const SMDS_MeshElement*> * faceSet = faceSetPtr[ iSide ];
9860 set< const SMDS_MeshElement* > facesOfNode1;
9861 for ( int iNode = 0; iNode < 2; iNode++ ) // loop on 2 nodes of a link
9863 // during a loop of the first node, we find all faces around n1,
9864 // during a loop of the second node, we find one face sharing both n1 and n2
9865 const SMDS_MeshNode* n = iNode ? n1 : n2; // a node of a link
9866 SMDS_ElemIteratorPtr fIt = n->GetInverseElementIterator(SMDSAbs_Face);
9867 while ( fIt->more() ) { // loop on faces sharing a node
9868 const SMDS_MeshElement* f = fIt->next();
9869 if (faceSet->find( f ) != faceSet->end() && // f is in face set
9870 ! facesOfNode1.insert( f ).second ) // f encounters twice
9872 if ( face[ iSide ] ) {
9873 MESSAGE( "2 faces per link " );
9874 return ( iSide ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
9877 faceSet->erase( f );
9879 // get not link nodes
9880 int nbN = f->NbNodes();
9881 if ( f->IsQuadratic() )
9883 nbNodes[ iSide ] = nbN;
9884 list< const SMDS_MeshNode* > & nodes = notLinkNodes[ iSide ];
9885 int i1 = f->GetNodeIndex( n1 );
9886 int i2 = f->GetNodeIndex( n2 );
9887 int iEnd = nbN, iBeg = -1, iDelta = 1;
9888 bool reverse = ( Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1 );
9890 std::swap( iEnd, iBeg ); iDelta = -1;
9895 if ( i == iEnd ) i = iBeg + iDelta;
9896 if ( i == i1 ) break;
9897 nodes.push_back ( f->GetNode( i ) );
9903 // check similarity of elements of the sides
9904 if (( face[0] && !face[1] ) || ( !face[0] && face[1] )) {
9905 MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
9906 if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
9907 return ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
9910 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9914 // set nodes to merge
9915 // -------------------
9917 if ( face[0] && face[1] ) {
9918 if ( nbNodes[0] != nbNodes[1] ) {
9919 MESSAGE("Diff nb of face nodes");
9920 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9922 #ifdef DEBUG_MATCHING_NODES
9923 MESSAGE ( " Link 1: " << link[0].first->GetID() <<" "<< link[0].second->GetID()
9924 << " F 1: " << face[0] << "| Link 2: " << link[1].first->GetID() <<" "
9925 << link[1].second->GetID() << " F 2: " << face[1] << " | Bind: " ) ;
9927 int nbN = nbNodes[0];
9929 list<const SMDS_MeshNode*>::iterator n1 = notLinkNodes[0].begin();
9930 list<const SMDS_MeshNode*>::iterator n2 = notLinkNodes[1].begin();
9931 for ( int i = 0 ; i < nbN - 2; ++i ) {
9932 #ifdef DEBUG_MATCHING_NODES
9933 MESSAGE ( (*n1)->GetID() << " to " << (*n2)->GetID() );
9935 nReplaceMap.insert( make_pair( *(n1++), *(n2++) ));
9939 // add other links of the face 1 to linkList
9940 // -----------------------------------------
9942 const SMDS_MeshElement* f0 = face[0];
9943 const SMDS_MeshNode* n1 = f0->GetNode( nbN - 1 );
9944 for ( int i = 0; i < nbN; i++ )
9946 const SMDS_MeshNode* n2 = f0->GetNode( i );
9947 pair< set< SMESH_TLink >::iterator, bool > iter_isnew =
9948 linkSet.insert( SMESH_TLink( n1, n2 ));
9949 if ( !iter_isnew.second ) { // already in a set: no need to process
9950 linkSet.erase( iter_isnew.first );
9952 else // new in set == encountered for the first time: add
9954 #ifdef DEBUG_MATCHING_NODES
9955 MESSAGE ( "Add link 1: " << n1->GetID() << " " << n2->GetID() << " "
9956 << " | link 2: " << nReplaceMap[n1]->GetID() << " " << nReplaceMap[n2]->GetID() << " " );
9958 linkList[0].push_back ( NLink( n1, n2 ));
9959 linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
9964 } // loop on link lists
9969 //================================================================================
9971 * \brief Create elements equal (on same nodes) to given ones
9972 * \param [in] theElements - a set of elems to duplicate. If it is empty, all
9973 * elements of the uppest dimension are duplicated.
9975 //================================================================================
9977 void SMESH_MeshEditor::DoubleElements( const TIDSortedElemSet& theElements )
9980 SMESHDS_Mesh* mesh = GetMeshDS();
9982 // get an element type and an iterator over elements
9984 SMDSAbs_ElementType type;
9985 SMDS_ElemIteratorPtr elemIt;
9986 vector< const SMDS_MeshElement* > allElems;
9987 if ( theElements.empty() )
9989 if ( mesh->NbNodes() == 0 )
9991 // get most complex type
9992 SMDSAbs_ElementType types[SMDSAbs_NbElementTypes] = {
9993 SMDSAbs_Volume, SMDSAbs_Face, SMDSAbs_Edge,
9994 SMDSAbs_0DElement, SMDSAbs_Ball, SMDSAbs_Node
9996 for ( int i = 0; i < SMDSAbs_NbElementTypes; ++i )
9997 if ( mesh->GetMeshInfo().NbElements( types[i] ))
10002 // put all elements in the vector <allElems>
10003 allElems.reserve( mesh->GetMeshInfo().NbElements( type ));
10004 elemIt = mesh->elementsIterator( type );
10005 while ( elemIt->more() )
10006 allElems.push_back( elemIt->next());
10007 elemIt = elemSetIterator( allElems );
10011 type = (*theElements.begin())->GetType();
10012 elemIt = elemSetIterator( theElements );
10015 // duplicate elements
10017 if ( type == SMDSAbs_Ball )
10019 SMDS_UnstructuredGrid* vtkGrid = mesh->getGrid();
10020 while ( elemIt->more() )
10022 const SMDS_MeshElement* elem = elemIt->next();
10023 if ( elem->GetType() != SMDSAbs_Ball )
10025 if (( elem = mesh->AddBall( elem->GetNode(0),
10026 vtkGrid->GetBallDiameter( elem->getVtkId() ))))
10027 myLastCreatedElems.Append( elem );
10032 vector< const SMDS_MeshNode* > nodes;
10033 while ( elemIt->more() )
10035 const SMDS_MeshElement* elem = elemIt->next();
10036 if ( elem->GetType() != type )
10039 nodes.assign( elem->begin_nodes(), elem->end_nodes() );
10041 if ( type == SMDSAbs_Volume && elem->GetVtkType() == VTK_POLYHEDRON )
10043 std::vector<int> quantities =
10044 static_cast< const SMDS_VtkVolume* >( elem )->GetQuantities();
10045 elem = mesh->AddPolyhedralVolume( nodes, quantities );
10049 AddElement( nodes, type, elem->IsPoly() );
10050 elem = 0; // myLastCreatedElems is already filled
10053 myLastCreatedElems.Append( elem );
10058 //================================================================================
10060 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10061 \param theElems - the list of elements (edges or faces) to be replicated
10062 The nodes for duplication could be found from these elements
10063 \param theNodesNot - list of nodes to NOT replicate
10064 \param theAffectedElems - the list of elements (cells and edges) to which the
10065 replicated nodes should be associated to.
10066 \return TRUE if operation has been completed successfully, FALSE otherwise
10068 //================================================================================
10070 bool SMESH_MeshEditor::DoubleNodes( const TIDSortedElemSet& theElems,
10071 const TIDSortedElemSet& theNodesNot,
10072 const TIDSortedElemSet& theAffectedElems )
10074 myLastCreatedElems.Clear();
10075 myLastCreatedNodes.Clear();
10077 if ( theElems.size() == 0 )
10080 SMESHDS_Mesh* aMeshDS = GetMeshDS();
10085 std::map< const SMDS_MeshNode*, const SMDS_MeshNode* > anOldNodeToNewNode;
10086 // duplicate elements and nodes
10087 res = doubleNodes( aMeshDS, theElems, theNodesNot, anOldNodeToNewNode, true );
10088 // replce nodes by duplications
10089 res = doubleNodes( aMeshDS, theAffectedElems, theNodesNot, anOldNodeToNewNode, false );
10093 //================================================================================
10095 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10096 \param theMeshDS - mesh instance
10097 \param theElems - the elements replicated or modified (nodes should be changed)
10098 \param theNodesNot - nodes to NOT replicate
10099 \param theNodeNodeMap - relation of old node to new created node
10100 \param theIsDoubleElem - flag os to replicate element or modify
10101 \return TRUE if operation has been completed successfully, FALSE otherwise
10103 //================================================================================
10105 bool SMESH_MeshEditor::doubleNodes( SMESHDS_Mesh* theMeshDS,
10106 const TIDSortedElemSet& theElems,
10107 const TIDSortedElemSet& theNodesNot,
10108 std::map< const SMDS_MeshNode*,
10109 const SMDS_MeshNode* >& theNodeNodeMap,
10110 const bool theIsDoubleElem )
10112 MESSAGE("doubleNodes");
10113 // iterate on through element and duplicate them (by nodes duplication)
10115 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10116 for ( ; elemItr != theElems.end(); ++elemItr )
10118 const SMDS_MeshElement* anElem = *elemItr;
10122 bool isDuplicate = false;
10123 // duplicate nodes to duplicate element
10124 std::vector<const SMDS_MeshNode*> newNodes( anElem->NbNodes() );
10125 SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
10127 while ( anIter->more() )
10130 SMDS_MeshNode* aCurrNode = (SMDS_MeshNode*)anIter->next();
10131 SMDS_MeshNode* aNewNode = aCurrNode;
10132 if ( theNodeNodeMap.find( aCurrNode ) != theNodeNodeMap.end() )
10133 aNewNode = (SMDS_MeshNode*)theNodeNodeMap[ aCurrNode ];
10134 else if ( theIsDoubleElem && theNodesNot.find( aCurrNode ) == theNodesNot.end() )
10137 aNewNode = theMeshDS->AddNode( aCurrNode->X(), aCurrNode->Y(), aCurrNode->Z() );
10138 theNodeNodeMap[ aCurrNode ] = aNewNode;
10139 myLastCreatedNodes.Append( aNewNode );
10141 isDuplicate |= (aCurrNode != aNewNode);
10142 newNodes[ ind++ ] = aNewNode;
10144 if ( !isDuplicate )
10147 if ( theIsDoubleElem )
10148 AddElement(newNodes, anElem->GetType(), anElem->IsPoly());
10151 MESSAGE("ChangeElementNodes");
10152 theMeshDS->ChangeElementNodes( anElem, &newNodes[ 0 ], anElem->NbNodes() );
10159 //================================================================================
10161 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10162 \param theNodes - identifiers of nodes to be doubled
10163 \param theModifiedElems - identifiers of elements to be updated by the new (doubled)
10164 nodes. If list of element identifiers is empty then nodes are doubled but
10165 they not assigned to elements
10166 \return TRUE if operation has been completed successfully, FALSE otherwise
10168 //================================================================================
10170 bool SMESH_MeshEditor::DoubleNodes( const std::list< int >& theListOfNodes,
10171 const std::list< int >& theListOfModifiedElems )
10173 MESSAGE("DoubleNodes");
10174 myLastCreatedElems.Clear();
10175 myLastCreatedNodes.Clear();
10177 if ( theListOfNodes.size() == 0 )
10180 SMESHDS_Mesh* aMeshDS = GetMeshDS();
10184 // iterate through nodes and duplicate them
10186 std::map< const SMDS_MeshNode*, const SMDS_MeshNode* > anOldNodeToNewNode;
10188 std::list< int >::const_iterator aNodeIter;
10189 for ( aNodeIter = theListOfNodes.begin(); aNodeIter != theListOfNodes.end(); ++aNodeIter )
10191 int aCurr = *aNodeIter;
10192 SMDS_MeshNode* aNode = (SMDS_MeshNode*)aMeshDS->FindNode( aCurr );
10198 const SMDS_MeshNode* aNewNode = aMeshDS->AddNode( aNode->X(), aNode->Y(), aNode->Z() );
10201 anOldNodeToNewNode[ aNode ] = aNewNode;
10202 myLastCreatedNodes.Append( aNewNode );
10206 // Create map of new nodes for modified elements
10208 std::map< SMDS_MeshElement*, vector<const SMDS_MeshNode*> > anElemToNodes;
10210 std::list< int >::const_iterator anElemIter;
10211 for ( anElemIter = theListOfModifiedElems.begin();
10212 anElemIter != theListOfModifiedElems.end(); ++anElemIter )
10214 int aCurr = *anElemIter;
10215 SMDS_MeshElement* anElem = (SMDS_MeshElement*)aMeshDS->FindElement( aCurr );
10219 vector<const SMDS_MeshNode*> aNodeArr( anElem->NbNodes() );
10221 SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
10223 while ( anIter->more() )
10225 SMDS_MeshNode* aCurrNode = (SMDS_MeshNode*)anIter->next();
10226 if ( aCurr && anOldNodeToNewNode.find( aCurrNode ) != anOldNodeToNewNode.end() )
10228 const SMDS_MeshNode* aNewNode = anOldNodeToNewNode[ aCurrNode ];
10229 aNodeArr[ ind++ ] = aNewNode;
10232 aNodeArr[ ind++ ] = aCurrNode;
10234 anElemToNodes[ anElem ] = aNodeArr;
10237 // Change nodes of elements
10239 std::map< SMDS_MeshElement*, vector<const SMDS_MeshNode*> >::iterator
10240 anElemToNodesIter = anElemToNodes.begin();
10241 for ( ; anElemToNodesIter != anElemToNodes.end(); ++anElemToNodesIter )
10243 const SMDS_MeshElement* anElem = anElemToNodesIter->first;
10244 vector<const SMDS_MeshNode*> aNodeArr = anElemToNodesIter->second;
10247 MESSAGE("ChangeElementNodes");
10248 aMeshDS->ChangeElementNodes( anElem, &aNodeArr[ 0 ], anElem->NbNodes() );
10257 //================================================================================
10259 \brief Check if element located inside shape
10260 \return TRUE if IN or ON shape, FALSE otherwise
10262 //================================================================================
10264 template<class Classifier>
10265 bool isInside(const SMDS_MeshElement* theElem,
10266 Classifier& theClassifier,
10267 const double theTol)
10269 gp_XYZ centerXYZ (0, 0, 0);
10270 SMDS_ElemIteratorPtr aNodeItr = theElem->nodesIterator();
10271 while (aNodeItr->more())
10272 centerXYZ += SMESH_TNodeXYZ(cast2Node( aNodeItr->next()));
10274 gp_Pnt aPnt = centerXYZ / theElem->NbNodes();
10275 theClassifier.Perform(aPnt, theTol);
10276 TopAbs_State aState = theClassifier.State();
10277 return (aState == TopAbs_IN || aState == TopAbs_ON );
10280 //================================================================================
10282 * \brief Classifier of the 3D point on the TopoDS_Face
10283 * with interaface suitable for isInside()
10285 //================================================================================
10287 struct _FaceClassifier
10289 Extrema_ExtPS _extremum;
10290 BRepAdaptor_Surface _surface;
10291 TopAbs_State _state;
10293 _FaceClassifier(const TopoDS_Face& face):_extremum(),_surface(face),_state(TopAbs_OUT)
10295 _extremum.Initialize( _surface,
10296 _surface.FirstUParameter(), _surface.LastUParameter(),
10297 _surface.FirstVParameter(), _surface.LastVParameter(),
10298 _surface.Tolerance(), _surface.Tolerance() );
10300 void Perform(const gp_Pnt& aPnt, double theTol)
10302 _state = TopAbs_OUT;
10303 _extremum.Perform(aPnt);
10304 if ( _extremum.IsDone() )
10305 for ( int iSol = 1; iSol <= _extremum.NbExt() && _state == TopAbs_OUT; ++iSol)
10306 #if OCC_VERSION_LARGE > 0x06040000 // Porting to OCCT6.5.1
10307 _state = ( _extremum.SquareDistance(iSol) <= theTol ? TopAbs_IN : TopAbs_OUT );
10309 _state = ( _extremum.Value(iSol) <= theTol ? TopAbs_IN : TopAbs_OUT );
10312 TopAbs_State State() const
10319 //================================================================================
10321 \brief Identify the elements that will be affected by node duplication (actual duplication is not performed).
10322 This method is the first step of DoubleNodeElemGroupsInRegion.
10323 \param theElems - list of groups of elements (edges or faces) to be replicated
10324 \param theNodesNot - list of groups of nodes not to replicated
10325 \param theShape - shape to detect affected elements (element which geometric center
10326 located on or inside shape). If the shape is null, detection is done on faces orientations
10327 (select elements with a gravity center on the side given by faces normals).
10328 This mode (null shape) is faster, but works only when theElems are faces, with coherents orientations.
10329 The replicated nodes should be associated to affected elements.
10330 \return groups of affected elements
10331 \sa DoubleNodeElemGroupsInRegion()
10333 //================================================================================
10335 bool SMESH_MeshEditor::AffectedElemGroupsInRegion( const TIDSortedElemSet& theElems,
10336 const TIDSortedElemSet& theNodesNot,
10337 const TopoDS_Shape& theShape,
10338 TIDSortedElemSet& theAffectedElems)
10340 if ( theShape.IsNull() )
10342 std::set<const SMDS_MeshNode*> alreadyCheckedNodes;
10343 std::set<const SMDS_MeshElement*> alreadyCheckedElems;
10344 std::set<const SMDS_MeshElement*> edgesToCheck;
10345 alreadyCheckedNodes.clear();
10346 alreadyCheckedElems.clear();
10347 edgesToCheck.clear();
10349 // --- iterates on elements to be replicated and get elements by back references from their nodes
10351 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10353 for ( ielem=1; elemItr != theElems.end(); ++elemItr )
10355 SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
10356 if (!anElem || (anElem->GetType() != SMDSAbs_Face))
10359 SMESH_MeshAlgos::FaceNormal( anElem, normal, /*normalized=*/true );
10360 MESSAGE("element " << ielem++ << " normal " << normal.X() << " " << normal.Y() << " " << normal.Z());
10361 std::set<const SMDS_MeshNode*> nodesElem;
10363 SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
10364 while ( nodeItr->more() )
10366 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10367 nodesElem.insert(aNode);
10369 std::set<const SMDS_MeshNode*>::iterator nodit = nodesElem.begin();
10370 for (; nodit != nodesElem.end(); nodit++)
10372 MESSAGE(" noeud ");
10373 const SMDS_MeshNode* aNode = *nodit;
10374 if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
10376 if (alreadyCheckedNodes.find(aNode) != alreadyCheckedNodes.end())
10378 alreadyCheckedNodes.insert(aNode);
10379 SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
10380 while ( backElemItr->more() )
10382 MESSAGE(" backelem ");
10383 const SMDS_MeshElement* curElem = backElemItr->next();
10384 if (alreadyCheckedElems.find(curElem) != alreadyCheckedElems.end())
10386 if (theElems.find(curElem) != theElems.end())
10388 alreadyCheckedElems.insert(curElem);
10389 double x=0, y=0, z=0;
10391 SMDS_ElemIteratorPtr nodeItr2 = curElem->nodesIterator();
10392 while ( nodeItr2->more() )
10394 const SMDS_MeshNode* anotherNode = cast2Node(nodeItr2->next());
10395 x += anotherNode->X();
10396 y += anotherNode->Y();
10397 z += anotherNode->Z();
10401 p.SetCoord( x/nb -aNode->X(),
10403 z/nb -aNode->Z() );
10404 MESSAGE(" check " << p.X() << " " << p.Y() << " " << p.Z());
10407 MESSAGE(" --- inserted")
10408 theAffectedElems.insert( curElem );
10410 else if (curElem->GetType() == SMDSAbs_Edge)
10411 edgesToCheck.insert(curElem);
10415 // --- add also edges lying on the set of faces (all nodes in alreadyCheckedNodes)
10416 std::set<const SMDS_MeshElement*>::iterator eit = edgesToCheck.begin();
10417 for( ; eit != edgesToCheck.end(); eit++)
10419 bool onside = true;
10420 const SMDS_MeshElement* anEdge = *eit;
10421 SMDS_ElemIteratorPtr nodeItr = anEdge->nodesIterator();
10422 while ( nodeItr->more() )
10424 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10425 if (alreadyCheckedNodes.find(aNode) == alreadyCheckedNodes.end())
10433 MESSAGE(" --- edge onside inserted")
10434 theAffectedElems.insert(anEdge);
10440 const double aTol = Precision::Confusion();
10441 auto_ptr< BRepClass3d_SolidClassifier> bsc3d;
10442 auto_ptr<_FaceClassifier> aFaceClassifier;
10443 if ( theShape.ShapeType() == TopAbs_SOLID )
10445 bsc3d.reset( new BRepClass3d_SolidClassifier(theShape));;
10446 bsc3d->PerformInfinitePoint(aTol);
10448 else if (theShape.ShapeType() == TopAbs_FACE )
10450 aFaceClassifier.reset( new _FaceClassifier(TopoDS::Face(theShape)));
10453 // iterates on indicated elements and get elements by back references from their nodes
10454 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10456 for ( ielem = 1; elemItr != theElems.end(); ++elemItr )
10458 MESSAGE("element " << ielem++);
10459 SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
10462 SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
10463 while ( nodeItr->more() )
10465 MESSAGE(" noeud ");
10466 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10467 if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
10469 SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
10470 while ( backElemItr->more() )
10472 MESSAGE(" backelem ");
10473 const SMDS_MeshElement* curElem = backElemItr->next();
10474 if ( curElem && theElems.find(curElem) == theElems.end() &&
10476 isInside( curElem, *bsc3d, aTol ) :
10477 isInside( curElem, *aFaceClassifier, aTol )))
10478 theAffectedElems.insert( curElem );
10486 //================================================================================
10488 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10489 \param theElems - group of of elements (edges or faces) to be replicated
10490 \param theNodesNot - group of nodes not to replicate
10491 \param theShape - shape to detect affected elements (element which geometric center
10492 located on or inside shape).
10493 The replicated nodes should be associated to affected elements.
10494 \return TRUE if operation has been completed successfully, FALSE otherwise
10496 //================================================================================
10498 bool SMESH_MeshEditor::DoubleNodesInRegion( const TIDSortedElemSet& theElems,
10499 const TIDSortedElemSet& theNodesNot,
10500 const TopoDS_Shape& theShape )
10502 if ( theShape.IsNull() )
10505 const double aTol = Precision::Confusion();
10506 auto_ptr< BRepClass3d_SolidClassifier> bsc3d;
10507 auto_ptr<_FaceClassifier> aFaceClassifier;
10508 if ( theShape.ShapeType() == TopAbs_SOLID )
10510 bsc3d.reset( new BRepClass3d_SolidClassifier(theShape));;
10511 bsc3d->PerformInfinitePoint(aTol);
10513 else if (theShape.ShapeType() == TopAbs_FACE )
10515 aFaceClassifier.reset( new _FaceClassifier(TopoDS::Face(theShape)));
10518 // iterates on indicated elements and get elements by back references from their nodes
10519 TIDSortedElemSet anAffected;
10520 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10521 for ( ; elemItr != theElems.end(); ++elemItr )
10523 SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
10527 SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
10528 while ( nodeItr->more() )
10530 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10531 if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
10533 SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
10534 while ( backElemItr->more() )
10536 const SMDS_MeshElement* curElem = backElemItr->next();
10537 if ( curElem && theElems.find(curElem) == theElems.end() &&
10539 isInside( curElem, *bsc3d, aTol ) :
10540 isInside( curElem, *aFaceClassifier, aTol )))
10541 anAffected.insert( curElem );
10545 return DoubleNodes( theElems, theNodesNot, anAffected );
10549 * \brief compute an oriented angle between two planes defined by four points.
10550 * The vector (p0,p1) defines the intersection of the 2 planes (p0,p1,g1) and (p0,p1,g2)
10551 * @param p0 base of the rotation axe
10552 * @param p1 extremity of the rotation axe
10553 * @param g1 belongs to the first plane
10554 * @param g2 belongs to the second plane
10556 double SMESH_MeshEditor::OrientedAngle(const gp_Pnt& p0, const gp_Pnt& p1, const gp_Pnt& g1, const gp_Pnt& g2)
10558 // MESSAGE(" p0: " << p0.X() << " " << p0.Y() << " " << p0.Z());
10559 // MESSAGE(" p1: " << p1.X() << " " << p1.Y() << " " << p1.Z());
10560 // MESSAGE(" g1: " << g1.X() << " " << g1.Y() << " " << g1.Z());
10561 // MESSAGE(" g2: " << g2.X() << " " << g2.Y() << " " << g2.Z());
10562 gp_Vec vref(p0, p1);
10565 gp_Vec n1 = vref.Crossed(v1);
10566 gp_Vec n2 = vref.Crossed(v2);
10568 return n2.AngleWithRef(n1, vref);
10570 catch ( Standard_Failure ) {
10572 return Max( v1.Magnitude(), v2.Magnitude() );
10576 * \brief Double nodes on shared faces between groups of volumes and create flat elements on demand.
10577 * The list of groups must contain at least two groups. The groups have to be disjoint: no common element into two different groups.
10578 * The nodes of the internal faces at the boundaries of the groups are doubled. Optionally, the internal faces are replaced by flat elements.
10579 * Triangles are transformed into prisms, and quadrangles into hexahedrons.
10580 * The flat elements are stored in groups of volumes. These groups are named according to the position of the group in the list:
10581 * 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.
10582 * 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.
10583 * All the flat elements are gathered into the group named "joints3D" (or "joints2D" in 2D situation).
10584 * The flat element of the multiple junctions between the simple junction are stored in a group named "jointsMultiples".
10585 * \param theElems - list of groups of volumes, where a group of volume is a set of
10586 * SMDS_MeshElements sorted by Id.
10587 * \param createJointElems - if TRUE, create the elements
10588 * \param onAllBoundaries - if TRUE, the nodes and elements are also created on
10589 * the boundary between \a theDomains and the rest mesh
10590 * \return TRUE if operation has been completed successfully, FALSE otherwise
10592 bool SMESH_MeshEditor::DoubleNodesOnGroupBoundaries( const std::vector<TIDSortedElemSet>& theElems,
10593 bool createJointElems,
10594 bool onAllBoundaries)
10596 MESSAGE("----------------------------------------------");
10597 MESSAGE("SMESH_MeshEditor::doubleNodesOnGroupBoundaries");
10598 MESSAGE("----------------------------------------------");
10600 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
10601 meshDS->BuildDownWardConnectivity(true);
10603 SMDS_UnstructuredGrid *grid = meshDS->getGrid();
10605 // --- build the list of faces shared by 2 domains (group of elements), with their domain and volume indexes
10606 // build the list of cells with only a node or an edge on the border, with their domain and volume indexes
10607 // build the list of nodes shared by 2 or more domains, with their domain indexes
10609 std::map<DownIdType, std::map<int,int>, DownIdCompare> faceDomains; // face --> (id domain --> id volume)
10610 std::map<int,int>celldom; // cell vtkId --> domain
10611 std::map<DownIdType, std::map<int,int>, DownIdCompare> cellDomains; // oldNode --> (id domain --> id cell)
10612 std::map<int, std::map<int,int> > nodeDomains; // oldId --> (domainId --> newId)
10613 faceDomains.clear();
10615 cellDomains.clear();
10616 nodeDomains.clear();
10617 std::map<int,int> emptyMap;
10618 std::set<int> emptySet;
10621 MESSAGE(".. Number of domains :"<<theElems.size());
10623 TIDSortedElemSet theRestDomElems;
10624 const int iRestDom = -1;
10625 const int idom0 = onAllBoundaries ? iRestDom : 0;
10626 const int nbDomains = theElems.size();
10628 // Check if the domains do not share an element
10629 for (int idom = 0; idom < nbDomains-1; idom++)
10631 // MESSAGE("... Check of domain #" << idom);
10632 const TIDSortedElemSet& domain = theElems[idom];
10633 TIDSortedElemSet::const_iterator elemItr = domain.begin();
10634 for (; elemItr != domain.end(); ++elemItr)
10636 const SMDS_MeshElement* anElem = *elemItr;
10637 int idombisdeb = idom + 1 ;
10638 for (int idombis = idombisdeb; idombis < theElems.size(); idombis++) // check if the element belongs to a domain further in the list
10640 const TIDSortedElemSet& domainbis = theElems[idombis];
10641 if ( domainbis.count(anElem) )
10643 MESSAGE(".... Domain #" << idom);
10644 MESSAGE(".... Domain #" << idombis);
10645 throw SALOME_Exception("The domains are not disjoint.");
10652 for (int idom = 0; idom < nbDomains; idom++)
10655 // --- build a map (face to duplicate --> volume to modify)
10656 // with all the faces shared by 2 domains (group of elements)
10657 // and corresponding volume of this domain, for each shared face.
10658 // a volume has a face shared by 2 domains if it has a neighbor which is not in his domain.
10660 MESSAGE("... Neighbors of domain #" << idom);
10661 const TIDSortedElemSet& domain = theElems[idom];
10662 TIDSortedElemSet::const_iterator elemItr = domain.begin();
10663 for (; elemItr != domain.end(); ++elemItr)
10665 const SMDS_MeshElement* anElem = *elemItr;
10668 int vtkId = anElem->getVtkId();
10669 //MESSAGE(" vtkId " << vtkId << " smdsId " << anElem->GetID());
10670 int neighborsVtkIds[NBMAXNEIGHBORS];
10671 int downIds[NBMAXNEIGHBORS];
10672 unsigned char downTypes[NBMAXNEIGHBORS];
10673 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
10674 for (int n = 0; n < nbNeighbors; n++)
10676 int smdsId = meshDS->fromVtkToSmds(neighborsVtkIds[n]);
10677 const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
10678 if (elem && ! domain.count(elem)) // neighbor is in another domain : face is shared
10681 for (int idombis = 0; idombis < theElems.size() && !ok; idombis++) // check if the neighbor belongs to another domain of the list
10683 // MESSAGE("Domain " << idombis);
10684 const TIDSortedElemSet& domainbis = theElems[idombis];
10685 if ( domainbis.count(elem)) ok = true ; // neighbor is in a correct domain : face is kept
10687 if ( ok || onAllBoundaries ) // the characteristics of the face is stored
10689 DownIdType face(downIds[n], downTypes[n]);
10690 if (!faceDomains[face].count(idom))
10692 faceDomains[face][idom] = vtkId; // volume associated to face in this domain
10693 celldom[vtkId] = idom;
10694 //MESSAGE(" cell with a border " << vtkId << " domain " << idom);
10698 theRestDomElems.insert( elem );
10699 faceDomains[face][iRestDom] = neighborsVtkIds[n];
10700 celldom[neighborsVtkIds[n]] = iRestDom;
10708 //MESSAGE("Number of shared faces " << faceDomains.size());
10709 std::map<DownIdType, std::map<int, int>, DownIdCompare>::iterator itface;
10711 // --- explore the shared faces domain by domain,
10712 // explore the nodes of the face and see if they belong to a cell in the domain,
10713 // which has only a node or an edge on the border (not a shared face)
10715 for (int idomain = idom0; idomain < nbDomains; idomain++)
10717 //MESSAGE("Domain " << idomain);
10718 const TIDSortedElemSet& domain = (idomain == iRestDom) ? theRestDomElems : theElems[idomain];
10719 itface = faceDomains.begin();
10720 for (; itface != faceDomains.end(); ++itface)
10722 const std::map<int, int>& domvol = itface->second;
10723 if (!domvol.count(idomain))
10725 DownIdType face = itface->first;
10726 //MESSAGE(" --- face " << face.cellId);
10727 std::set<int> oldNodes;
10729 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
10730 std::set<int>::iterator itn = oldNodes.begin();
10731 for (; itn != oldNodes.end(); ++itn)
10734 //MESSAGE(" node " << oldId);
10735 vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
10736 for (int i=0; i<l.ncells; i++)
10738 int vtkId = l.cells[i];
10739 const SMDS_MeshElement* anElem = GetMeshDS()->FindElement(GetMeshDS()->fromVtkToSmds(vtkId));
10740 if (!domain.count(anElem))
10742 int vtkType = grid->GetCellType(vtkId);
10743 int downId = grid->CellIdToDownId(vtkId);
10746 MESSAGE("doubleNodesOnGroupBoundaries: internal algorithm problem");
10747 continue; // not OK at this stage of the algorithm:
10748 //no cells created after BuildDownWardConnectivity
10750 DownIdType aCell(downId, vtkType);
10751 cellDomains[aCell][idomain] = vtkId;
10752 celldom[vtkId] = idomain;
10753 //MESSAGE(" cell " << vtkId << " domain " << idomain);
10759 // --- explore the shared faces domain by domain, to duplicate the nodes in a coherent way
10760 // for each shared face, get the nodes
10761 // for each node, for each domain of the face, create a clone of the node
10763 // --- edges at the intersection of 3 or 4 domains, with the order of domains to build
10764 // junction elements of type prism or hexa. the key is the pair of nodesId (lower first)
10765 // the value is the ordered domain ids. (more than 4 domains not taken into account)
10767 std::map<std::vector<int>, std::vector<int> > edgesMultiDomains; // nodes of edge --> ordered domains
10768 std::map<int, std::vector<int> > mutipleNodes; // nodes multi domains with domain order
10769 std::map<int, std::vector<int> > mutipleNodesToFace; // nodes multi domains with domain order to transform in Face (junction between 3 or more 2D domains)
10771 MESSAGE(".. Duplication of the nodes");
10772 for (int idomain = idom0; idomain < nbDomains; idomain++)
10774 itface = faceDomains.begin();
10775 for (; itface != faceDomains.end(); ++itface)
10777 const std::map<int, int>& domvol = itface->second;
10778 if (!domvol.count(idomain))
10780 DownIdType face = itface->first;
10781 //MESSAGE(" --- face " << face.cellId);
10782 std::set<int> oldNodes;
10784 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
10785 std::set<int>::iterator itn = oldNodes.begin();
10786 for (; itn != oldNodes.end(); ++itn)
10789 if (nodeDomains[oldId].empty())
10791 nodeDomains[oldId][idomain] = oldId; // keep the old node in the first domain
10792 //MESSAGE("-+-+-b oldNode " << oldId << " domain " << idomain);
10794 std::map<int, int>::const_iterator itdom = domvol.begin();
10795 for (; itdom != domvol.end(); ++itdom)
10797 int idom = itdom->first;
10798 //MESSAGE(" domain " << idom);
10799 if (!nodeDomains[oldId].count(idom)) // --- node to clone
10801 if (nodeDomains[oldId].size() >= 2) // a multiple node
10803 vector<int> orderedDoms;
10804 //MESSAGE("multiple node " << oldId);
10805 if (mutipleNodes.count(oldId))
10806 orderedDoms = mutipleNodes[oldId];
10809 map<int,int>::iterator it = nodeDomains[oldId].begin();
10810 for (; it != nodeDomains[oldId].end(); ++it)
10811 orderedDoms.push_back(it->first);
10813 orderedDoms.push_back(idom); // TODO order ==> push_front or back
10814 //stringstream txt;
10815 //for (int i=0; i<orderedDoms.size(); i++)
10816 // txt << orderedDoms[i] << " ";
10817 //MESSAGE("orderedDoms " << txt.str());
10818 mutipleNodes[oldId] = orderedDoms;
10820 double *coords = grid->GetPoint(oldId);
10821 SMDS_MeshNode *newNode = meshDS->AddNode(coords[0], coords[1], coords[2]);
10822 int newId = newNode->getVtkId();
10823 nodeDomains[oldId][idom] = newId; // cloned node for other domains
10824 //MESSAGE("-+-+-c oldNode " << oldId << " domain " << idomain << " newNode " << newId << " domain " << idom << " size=" <<nodeDomains[oldId].size());
10831 MESSAGE(".. Creation of elements");
10832 for (int idomain = idom0; idomain < nbDomains; idomain++)
10834 itface = faceDomains.begin();
10835 for (; itface != faceDomains.end(); ++itface)
10837 std::map<int, int> domvol = itface->second;
10838 if (!domvol.count(idomain))
10840 DownIdType face = itface->first;
10841 //MESSAGE(" --- face " << face.cellId);
10842 std::set<int> oldNodes;
10844 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
10845 int nbMultipleNodes = 0;
10846 std::set<int>::iterator itn = oldNodes.begin();
10847 for (; itn != oldNodes.end(); ++itn)
10850 if (mutipleNodes.count(oldId))
10853 if (nbMultipleNodes > 1) // check if an edge of the face is shared between 3 or more domains
10855 //MESSAGE("multiple Nodes detected on a shared face");
10856 int downId = itface->first.cellId;
10857 unsigned char cellType = itface->first.cellType;
10858 // --- shared edge or shared face ?
10859 if ((cellType == VTK_LINE) || (cellType == VTK_QUADRATIC_EDGE)) // shared edge (between two faces)
10862 int nbNodes = grid->getDownArray(cellType)->getNodes(downId, nodes);
10863 for (int i=0; i< nbNodes; i=i+nbNodes-1) // i=0 , i=nbNodes-1
10864 if (mutipleNodes.count(nodes[i]))
10865 if (!mutipleNodesToFace.count(nodes[i]))
10866 mutipleNodesToFace[nodes[i]] = mutipleNodes[nodes[i]];
10868 else // shared face (between two volumes)
10870 int nbEdges = grid->getDownArray(cellType)->getNumberOfDownCells(downId);
10871 const int* downEdgeIds = grid->getDownArray(cellType)->getDownCells(downId);
10872 const unsigned char* edgeType = grid->getDownArray(cellType)->getDownTypes(downId);
10873 for (int ie =0; ie < nbEdges; ie++)
10876 int nbNodes = grid->getDownArray(edgeType[ie])->getNodes(downEdgeIds[ie], nodes);
10877 if (mutipleNodes.count(nodes[0]) && mutipleNodes.count(nodes[nbNodes-1]))
10879 vector<int> vn0 = mutipleNodes[nodes[0]];
10880 vector<int> vn1 = mutipleNodes[nodes[nbNodes - 1]];
10882 for (int i0 = 0; i0 < vn0.size(); i0++)
10883 for (int i1 = 0; i1 < vn1.size(); i1++)
10884 if (vn0[i0] == vn1[i1])
10885 doms.push_back(vn0[i0]);
10886 if (doms.size() >2)
10888 //MESSAGE(" detect edgesMultiDomains " << nodes[0] << " " << nodes[nbNodes - 1]);
10889 double *coords = grid->GetPoint(nodes[0]);
10890 gp_Pnt p0(coords[0], coords[1], coords[2]);
10891 coords = grid->GetPoint(nodes[nbNodes - 1]);
10892 gp_Pnt p1(coords[0], coords[1], coords[2]);
10894 int vtkVolIds[1000]; // an edge can belong to a lot of volumes
10895 map<int, SMDS_VtkVolume*> domvol; // domain --> a volume with the edge
10896 map<int, double> angleDom; // oriented angles between planes defined by edge and volume centers
10897 int nbvol = grid->GetParentVolumes(vtkVolIds, downEdgeIds[ie], edgeType[ie]);
10898 for (int id=0; id < doms.size(); id++)
10900 int idom = doms[id];
10901 const TIDSortedElemSet& domain = (idom == iRestDom) ? theRestDomElems : theElems[idom];
10902 for (int ivol=0; ivol<nbvol; ivol++)
10904 int smdsId = meshDS->fromVtkToSmds(vtkVolIds[ivol]);
10905 SMDS_MeshElement* elem = (SMDS_MeshElement*)meshDS->FindElement(smdsId);
10906 if (domain.count(elem))
10908 SMDS_VtkVolume* svol = dynamic_cast<SMDS_VtkVolume*>(elem);
10909 domvol[idom] = svol;
10910 //MESSAGE(" domain " << idom << " volume " << elem->GetID());
10912 vtkIdType npts = 0;
10913 vtkIdType* pts = 0;
10914 grid->GetCellPoints(vtkVolIds[ivol], npts, pts);
10915 SMDS_VtkVolume::gravityCenter(grid, pts, npts, values);
10918 gref.SetXYZ(gp_XYZ(values[0], values[1], values[2]));
10919 angleDom[idom] = 0;
10923 gp_Pnt g(values[0], values[1], values[2]);
10924 angleDom[idom] = OrientedAngle(p0, p1, gref, g); // -pi<angle<+pi
10925 //MESSAGE(" angle=" << angleDom[idom]);
10931 map<double, int> sortedDom; // sort domains by angle
10932 for (map<int, double>::iterator ia = angleDom.begin(); ia != angleDom.end(); ++ia)
10933 sortedDom[ia->second] = ia->first;
10934 vector<int> vnodes;
10936 for (map<double, int>::iterator ib = sortedDom.begin(); ib != sortedDom.end(); ++ib)
10938 vdom.push_back(ib->second);
10939 //MESSAGE(" ordered domain " << ib->second << " angle " << ib->first);
10941 for (int ino = 0; ino < nbNodes; ino++)
10942 vnodes.push_back(nodes[ino]);
10943 edgesMultiDomains[vnodes] = vdom; // nodes vector --> ordered domains
10952 // --- iterate on shared faces (volumes to modify, face to extrude)
10953 // get node id's of the face (id SMDS = id VTK)
10954 // create flat element with old and new nodes if requested
10956 // --- new quad nodes on flat quad elements: oldId --> ((domain1 X domain2) --> newId)
10957 // (domain1 X domain2) = domain1 + MAXINT*domain2
10959 std::map<int, std::map<long,int> > nodeQuadDomains;
10960 std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
10962 MESSAGE(".. Creation of elements: simple junction");
10963 if (createJointElems)
10966 string joints2DName = "joints2D";
10967 mapOfJunctionGroups[joints2DName] = this->myMesh->AddGroup(SMDSAbs_Face, joints2DName.c_str(), idg);
10968 SMESHDS_Group *joints2DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints2DName]->GetGroupDS());
10969 string joints3DName = "joints3D";
10970 mapOfJunctionGroups[joints3DName] = this->myMesh->AddGroup(SMDSAbs_Volume, joints3DName.c_str(), idg);
10971 SMESHDS_Group *joints3DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints3DName]->GetGroupDS());
10973 itface = faceDomains.begin();
10974 for (; itface != faceDomains.end(); ++itface)
10976 DownIdType face = itface->first;
10977 std::set<int> oldNodes;
10978 std::set<int>::iterator itn;
10980 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
10982 std::map<int, int> domvol = itface->second;
10983 std::map<int, int>::iterator itdom = domvol.begin();
10984 int dom1 = itdom->first;
10985 int vtkVolId = itdom->second;
10987 int dom2 = itdom->first;
10988 SMDS_MeshCell *vol = grid->extrudeVolumeFromFace(vtkVolId, dom1, dom2, oldNodes, nodeDomains,
10990 stringstream grpname;
10993 grpname << dom1 << "_" << dom2;
10995 grpname << dom2 << "_" << dom1;
10996 string namegrp = grpname.str();
10997 if (!mapOfJunctionGroups.count(namegrp))
10998 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(vol->GetType(), namegrp.c_str(), idg);
10999 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11001 sgrp->Add(vol->GetID());
11002 if (vol->GetType() == SMDSAbs_Volume)
11003 joints3DGrp->Add(vol->GetID());
11004 else if (vol->GetType() == SMDSAbs_Face)
11005 joints2DGrp->Add(vol->GetID());
11009 // --- create volumes on multiple domain intersection if requested
11010 // iterate on mutipleNodesToFace
11011 // iterate on edgesMultiDomains
11013 MESSAGE(".. Creation of elements: multiple junction");
11014 if (createJointElems)
11016 // --- iterate on mutipleNodesToFace
11018 std::map<int, std::vector<int> >::iterator itn = mutipleNodesToFace.begin();
11019 for (; itn != mutipleNodesToFace.end(); ++itn)
11021 int node = itn->first;
11022 vector<int> orderDom = itn->second;
11023 vector<vtkIdType> orderedNodes;
11024 for (int idom = 0; idom <orderDom.size(); idom++)
11025 orderedNodes.push_back( nodeDomains[node][orderDom[idom]] );
11026 SMDS_MeshFace* face = this->GetMeshDS()->AddFaceFromVtkIds(orderedNodes);
11028 stringstream grpname;
11030 grpname << 0 << "_" << 0;
11032 string namegrp = grpname.str();
11033 if (!mapOfJunctionGroups.count(namegrp))
11034 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Face, namegrp.c_str(), idg);
11035 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11037 sgrp->Add(face->GetID());
11040 // --- iterate on edgesMultiDomains
11042 std::map<std::vector<int>, std::vector<int> >::iterator ite = edgesMultiDomains.begin();
11043 for (; ite != edgesMultiDomains.end(); ++ite)
11045 vector<int> nodes = ite->first;
11046 vector<int> orderDom = ite->second;
11047 vector<vtkIdType> orderedNodes;
11048 if (nodes.size() == 2)
11050 //MESSAGE(" use edgesMultiDomains " << nodes[0] << " " << nodes[1]);
11051 for (int ino=0; ino < nodes.size(); ino++)
11052 if (orderDom.size() == 3)
11053 for (int idom = 0; idom <orderDom.size(); idom++)
11054 orderedNodes.push_back( nodeDomains[nodes[ino]][orderDom[idom]] );
11056 for (int idom = orderDom.size()-1; idom >=0; idom--)
11057 orderedNodes.push_back( nodeDomains[nodes[ino]][orderDom[idom]] );
11058 SMDS_MeshVolume* vol = this->GetMeshDS()->AddVolumeFromVtkIds(orderedNodes);
11061 string namegrp = "jointsMultiples";
11062 if (!mapOfJunctionGroups.count(namegrp))
11063 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
11064 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11066 sgrp->Add(vol->GetID());
11070 INFOS("Quadratic multiple joints not implemented");
11071 // TODO quadratic nodes
11076 // --- list the explicit faces and edges of the mesh that need to be modified,
11077 // i.e. faces and edges built with one or more duplicated nodes.
11078 // associate these faces or edges to their corresponding domain.
11079 // only the first domain found is kept when a face or edge is shared
11081 std::map<DownIdType, std::map<int,int>, DownIdCompare> faceOrEdgeDom; // cellToModify --> (id domain --> id cell)
11082 std::map<int,int> feDom; // vtk id of cell to modify --> id domain
11083 faceOrEdgeDom.clear();
11086 MESSAGE(".. Modification of elements");
11087 for (int idomain = idom0; idomain < nbDomains; idomain++)
11089 std::map<int, std::map<int, int> >::const_iterator itnod = nodeDomains.begin();
11090 for (; itnod != nodeDomains.end(); ++itnod)
11092 int oldId = itnod->first;
11093 //MESSAGE(" node " << oldId);
11094 vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
11095 for (int i = 0; i < l.ncells; i++)
11097 int vtkId = l.cells[i];
11098 int vtkType = grid->GetCellType(vtkId);
11099 int downId = grid->CellIdToDownId(vtkId);
11101 continue; // new cells: not to be modified
11102 DownIdType aCell(downId, vtkType);
11103 int volParents[1000];
11104 int nbvol = grid->GetParentVolumes(volParents, vtkId);
11105 for (int j = 0; j < nbvol; j++)
11106 if (celldom.count(volParents[j]) && (celldom[volParents[j]] == idomain))
11107 if (!feDom.count(vtkId))
11109 feDom[vtkId] = idomain;
11110 faceOrEdgeDom[aCell] = emptyMap;
11111 faceOrEdgeDom[aCell][idomain] = vtkId; // affect face or edge to the first domain only
11112 //MESSAGE("affect cell " << this->GetMeshDS()->fromVtkToSmds(vtkId) << " domain " << idomain
11113 // << " type " << vtkType << " downId " << downId);
11119 // --- iterate on shared faces (volumes to modify, face to extrude)
11120 // get node id's of the face
11121 // replace old nodes by new nodes in volumes, and update inverse connectivity
11123 std::map<DownIdType, std::map<int,int>, DownIdCompare>* maps[3] = {&faceDomains, &cellDomains, &faceOrEdgeDom};
11124 for (int m=0; m<3; m++)
11126 std::map<DownIdType, std::map<int,int>, DownIdCompare>* amap = maps[m];
11127 itface = (*amap).begin();
11128 for (; itface != (*amap).end(); ++itface)
11130 DownIdType face = itface->first;
11131 std::set<int> oldNodes;
11132 std::set<int>::iterator itn;
11134 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11135 //MESSAGE("examine cell, downId " << face.cellId << " type " << int(face.cellType));
11136 std::map<int, int> localClonedNodeIds;
11138 std::map<int, int> domvol = itface->second;
11139 std::map<int, int>::iterator itdom = domvol.begin();
11140 for (; itdom != domvol.end(); ++itdom)
11142 int idom = itdom->first;
11143 int vtkVolId = itdom->second;
11144 //MESSAGE("modify nodes of cell " << this->GetMeshDS()->fromVtkToSmds(vtkVolId) << " domain " << idom);
11145 localClonedNodeIds.clear();
11146 for (itn = oldNodes.begin(); itn != oldNodes.end(); ++itn)
11149 if (nodeDomains[oldId].count(idom))
11151 localClonedNodeIds[oldId] = nodeDomains[oldId][idom];
11152 //MESSAGE(" node " << oldId << " --> " << localClonedNodeIds[oldId]);
11155 meshDS->ModifyCellNodes(vtkVolId, localClonedNodeIds);
11160 meshDS->CleanDownWardConnectivity(); // Mesh has been modified, downward connectivity is no more usable, free memory
11161 grid->BuildLinks();
11169 * \brief Double nodes on some external faces and create flat elements.
11170 * Flat elements are mainly used by some types of mechanic calculations.
11172 * Each group of the list must be constituted of faces.
11173 * Triangles are transformed in prisms, and quadrangles in hexahedrons.
11174 * @param theElems - list of groups of faces, where a group of faces is a set of
11175 * SMDS_MeshElements sorted by Id.
11176 * @return TRUE if operation has been completed successfully, FALSE otherwise
11178 bool SMESH_MeshEditor::CreateFlatElementsOnFacesGroups(const std::vector<TIDSortedElemSet>& theElems)
11180 MESSAGE("-------------------------------------------------");
11181 MESSAGE("SMESH_MeshEditor::CreateFlatElementsOnFacesGroups");
11182 MESSAGE("-------------------------------------------------");
11184 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11186 // --- For each group of faces
11187 // duplicate the nodes, create a flat element based on the face
11188 // replace the nodes of the faces by their clones
11190 std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> clonedNodes;
11191 std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> intermediateNodes;
11192 clonedNodes.clear();
11193 intermediateNodes.clear();
11194 std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11195 mapOfJunctionGroups.clear();
11197 for (int idom = 0; idom < theElems.size(); idom++)
11199 const TIDSortedElemSet& domain = theElems[idom];
11200 TIDSortedElemSet::const_iterator elemItr = domain.begin();
11201 for (; elemItr != domain.end(); ++elemItr)
11203 SMDS_MeshElement* anElem = (SMDS_MeshElement*) *elemItr;
11204 SMDS_MeshFace* aFace = dynamic_cast<SMDS_MeshFace*> (anElem);
11207 // MESSAGE("aFace=" << aFace->GetID());
11208 bool isQuad = aFace->IsQuadratic();
11209 vector<const SMDS_MeshNode*> ln0, ln1, ln2, ln3, ln4;
11211 // --- clone the nodes, create intermediate nodes for non medium nodes of a quad face
11213 SMDS_ElemIteratorPtr nodeIt = aFace->nodesIterator();
11214 while (nodeIt->more())
11216 const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*> (nodeIt->next());
11217 bool isMedium = isQuad && (aFace->IsMediumNode(node));
11219 ln2.push_back(node);
11221 ln0.push_back(node);
11223 const SMDS_MeshNode* clone = 0;
11224 if (!clonedNodes.count(node))
11226 clone = meshDS->AddNode(node->X(), node->Y(), node->Z());
11227 clonedNodes[node] = clone;
11230 clone = clonedNodes[node];
11233 ln3.push_back(clone);
11235 ln1.push_back(clone);
11237 const SMDS_MeshNode* inter = 0;
11238 if (isQuad && (!isMedium))
11240 if (!intermediateNodes.count(node))
11242 inter = meshDS->AddNode(node->X(), node->Y(), node->Z());
11243 intermediateNodes[node] = inter;
11246 inter = intermediateNodes[node];
11247 ln4.push_back(inter);
11251 // --- extrude the face
11253 vector<const SMDS_MeshNode*> ln;
11254 SMDS_MeshVolume* vol = 0;
11255 vtkIdType aType = aFace->GetVtkType();
11259 vol = meshDS->AddVolume(ln0[2], ln0[1], ln0[0], ln1[2], ln1[1], ln1[0]);
11260 // MESSAGE("vol prism " << vol->GetID());
11261 ln.push_back(ln1[0]);
11262 ln.push_back(ln1[1]);
11263 ln.push_back(ln1[2]);
11266 vol = meshDS->AddVolume(ln0[3], ln0[2], ln0[1], ln0[0], ln1[3], ln1[2], ln1[1], ln1[0]);
11267 // MESSAGE("vol hexa " << vol->GetID());
11268 ln.push_back(ln1[0]);
11269 ln.push_back(ln1[1]);
11270 ln.push_back(ln1[2]);
11271 ln.push_back(ln1[3]);
11273 case VTK_QUADRATIC_TRIANGLE:
11274 vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln0[0], ln0[1], ln0[2], ln3[0], ln3[1], ln3[2],
11275 ln2[0], ln2[1], ln2[2], ln4[0], ln4[1], ln4[2]);
11276 // MESSAGE("vol quad prism " << vol->GetID());
11277 ln.push_back(ln1[0]);
11278 ln.push_back(ln1[1]);
11279 ln.push_back(ln1[2]);
11280 ln.push_back(ln3[0]);
11281 ln.push_back(ln3[1]);
11282 ln.push_back(ln3[2]);
11284 case VTK_QUADRATIC_QUAD:
11285 // vol = meshDS->AddVolume(ln0[0], ln0[1], ln0[2], ln0[3], ln1[0], ln1[1], ln1[2], ln1[3],
11286 // ln2[0], ln2[1], ln2[2], ln2[3], ln3[0], ln3[1], ln3[2], ln3[3],
11287 // ln4[0], ln4[1], ln4[2], ln4[3]);
11288 vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln1[3], ln0[0], ln0[1], ln0[2], ln0[3],
11289 ln3[0], ln3[1], ln3[2], ln3[3], ln2[0], ln2[1], ln2[2], ln2[3],
11290 ln4[0], ln4[1], ln4[2], ln4[3]);
11291 // MESSAGE("vol quad hexa " << vol->GetID());
11292 ln.push_back(ln1[0]);
11293 ln.push_back(ln1[1]);
11294 ln.push_back(ln1[2]);
11295 ln.push_back(ln1[3]);
11296 ln.push_back(ln3[0]);
11297 ln.push_back(ln3[1]);
11298 ln.push_back(ln3[2]);
11299 ln.push_back(ln3[3]);
11309 stringstream grpname;
11313 string namegrp = grpname.str();
11314 if (!mapOfJunctionGroups.count(namegrp))
11315 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
11316 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11318 sgrp->Add(vol->GetID());
11321 // --- modify the face
11323 aFace->ChangeNodes(&ln[0], ln.size());
11330 * \brief identify all the elements around a geom shape, get the faces delimiting the hole
11331 * Build groups of volume to remove, groups of faces to replace on the skin of the object,
11332 * groups of faces to remove inside the object, (idem edges).
11333 * Build ordered list of nodes at the border of each group of faces to replace (to be used to build a geom subshape)
11335 void SMESH_MeshEditor::CreateHoleSkin(double radius,
11336 const TopoDS_Shape& theShape,
11337 SMESH_NodeSearcher* theNodeSearcher,
11338 const char* groupName,
11339 std::vector<double>& nodesCoords,
11340 std::vector<std::vector<int> >& listOfListOfNodes)
11342 MESSAGE("--------------------------------");
11343 MESSAGE("SMESH_MeshEditor::CreateHoleSkin");
11344 MESSAGE("--------------------------------");
11346 // --- zone of volumes to remove is given :
11347 // 1 either by a geom shape (one or more vertices) and a radius,
11348 // 2 either by a group of nodes (representative of the shape)to use with the radius,
11349 // 3 either by a group of nodes where all the elements build on one of this nodes are to remove,
11350 // In the case 2, the group of nodes is an external group of nodes from another mesh,
11351 // In the case 3, the group of nodes is an internal group of the mesh (obtained for instance by a filter),
11352 // defined by it's name.
11354 SMESHDS_GroupBase* groupDS = 0;
11355 SMESH_Mesh::GroupIteratorPtr groupIt = this->myMesh->GetGroups();
11356 while ( groupIt->more() )
11359 SMESH_Group * group = groupIt->next();
11360 if ( !group ) continue;
11361 groupDS = group->GetGroupDS();
11362 if ( !groupDS || groupDS->IsEmpty() ) continue;
11363 std::string grpName = group->GetName();
11364 //MESSAGE("grpName=" << grpName);
11365 if (grpName == groupName)
11371 bool isNodeGroup = false;
11372 bool isNodeCoords = false;
11375 if (groupDS->GetType() != SMDSAbs_Node)
11377 isNodeGroup = true; // a group of nodes exists and it is in this mesh
11380 if (nodesCoords.size() > 0)
11381 isNodeCoords = true; // a list o nodes given by their coordinates
11382 //MESSAGE("---" << isNodeGroup << " " << isNodeCoords);
11384 // --- define groups to build
11386 int idg; // --- group of SMDS volumes
11387 string grpvName = groupName;
11388 grpvName += "_vol";
11389 SMESH_Group *grp = this->myMesh->AddGroup(SMDSAbs_Volume, grpvName.c_str(), idg);
11392 MESSAGE("group not created " << grpvName);
11395 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(grp->GetGroupDS());
11397 int idgs; // --- group of SMDS faces on the skin
11398 string grpsName = groupName;
11399 grpsName += "_skin";
11400 SMESH_Group *grps = this->myMesh->AddGroup(SMDSAbs_Face, grpsName.c_str(), idgs);
11403 MESSAGE("group not created " << grpsName);
11406 SMESHDS_Group *sgrps = dynamic_cast<SMESHDS_Group*>(grps->GetGroupDS());
11408 int idgi; // --- group of SMDS faces internal (several shapes)
11409 string grpiName = groupName;
11410 grpiName += "_internalFaces";
11411 SMESH_Group *grpi = this->myMesh->AddGroup(SMDSAbs_Face, grpiName.c_str(), idgi);
11414 MESSAGE("group not created " << grpiName);
11417 SMESHDS_Group *sgrpi = dynamic_cast<SMESHDS_Group*>(grpi->GetGroupDS());
11419 int idgei; // --- group of SMDS faces internal (several shapes)
11420 string grpeiName = groupName;
11421 grpeiName += "_internalEdges";
11422 SMESH_Group *grpei = this->myMesh->AddGroup(SMDSAbs_Edge, grpeiName.c_str(), idgei);
11425 MESSAGE("group not created " << grpeiName);
11428 SMESHDS_Group *sgrpei = dynamic_cast<SMESHDS_Group*>(grpei->GetGroupDS());
11430 // --- build downward connectivity
11432 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11433 meshDS->BuildDownWardConnectivity(true);
11434 SMDS_UnstructuredGrid* grid = meshDS->getGrid();
11436 // --- set of volumes detected inside
11438 std::set<int> setOfInsideVol;
11439 std::set<int> setOfVolToCheck;
11441 std::vector<gp_Pnt> gpnts;
11444 if (isNodeGroup) // --- a group of nodes is provided : find all the volumes using one or more of this nodes
11446 MESSAGE("group of nodes provided");
11447 SMDS_ElemIteratorPtr elemIt = groupDS->GetElements();
11448 while ( elemIt->more() )
11450 const SMDS_MeshElement* elem = elemIt->next();
11453 const SMDS_MeshNode* node = dynamic_cast<const SMDS_MeshNode*>(elem);
11456 SMDS_MeshElement* vol = 0;
11457 SMDS_ElemIteratorPtr volItr = node->GetInverseElementIterator(SMDSAbs_Volume);
11458 while (volItr->more())
11460 vol = (SMDS_MeshElement*)volItr->next();
11461 setOfInsideVol.insert(vol->getVtkId());
11462 sgrp->Add(vol->GetID());
11466 else if (isNodeCoords)
11468 MESSAGE("list of nodes coordinates provided");
11471 while (i < nodesCoords.size()-2)
11473 double x = nodesCoords[i++];
11474 double y = nodesCoords[i++];
11475 double z = nodesCoords[i++];
11476 gp_Pnt p = gp_Pnt(x, y ,z);
11477 gpnts.push_back(p);
11478 MESSAGE("TopoDS_Vertex " << k << " " << p.X() << " " << p.Y() << " " << p.Z());
11482 else // --- no group, no coordinates : use the vertices of the geom shape provided, and radius
11484 MESSAGE("no group of nodes provided, using vertices from geom shape, and radius");
11485 TopTools_IndexedMapOfShape vertexMap;
11486 TopExp::MapShapes( theShape, TopAbs_VERTEX, vertexMap );
11487 gp_Pnt p = gp_Pnt(0,0,0);
11488 if (vertexMap.Extent() < 1)
11491 for ( int i = 1; i <= vertexMap.Extent(); ++i )
11493 const TopoDS_Vertex& vertex = TopoDS::Vertex( vertexMap( i ));
11494 p = BRep_Tool::Pnt(vertex);
11495 gpnts.push_back(p);
11496 MESSAGE("TopoDS_Vertex " << i << " " << p.X() << " " << p.Y() << " " << p.Z());
11500 if (gpnts.size() > 0)
11503 const SMDS_MeshNode* startNode = theNodeSearcher->FindClosestTo(gpnts[0]);
11505 nodeId = startNode->GetID();
11506 MESSAGE("nodeId " << nodeId);
11508 double radius2 = radius*radius;
11509 MESSAGE("radius2 " << radius2);
11511 // --- volumes on start node
11513 setOfVolToCheck.clear();
11514 SMDS_MeshElement* startVol = 0;
11515 SMDS_ElemIteratorPtr volItr = startNode->GetInverseElementIterator(SMDSAbs_Volume);
11516 while (volItr->more())
11518 startVol = (SMDS_MeshElement*)volItr->next();
11519 setOfVolToCheck.insert(startVol->getVtkId());
11521 if (setOfVolToCheck.empty())
11523 MESSAGE("No volumes found");
11527 // --- starting with central volumes then their neighbors, check if they are inside
11528 // or outside the domain, until no more new neighbor volume is inside.
11529 // Fill the group of inside volumes
11531 std::map<int, double> mapOfNodeDistance2;
11532 mapOfNodeDistance2.clear();
11533 std::set<int> setOfOutsideVol;
11534 while (!setOfVolToCheck.empty())
11536 std::set<int>::iterator it = setOfVolToCheck.begin();
11538 MESSAGE("volume to check, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
11539 bool volInside = false;
11540 vtkIdType npts = 0;
11541 vtkIdType* pts = 0;
11542 grid->GetCellPoints(vtkId, npts, pts);
11543 for (int i=0; i<npts; i++)
11545 double distance2 = 0;
11546 if (mapOfNodeDistance2.count(pts[i]))
11548 distance2 = mapOfNodeDistance2[pts[i]];
11549 MESSAGE("point " << pts[i] << " distance2 " << distance2);
11553 double *coords = grid->GetPoint(pts[i]);
11554 gp_Pnt aPoint = gp_Pnt(coords[0], coords[1], coords[2]);
11556 for (int j=0; j<gpnts.size(); j++)
11558 double d2 = aPoint.SquareDistance(gpnts[j]);
11559 if (d2 < distance2)
11562 if (distance2 < radius2)
11566 mapOfNodeDistance2[pts[i]] = distance2;
11567 MESSAGE(" point " << pts[i] << " distance2 " << distance2 << " coords " << coords[0] << " " << coords[1] << " " << coords[2]);
11569 if (distance2 < radius2)
11571 volInside = true; // one or more nodes inside the domain
11572 sgrp->Add(meshDS->fromVtkToSmds(vtkId));
11578 setOfInsideVol.insert(vtkId);
11579 MESSAGE(" volume inside, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
11580 int neighborsVtkIds[NBMAXNEIGHBORS];
11581 int downIds[NBMAXNEIGHBORS];
11582 unsigned char downTypes[NBMAXNEIGHBORS];
11583 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
11584 for (int n = 0; n < nbNeighbors; n++)
11585 if (!setOfInsideVol.count(neighborsVtkIds[n]) ||setOfOutsideVol.count(neighborsVtkIds[n]))
11586 setOfVolToCheck.insert(neighborsVtkIds[n]);
11590 setOfOutsideVol.insert(vtkId);
11591 MESSAGE(" volume outside, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
11593 setOfVolToCheck.erase(vtkId);
11597 // --- for outside hexahedrons, check if they have more than one neighbor volume inside
11598 // If yes, add the volume to the inside set
11600 bool addedInside = true;
11601 std::set<int> setOfVolToReCheck;
11602 while (addedInside)
11604 MESSAGE(" --------------------------- re check");
11605 addedInside = false;
11606 std::set<int>::iterator itv = setOfInsideVol.begin();
11607 for (; itv != setOfInsideVol.end(); ++itv)
11610 int neighborsVtkIds[NBMAXNEIGHBORS];
11611 int downIds[NBMAXNEIGHBORS];
11612 unsigned char downTypes[NBMAXNEIGHBORS];
11613 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
11614 for (int n = 0; n < nbNeighbors; n++)
11615 if (!setOfInsideVol.count(neighborsVtkIds[n]))
11616 setOfVolToReCheck.insert(neighborsVtkIds[n]);
11618 setOfVolToCheck = setOfVolToReCheck;
11619 setOfVolToReCheck.clear();
11620 while (!setOfVolToCheck.empty())
11622 std::set<int>::iterator it = setOfVolToCheck.begin();
11624 if (grid->GetCellType(vtkId) == VTK_HEXAHEDRON)
11626 MESSAGE("volume to recheck, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
11627 int countInside = 0;
11628 int neighborsVtkIds[NBMAXNEIGHBORS];
11629 int downIds[NBMAXNEIGHBORS];
11630 unsigned char downTypes[NBMAXNEIGHBORS];
11631 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
11632 for (int n = 0; n < nbNeighbors; n++)
11633 if (setOfInsideVol.count(neighborsVtkIds[n]))
11635 MESSAGE("countInside " << countInside);
11636 if (countInside > 1)
11638 MESSAGE(" volume inside, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
11639 setOfInsideVol.insert(vtkId);
11640 sgrp->Add(meshDS->fromVtkToSmds(vtkId));
11641 addedInside = true;
11644 setOfVolToReCheck.insert(vtkId);
11646 setOfVolToCheck.erase(vtkId);
11650 // --- map of Downward faces at the boundary, inside the global volume
11651 // map of Downward faces on the skin of the global volume (equivalent to SMDS faces on the skin)
11652 // fill group of SMDS faces inside the volume (when several volume shapes)
11653 // fill group of SMDS faces on the skin of the global volume (if skin)
11655 std::map<DownIdType, int, DownIdCompare> boundaryFaces; // boundary faces inside the volume --> corresponding cell
11656 std::map<DownIdType, int, DownIdCompare> skinFaces; // faces on the skin of the global volume --> corresponding cell
11657 std::set<int>::iterator it = setOfInsideVol.begin();
11658 for (; it != setOfInsideVol.end(); ++it)
11661 //MESSAGE(" vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
11662 int neighborsVtkIds[NBMAXNEIGHBORS];
11663 int downIds[NBMAXNEIGHBORS];
11664 unsigned char downTypes[NBMAXNEIGHBORS];
11665 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId, true);
11666 for (int n = 0; n < nbNeighbors; n++)
11668 int neighborDim = SMDS_Downward::getCellDimension(grid->GetCellType(neighborsVtkIds[n]));
11669 if (neighborDim == 3)
11671 if (! setOfInsideVol.count(neighborsVtkIds[n])) // neighbor volume is not inside : face is boundary
11673 DownIdType face(downIds[n], downTypes[n]);
11674 boundaryFaces[face] = vtkId;
11676 // if the face between to volumes is in the mesh, get it (internal face between shapes)
11677 int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
11678 if (vtkFaceId >= 0)
11680 sgrpi->Add(meshDS->fromVtkToSmds(vtkFaceId));
11681 // find also the smds edges on this face
11682 int nbEdges = grid->getDownArray(downTypes[n])->getNumberOfDownCells(downIds[n]);
11683 const int* dEdges = grid->getDownArray(downTypes[n])->getDownCells(downIds[n]);
11684 const unsigned char* dTypes = grid->getDownArray(downTypes[n])->getDownTypes(downIds[n]);
11685 for (int i = 0; i < nbEdges; i++)
11687 int vtkEdgeId = grid->getDownArray(dTypes[i])->getVtkCellId(dEdges[i]);
11688 if (vtkEdgeId >= 0)
11689 sgrpei->Add(meshDS->fromVtkToSmds(vtkEdgeId));
11693 else if (neighborDim == 2) // skin of the volume
11695 DownIdType face(downIds[n], downTypes[n]);
11696 skinFaces[face] = vtkId;
11697 int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
11698 if (vtkFaceId >= 0)
11699 sgrps->Add(meshDS->fromVtkToSmds(vtkFaceId));
11704 // --- identify the edges constituting the wire of each subshape on the skin
11705 // define polylines with the nodes of edges, equivalent to wires
11706 // project polylines on subshapes, and partition, to get geom faces
11708 std::map<int, std::set<int> > shapeIdToVtkIdSet; // shapeId --> set of vtkId on skin
11709 std::set<int> emptySet;
11711 std::set<int> shapeIds;
11713 SMDS_ElemIteratorPtr itelem = sgrps->GetElements();
11714 while (itelem->more())
11716 const SMDS_MeshElement *elem = itelem->next();
11717 int shapeId = elem->getshapeId();
11718 int vtkId = elem->getVtkId();
11719 if (!shapeIdToVtkIdSet.count(shapeId))
11721 shapeIdToVtkIdSet[shapeId] = emptySet;
11722 shapeIds.insert(shapeId);
11724 shapeIdToVtkIdSet[shapeId].insert(vtkId);
11727 std::map<int, std::set<DownIdType, DownIdCompare> > shapeIdToEdges; // shapeId --> set of downward edges
11728 std::set<DownIdType, DownIdCompare> emptyEdges;
11729 emptyEdges.clear();
11731 std::map<int, std::set<int> >::iterator itShape = shapeIdToVtkIdSet.begin();
11732 for (; itShape != shapeIdToVtkIdSet.end(); ++itShape)
11734 int shapeId = itShape->first;
11735 MESSAGE(" --- Shape ID --- "<< shapeId);
11736 shapeIdToEdges[shapeId] = emptyEdges;
11738 std::vector<int> nodesEdges;
11740 std::set<int>::iterator its = itShape->second.begin();
11741 for (; its != itShape->second.end(); ++its)
11744 MESSAGE(" " << vtkId);
11745 int neighborsVtkIds[NBMAXNEIGHBORS];
11746 int downIds[NBMAXNEIGHBORS];
11747 unsigned char downTypes[NBMAXNEIGHBORS];
11748 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
11749 for (int n = 0; n < nbNeighbors; n++)
11751 if (neighborsVtkIds[n]<0) // only smds faces are considered as neighbors here
11753 int smdsId = meshDS->fromVtkToSmds(neighborsVtkIds[n]);
11754 const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
11755 if ( shapeIds.count(elem->getshapeId()) && !sgrps->Contains(elem)) // edge : neighbor in the set of shape, not in the group
11757 DownIdType edge(downIds[n], downTypes[n]);
11758 if (!shapeIdToEdges[shapeId].count(edge))
11760 shapeIdToEdges[shapeId].insert(edge);
11762 int nbNodes = grid->getDownArray(downTypes[n])->getNodes(downIds[n],vtkNodeId);
11763 nodesEdges.push_back(vtkNodeId[0]);
11764 nodesEdges.push_back(vtkNodeId[nbNodes-1]);
11765 MESSAGE(" --- nodes " << vtkNodeId[0]+1 << " " << vtkNodeId[nbNodes-1]+1);
11771 std::list<int> order;
11773 if (nodesEdges.size() > 0)
11775 order.push_back(nodesEdges[0]); MESSAGE(" --- back " << order.back()+1); // SMDS id = VTK id + 1;
11776 nodesEdges[0] = -1;
11777 order.push_back(nodesEdges[1]); MESSAGE(" --- back " << order.back()+1);
11778 nodesEdges[1] = -1; // do not reuse this edge
11782 int nodeTofind = order.back(); // try first to push back
11784 for (i = 0; i<nodesEdges.size(); i++)
11785 if (nodesEdges[i] == nodeTofind)
11787 if (i == nodesEdges.size())
11788 found = false; // no follower found on back
11791 if (i%2) // odd ==> use the previous one
11792 if (nodesEdges[i-1] < 0)
11796 order.push_back(nodesEdges[i-1]); MESSAGE(" --- back " << order.back()+1);
11797 nodesEdges[i-1] = -1;
11799 else // even ==> use the next one
11800 if (nodesEdges[i+1] < 0)
11804 order.push_back(nodesEdges[i+1]); MESSAGE(" --- back " << order.back()+1);
11805 nodesEdges[i+1] = -1;
11810 // try to push front
11812 nodeTofind = order.front(); // try to push front
11813 for (i = 0; i<nodesEdges.size(); i++)
11814 if (nodesEdges[i] == nodeTofind)
11816 if (i == nodesEdges.size())
11818 found = false; // no predecessor found on front
11821 if (i%2) // odd ==> use the previous one
11822 if (nodesEdges[i-1] < 0)
11826 order.push_front(nodesEdges[i-1]); MESSAGE(" --- front " << order.front()+1);
11827 nodesEdges[i-1] = -1;
11829 else // even ==> use the next one
11830 if (nodesEdges[i+1] < 0)
11834 order.push_front(nodesEdges[i+1]); MESSAGE(" --- front " << order.front()+1);
11835 nodesEdges[i+1] = -1;
11841 std::vector<int> nodes;
11842 nodes.push_back(shapeId);
11843 std::list<int>::iterator itl = order.begin();
11844 for (; itl != order.end(); itl++)
11846 nodes.push_back((*itl) + 1); // SMDS id = VTK id + 1;
11847 MESSAGE(" ordered node " << nodes[nodes.size()-1]);
11849 listOfListOfNodes.push_back(nodes);
11852 // partition geom faces with blocFissure
11853 // mesh blocFissure and geom faces of the skin (external wires given, triangle algo to choose)
11854 // mesh volume around blocFissure (skin triangles and quadrangle given, tetra algo to choose)
11860 //================================================================================
11862 * \brief Generates skin mesh (containing 2D cells) from 3D mesh
11863 * The created 2D mesh elements based on nodes of free faces of boundary volumes
11864 * \return TRUE if operation has been completed successfully, FALSE otherwise
11866 //================================================================================
11868 bool SMESH_MeshEditor::Make2DMeshFrom3D()
11870 // iterates on volume elements and detect all free faces on them
11871 SMESHDS_Mesh* aMesh = GetMeshDS();
11874 //bool res = false;
11875 int nbFree = 0, nbExisted = 0, nbCreated = 0;
11876 SMDS_VolumeIteratorPtr vIt = aMesh->volumesIterator();
11879 const SMDS_MeshVolume* volume = vIt->next();
11880 SMDS_VolumeTool vTool( volume, /*ignoreCentralNodes=*/false );
11881 vTool.SetExternalNormal();
11882 //const bool isPoly = volume->IsPoly();
11883 const int iQuad = volume->IsQuadratic();
11884 for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
11886 if (!vTool.IsFreeFace(iface))
11889 vector<const SMDS_MeshNode *> nodes;
11890 int nbFaceNodes = vTool.NbFaceNodes(iface);
11891 const SMDS_MeshNode** faceNodes = vTool.GetFaceNodes(iface);
11893 for ( ; inode < nbFaceNodes; inode += iQuad+1)
11894 nodes.push_back(faceNodes[inode]);
11895 if (iQuad) { // add medium nodes
11896 for ( inode = 1; inode < nbFaceNodes; inode += 2)
11897 nodes.push_back(faceNodes[inode]);
11898 if ( nbFaceNodes == 9 ) // bi-quadratic quad
11899 nodes.push_back(faceNodes[8]);
11901 // add new face based on volume nodes
11902 if (aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false) ) {
11904 continue; // face already exsist
11906 AddElement(nodes, SMDSAbs_Face, ( !iQuad && nbFaceNodes/(iQuad+1) > 4 ));
11910 return ( nbFree==(nbExisted+nbCreated) );
11915 inline const SMDS_MeshNode* getNodeWithSameID(SMESHDS_Mesh* mesh, const SMDS_MeshNode* node)
11917 if ( const SMDS_MeshNode* n = mesh->FindNode( node->GetID() ))
11919 return mesh->AddNodeWithID( node->X(),node->Y(),node->Z(), node->GetID() );
11922 //================================================================================
11924 * \brief Creates missing boundary elements
11925 * \param elements - elements whose boundary is to be checked
11926 * \param dimension - defines type of boundary elements to create
11927 * \param group - a group to store created boundary elements in
11928 * \param targetMesh - a mesh to store created boundary elements in
11929 * \param toCopyElements - if true, the checked elements will be copied into the targetMesh
11930 * \param toCopyExistingBoundary - if true, not only new but also pre-existing
11931 * boundary elements will be copied into the targetMesh
11932 * \param toAddExistingBondary - if true, not only new but also pre-existing
11933 * boundary elements will be added into the new group
11934 * \param aroundElements - if true, elements will be created on boundary of given
11935 * elements else, on boundary of the whole mesh.
11936 * \return nb of added boundary elements
11938 //================================================================================
11940 int SMESH_MeshEditor::MakeBoundaryMesh(const TIDSortedElemSet& elements,
11941 Bnd_Dimension dimension,
11942 SMESH_Group* group/*=0*/,
11943 SMESH_Mesh* targetMesh/*=0*/,
11944 bool toCopyElements/*=false*/,
11945 bool toCopyExistingBoundary/*=false*/,
11946 bool toAddExistingBondary/*= false*/,
11947 bool aroundElements/*= false*/)
11949 SMDSAbs_ElementType missType = (dimension == BND_2DFROM3D) ? SMDSAbs_Face : SMDSAbs_Edge;
11950 SMDSAbs_ElementType elemType = (dimension == BND_1DFROM2D) ? SMDSAbs_Face : SMDSAbs_Volume;
11951 // hope that all elements are of the same type, do not check them all
11952 if ( !elements.empty() && (*elements.begin())->GetType() != elemType )
11953 throw SALOME_Exception(LOCALIZED("wrong element type"));
11956 toCopyElements = toCopyExistingBoundary = false;
11958 SMESH_MeshEditor tgtEditor( targetMesh ? targetMesh : myMesh );
11959 SMESHDS_Mesh* aMesh = GetMeshDS(), *tgtMeshDS = tgtEditor.GetMeshDS();
11960 int nbAddedBnd = 0;
11962 // editor adding present bnd elements and optionally holding elements to add to the group
11963 SMESH_MeshEditor* presentEditor;
11964 SMESH_MeshEditor tgtEditor2( tgtEditor.GetMesh() );
11965 presentEditor = toAddExistingBondary ? &tgtEditor : &tgtEditor2;
11967 SMESH_MesherHelper helper( *myMesh );
11968 const TopAbs_ShapeEnum missShapeType = ( missType==SMDSAbs_Face ? TopAbs_FACE : TopAbs_EDGE );
11969 SMDS_VolumeTool vTool;
11970 TIDSortedElemSet avoidSet;
11971 const TIDSortedElemSet emptySet, *elemSet = aroundElements ? &elements : &emptySet;
11974 typedef vector<const SMDS_MeshNode*> TConnectivity;
11976 SMDS_ElemIteratorPtr eIt;
11977 if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
11978 else eIt = elemSetIterator( elements );
11980 while (eIt->more())
11982 const SMDS_MeshElement* elem = eIt->next();
11983 const int iQuad = elem->IsQuadratic();
11985 // ------------------------------------------------------------------------------------
11986 // 1. For an elem, get present bnd elements and connectivities of missing bnd elements
11987 // ------------------------------------------------------------------------------------
11988 vector<const SMDS_MeshElement*> presentBndElems;
11989 vector<TConnectivity> missingBndElems;
11990 TConnectivity nodes, elemNodes;
11991 if ( vTool.Set(elem, /*ignoreCentralNodes=*/true) ) // elem is a volume --------------
11993 vTool.SetExternalNormal();
11994 const SMDS_MeshElement* otherVol = 0;
11995 for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
11997 if ( !vTool.IsFreeFace(iface, &otherVol) &&
11998 ( !aroundElements || elements.count( otherVol )))
12000 const SMDS_MeshNode** nn = vTool.GetFaceNodes(iface);
12001 const int nbFaceNodes = vTool.NbFaceNodes (iface);
12002 if ( missType == SMDSAbs_Edge ) // boundary edges
12004 nodes.resize( 2+iQuad );
12005 for ( int i = 0; i < nbFaceNodes; i += 1+iQuad)
12007 for ( int j = 0; j < nodes.size(); ++j )
12009 if ( const SMDS_MeshElement* edge =
12010 aMesh->FindElement(nodes,SMDSAbs_Edge,/*noMedium=*/false))
12011 presentBndElems.push_back( edge );
12013 missingBndElems.push_back( nodes );
12016 else // boundary face
12019 for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12020 nodes.push_back( nn[inode] ); // add corner nodes
12022 for ( inode = 1; inode < nbFaceNodes; inode += 2)
12023 nodes.push_back( nn[inode] ); // add medium nodes
12024 int iCenter = vTool.GetCenterNodeIndex(iface); // for HEX27
12026 nodes.push_back( vTool.GetNodes()[ iCenter ] );
12028 if (const SMDS_MeshElement * f = aMesh->FindElement( nodes,
12029 SMDSAbs_Face, /*noMedium=*/false ))
12030 presentBndElems.push_back( f );
12032 missingBndElems.push_back( nodes );
12034 if ( targetMesh != myMesh )
12036 // add 1D elements on face boundary to be added to a new mesh
12037 const SMDS_MeshElement* edge;
12038 for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12041 edge = aMesh->FindEdge( nn[inode], nn[inode+1], nn[inode+2]);
12043 edge = aMesh->FindEdge( nn[inode], nn[inode+1]);
12044 if ( edge && avoidSet.insert( edge ).second )
12045 presentBndElems.push_back( edge );
12051 else if ( elem->GetType() == SMDSAbs_Face ) // elem is a face ------------------------
12053 avoidSet.clear(), avoidSet.insert( elem );
12054 elemNodes.assign( SMDS_MeshElement::iterator( elem->interlacedNodesElemIterator() ),
12055 SMDS_MeshElement::iterator() );
12056 elemNodes.push_back( elemNodes[0] );
12057 nodes.resize( 2 + iQuad );
12058 const int nbLinks = elem->NbCornerNodes();
12059 for ( int i = 0, iN = 0; i < nbLinks; i++, iN += 1+iQuad )
12061 nodes[0] = elemNodes[iN];
12062 nodes[1] = elemNodes[iN+1+iQuad];
12063 if ( SMESH_MeshAlgos::FindFaceInSet( nodes[0], nodes[1], *elemSet, avoidSet))
12064 continue; // not free link
12066 if ( iQuad ) nodes[2] = elemNodes[iN+1];
12067 if ( const SMDS_MeshElement* edge =
12068 aMesh->FindElement(nodes,SMDSAbs_Edge,/*noMedium=*/false))
12069 presentBndElems.push_back( edge );
12071 missingBndElems.push_back( nodes );
12075 // ---------------------------------
12076 // 2. Add missing boundary elements
12077 // ---------------------------------
12078 if ( targetMesh != myMesh )
12079 // instead of making a map of nodes in this mesh and targetMesh,
12080 // we create nodes with same IDs.
12081 for ( int i = 0; i < missingBndElems.size(); ++i )
12083 TConnectivity& srcNodes = missingBndElems[i];
12084 TConnectivity nodes( srcNodes.size() );
12085 for ( inode = 0; inode < nodes.size(); ++inode )
12086 nodes[inode] = getNodeWithSameID( tgtMeshDS, srcNodes[inode] );
12087 if ( aroundElements && tgtEditor.GetMeshDS()->FindElement( nodes,
12089 /*noMedium=*/false))
12091 tgtEditor.AddElement(nodes, missType, !iQuad && nodes.size()/(iQuad+1)>4);
12095 for ( int i = 0; i < missingBndElems.size(); ++i )
12097 TConnectivity& nodes = missingBndElems[i];
12098 if ( aroundElements && tgtEditor.GetMeshDS()->FindElement( nodes,
12100 /*noMedium=*/false))
12102 SMDS_MeshElement* elem =
12103 tgtEditor.AddElement(nodes, missType, !iQuad && nodes.size()/(iQuad+1)>4);
12106 // try to set a new element to a shape
12107 if ( myMesh->HasShapeToMesh() )
12110 set< pair<TopAbs_ShapeEnum, int > > mediumShapes;
12111 const int nbN = nodes.size() / (iQuad+1 );
12112 for ( inode = 0; inode < nbN && ok; ++inode )
12114 pair<int, TopAbs_ShapeEnum> i_stype =
12115 helper.GetMediumPos( nodes[inode], nodes[(inode+1)%nbN]);
12116 if (( ok = ( i_stype.first > 0 && i_stype.second >= TopAbs_FACE )))
12117 mediumShapes.insert( make_pair ( i_stype.second, i_stype.first ));
12119 if ( ok && mediumShapes.size() > 1 )
12121 set< pair<TopAbs_ShapeEnum, int > >::iterator stype_i = mediumShapes.begin();
12122 pair<TopAbs_ShapeEnum, int> stype_i_0 = *stype_i;
12123 for ( ++stype_i; stype_i != mediumShapes.end() && ok; ++stype_i )
12125 if (( ok = ( stype_i->first != stype_i_0.first )))
12126 ok = helper.IsSubShape( aMesh->IndexToShape( stype_i->second ),
12127 aMesh->IndexToShape( stype_i_0.second ));
12130 if ( ok && mediumShapes.begin()->first == missShapeType )
12131 aMesh->SetMeshElementOnShape( elem, mediumShapes.begin()->second );
12135 // ----------------------------------
12136 // 3. Copy present boundary elements
12137 // ----------------------------------
12138 if ( toCopyExistingBoundary )
12139 for ( int i = 0 ; i < presentBndElems.size(); ++i )
12141 const SMDS_MeshElement* e = presentBndElems[i];
12142 TConnectivity nodes( e->NbNodes() );
12143 for ( inode = 0; inode < nodes.size(); ++inode )
12144 nodes[inode] = getNodeWithSameID( tgtMeshDS, e->GetNode(inode) );
12145 presentEditor->AddElement(nodes, e->GetType(), e->IsPoly());
12147 else // store present elements to add them to a group
12148 for ( int i = 0 ; i < presentBndElems.size(); ++i )
12150 presentEditor->myLastCreatedElems.Append(presentBndElems[i]);
12153 } // loop on given elements
12155 // ---------------------------------------------
12156 // 4. Fill group with boundary elements
12157 // ---------------------------------------------
12160 if ( SMESHDS_Group* g = dynamic_cast<SMESHDS_Group*>( group->GetGroupDS() ))
12161 for ( int i = 0; i < tgtEditor.myLastCreatedElems.Size(); ++i )
12162 g->SMDSGroup().Add( tgtEditor.myLastCreatedElems( i+1 ));
12164 tgtEditor.myLastCreatedElems.Clear();
12165 tgtEditor2.myLastCreatedElems.Clear();
12167 // -----------------------
12168 // 5. Copy given elements
12169 // -----------------------
12170 if ( toCopyElements && targetMesh != myMesh )
12172 if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
12173 else eIt = elemSetIterator( elements );
12174 while (eIt->more())
12176 const SMDS_MeshElement* elem = eIt->next();
12177 TConnectivity nodes( elem->NbNodes() );
12178 for ( inode = 0; inode < nodes.size(); ++inode )
12179 nodes[inode] = getNodeWithSameID( tgtMeshDS, elem->GetNode(inode) );
12180 tgtEditor.AddElement(nodes, elemType, elem->IsPoly());
12182 tgtEditor.myLastCreatedElems.Clear();