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 // containers to store an old group and generated new ones;
6457 // 1st new group is for result elems of different type than a source one;
6458 // 2nd new group is for same type result elems ("top" group at extrusion)
6460 using boost::make_tuple;
6461 typedef tuple< SMESHDS_GroupBase*, SMESHDS_Group*, SMESHDS_Group* > TOldNewGroup;
6462 vector< list< TOldNewGroup > > groupsByType( SMDSAbs_NbElementTypes );
6463 vector< TOldNewGroup* > orderedOldNewGroups; // in order of old groups
6465 set< string > groupNames;
6467 SMESH_Mesh::GroupIteratorPtr groupIt = GetMesh()->GetGroups();
6468 if ( !groupIt->more() ) return newGroupIDs;
6470 int newGroupID = mesh->GetGroupIds().back()+1;
6471 while ( groupIt->more() )
6473 SMESH_Group * group = groupIt->next();
6474 if ( !group ) continue;
6475 SMESHDS_GroupBase* groupDS = group->GetGroupDS();
6476 if ( !groupDS || groupDS->IsEmpty() ) continue;
6477 groupNames.insert ( group->GetName() );
6478 groupDS->SetStoreName( group->GetName() );
6479 const SMDSAbs_ElementType type = groupDS->GetType();
6480 SMESHDS_Group* newGroup = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
6481 SMESHDS_Group* newTopGroup = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
6482 groupsByType[ type ].push_back( make_tuple( groupDS, newGroup, newTopGroup ));
6483 orderedOldNewGroups.push_back( & groupsByType[ type ].back() );
6486 // Loop on nodes and elements to add them in new groups
6488 // is there elements of different types generated from one source element;
6489 // it is false for transformations and true for sweeping
6490 bool isTwoTypesResult = false;
6492 vector< const SMDS_MeshElement* > resultElems;
6493 for ( int isNodes = 0; isNodes < 2; ++isNodes )
6495 const SMESH_SequenceOfElemPtr& gens = isNodes ? nodeGens : elemGens;
6496 const SMESH_SequenceOfElemPtr& elems = isNodes ? myLastCreatedNodes : myLastCreatedElems;
6497 if ( gens.Length() != elems.Length() )
6498 throw SALOME_Exception("SMESH_MeshEditor::generateGroups(): invalid args");
6500 // loop on created elements
6501 for (int iElem = 1; iElem <= elems.Length(); ++iElem )
6503 const SMDS_MeshElement* sourceElem = gens( iElem );
6504 if ( !sourceElem ) {
6505 MESSAGE("generateGroups(): NULL source element");
6508 list< TOldNewGroup > & groupsOldNew = groupsByType[ sourceElem->GetType() ];
6509 if ( groupsOldNew.empty() ) { // no groups of this type at all
6510 while ( iElem < gens.Length() && gens( iElem+1 ) == sourceElem )
6511 ++iElem; // skip all elements made by sourceElem
6514 // collect all elements made by the iElem-th sourceElem
6515 resultElems.clear();
6516 if ( const SMDS_MeshElement* resElem = elems( iElem ))
6517 if ( resElem != sourceElem )
6518 resultElems.push_back( resElem );
6519 while ( iElem < gens.Length() && gens( iElem+1 ) == sourceElem )
6520 if ( const SMDS_MeshElement* resElem = elems( ++iElem ))
6521 if ( resElem != sourceElem )
6522 resultElems.push_back( resElem );
6524 // there must be a top element
6525 const SMDS_MeshElement* topElem = 0;
6528 topElem = resultElems.back();
6529 resultElems.pop_back();
6533 vector< const SMDS_MeshElement* >::reverse_iterator resElemIt = resultElems.rbegin();
6534 for ( ; resElemIt != resultElems.rend() ; ++resElemIt )
6535 if ( (*resElemIt)->GetType() == sourceElem->GetType() )
6537 topElem = *resElemIt;
6538 *resElemIt = 0; // erase *resElemIt
6543 // add resultElems to groups originted from ones the sourceElem belongs to
6544 list< TOldNewGroup >::iterator gOldNew, gLast = groupsOldNew.end();
6545 for ( gOldNew = groupsOldNew.begin(); gOldNew != gLast; ++gOldNew )
6547 SMESHDS_GroupBase* oldGroup = gOldNew->get<0>();
6548 if ( oldGroup->Contains( sourceElem )) // sourceElem is in oldGroup
6550 // fill in a new group
6551 SMDS_MeshGroup & newGroup = gOldNew->get<1>()->SMDSGroup();
6552 vector< const SMDS_MeshElement* >::iterator resLast = resultElems.end(), resElemIt;
6553 for ( resElemIt = resultElems.begin(); resElemIt != resLast; ++resElemIt )
6555 newGroup.Add( *resElemIt );
6557 // fill a "top" group
6560 SMDS_MeshGroup & newTopGroup = gOldNew->get<2>()->SMDSGroup();
6561 newTopGroup.Add( topElem );
6563 if ( !newGroup.IsEmpty() )
6564 isTwoTypesResult = true;
6568 } // loop on created elements
6569 }// loop on nodes and elements
6571 // Create new SMESH_Groups from SMESHDS_Groups and remove empty SMESHDS_Groups
6573 list<int> topGrouIds;
6574 for ( size_t i = 0; i < orderedOldNewGroups.size(); ++i )
6576 SMESHDS_GroupBase* oldGroupDS = orderedOldNewGroups[i]->get<0>();
6577 SMESHDS_Group* newGroups[2] = { orderedOldNewGroups[i]->get<1>(),
6578 orderedOldNewGroups[i]->get<2>() };
6579 for ( int is2nd = 0; is2nd < 2; ++is2nd )
6581 SMESHDS_Group* newGroupDS = newGroups[ is2nd ];
6582 if ( newGroupDS->IsEmpty() )
6584 mesh->GetMeshDS()->RemoveGroup( newGroupDS );
6589 newGroupDS->SetType( newGroupDS->GetElements()->next()->GetType() );
6592 const bool isTop = ( isTwoTypesResult &&
6593 newGroupDS->GetType() == oldGroupDS->GetType() &&
6596 string name = oldGroupDS->GetStoreName();
6597 { // remove trailing whitespaces (issue 22599)
6598 size_t size = name.size();
6599 while ( size > 1 && isspace( name[ size-1 ]))
6601 if ( size != name.size() )
6603 name.resize( size );
6604 oldGroupDS->SetStoreName( name.c_str() );
6607 if ( !targetMesh ) {
6608 string suffix = ( isTop ? "top": postfix.c_str() );
6612 while ( !groupNames.insert( name ).second ) // name exists
6613 name = SMESH_Comment( oldGroupDS->GetStoreName() ) << "_" << suffix << "_" << nb++;
6618 newGroupDS->SetStoreName( name.c_str() );
6620 // make a SMESH_Groups
6621 mesh->AddGroup( newGroupDS );
6623 topGrouIds.push_back( newGroupDS->GetID() );
6625 newGroupIDs->push_back( newGroupDS->GetID() );
6629 newGroupIDs->splice( newGroupIDs->end(), topGrouIds );
6634 //================================================================================
6636 * \brief Return list of group of nodes close to each other within theTolerance
6637 * Search among theNodes or in the whole mesh if theNodes is empty using
6638 * an Octree algorithm
6640 //================================================================================
6642 void SMESH_MeshEditor::FindCoincidentNodes (TIDSortedNodeSet & theNodes,
6643 const double theTolerance,
6644 TListOfListOfNodes & theGroupsOfNodes)
6646 myLastCreatedElems.Clear();
6647 myLastCreatedNodes.Clear();
6649 if ( theNodes.empty() )
6650 { // get all nodes in the mesh
6651 SMDS_NodeIteratorPtr nIt = GetMeshDS()->nodesIterator(/*idInceasingOrder=*/true);
6652 while ( nIt->more() )
6653 theNodes.insert( theNodes.end(),nIt->next());
6656 SMESH_OctreeNode::FindCoincidentNodes ( theNodes, &theGroupsOfNodes, theTolerance);
6659 //=======================================================================
6660 //function : SimplifyFace
6662 //=======================================================================
6664 int SMESH_MeshEditor::SimplifyFace (const vector<const SMDS_MeshNode *>& faceNodes,
6665 vector<const SMDS_MeshNode *>& poly_nodes,
6666 vector<int>& quantities) const
6668 int nbNodes = faceNodes.size();
6673 set<const SMDS_MeshNode*> nodeSet;
6675 // get simple seq of nodes
6676 //const SMDS_MeshNode* simpleNodes[ nbNodes ];
6677 vector<const SMDS_MeshNode*> simpleNodes( nbNodes );
6678 int iSimple = 0, nbUnique = 0;
6680 simpleNodes[iSimple++] = faceNodes[0];
6682 for (int iCur = 1; iCur < nbNodes; iCur++) {
6683 if (faceNodes[iCur] != simpleNodes[iSimple - 1]) {
6684 simpleNodes[iSimple++] = faceNodes[iCur];
6685 if (nodeSet.insert( faceNodes[iCur] ).second)
6689 int nbSimple = iSimple;
6690 if (simpleNodes[nbSimple - 1] == simpleNodes[0]) {
6700 bool foundLoop = (nbSimple > nbUnique);
6703 set<const SMDS_MeshNode*> loopSet;
6704 for (iSimple = 0; iSimple < nbSimple && !foundLoop; iSimple++) {
6705 const SMDS_MeshNode* n = simpleNodes[iSimple];
6706 if (!loopSet.insert( n ).second) {
6710 int iC = 0, curLast = iSimple;
6711 for (; iC < curLast; iC++) {
6712 if (simpleNodes[iC] == n) break;
6714 int loopLen = curLast - iC;
6716 // create sub-element
6718 quantities.push_back(loopLen);
6719 for (; iC < curLast; iC++) {
6720 poly_nodes.push_back(simpleNodes[iC]);
6723 // shift the rest nodes (place from the first loop position)
6724 for (iC = curLast + 1; iC < nbSimple; iC++) {
6725 simpleNodes[iC - loopLen] = simpleNodes[iC];
6727 nbSimple -= loopLen;
6730 } // for (iSimple = 0; iSimple < nbSimple; iSimple++)
6731 } // while (foundLoop)
6735 quantities.push_back(iSimple);
6736 for (int i = 0; i < iSimple; i++)
6737 poly_nodes.push_back(simpleNodes[i]);
6743 //=======================================================================
6744 //function : MergeNodes
6745 //purpose : In each group, the cdr of nodes are substituted by the first one
6747 //=======================================================================
6749 void SMESH_MeshEditor::MergeNodes (TListOfListOfNodes & theGroupsOfNodes)
6751 MESSAGE("MergeNodes");
6752 myLastCreatedElems.Clear();
6753 myLastCreatedNodes.Clear();
6755 SMESHDS_Mesh* aMesh = GetMeshDS();
6757 TNodeNodeMap nodeNodeMap; // node to replace - new node
6758 set<const SMDS_MeshElement*> elems; // all elements with changed nodes
6759 list< int > rmElemIds, rmNodeIds;
6761 // Fill nodeNodeMap and elems
6763 TListOfListOfNodes::iterator grIt = theGroupsOfNodes.begin();
6764 for ( ; grIt != theGroupsOfNodes.end(); grIt++ ) {
6765 list<const SMDS_MeshNode*>& nodes = *grIt;
6766 list<const SMDS_MeshNode*>::iterator nIt = nodes.begin();
6767 const SMDS_MeshNode* nToKeep = *nIt;
6768 //MESSAGE("node to keep " << nToKeep->GetID());
6769 for ( ++nIt; nIt != nodes.end(); nIt++ ) {
6770 const SMDS_MeshNode* nToRemove = *nIt;
6771 nodeNodeMap.insert( TNodeNodeMap::value_type( nToRemove, nToKeep ));
6772 if ( nToRemove != nToKeep ) {
6773 //MESSAGE(" node to remove " << nToRemove->GetID());
6774 rmNodeIds.push_back( nToRemove->GetID() );
6775 AddToSameGroups( nToKeep, nToRemove, aMesh );
6776 // set _alwaysComputed to a sub-mesh of VERTEX to enable mesh computing
6777 // after MergeNodes() w/o creating node in place of merged ones.
6778 const SMDS_PositionPtr& pos = nToRemove->GetPosition();
6779 if ( pos && pos->GetTypeOfPosition() == SMDS_TOP_VERTEX )
6780 if ( SMESH_subMesh* sm = myMesh->GetSubMeshContaining( nToRemove->getshapeId() ))
6781 sm->SetIsAlwaysComputed( true );
6784 SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
6785 while ( invElemIt->more() ) {
6786 const SMDS_MeshElement* elem = invElemIt->next();
6791 // Change element nodes or remove an element
6793 set<const SMDS_MeshElement*>::iterator eIt = elems.begin();
6794 for ( ; eIt != elems.end(); eIt++ ) {
6795 const SMDS_MeshElement* elem = *eIt;
6796 //MESSAGE(" ---- inverse elem on node to remove " << elem->GetID());
6797 int nbNodes = elem->NbNodes();
6798 int aShapeId = FindShape( elem );
6800 set<const SMDS_MeshNode*> nodeSet;
6801 vector< const SMDS_MeshNode*> curNodes( nbNodes ), uniqueNodes( nbNodes );
6802 int iUnique = 0, iCur = 0, nbRepl = 0;
6803 vector<int> iRepl( nbNodes );
6805 // get new seq of nodes
6806 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6807 while ( itN->more() ) {
6808 const SMDS_MeshNode* n =
6809 static_cast<const SMDS_MeshNode*>( itN->next() );
6811 TNodeNodeMap::iterator nnIt = nodeNodeMap.find( n );
6812 if ( nnIt != nodeNodeMap.end() ) { // n sticks
6814 // BUG 0020185: begin
6816 bool stopRecur = false;
6817 set<const SMDS_MeshNode*> nodesRecur;
6818 nodesRecur.insert(n);
6819 while (!stopRecur) {
6820 TNodeNodeMap::iterator nnIt_i = nodeNodeMap.find( n );
6821 if ( nnIt_i != nodeNodeMap.end() ) { // n sticks
6822 n = (*nnIt_i).second;
6823 if (!nodesRecur.insert(n).second) {
6824 // error: recursive dependancy
6834 curNodes[ iCur ] = n;
6835 bool isUnique = nodeSet.insert( n ).second;
6837 uniqueNodes[ iUnique++ ] = n;
6839 iRepl[ nbRepl++ ] = iCur;
6843 // Analyse element topology after replacement
6846 int nbUniqueNodes = nodeSet.size();
6847 //MESSAGE("nbNodes nbUniqueNodes " << nbNodes << " " << nbUniqueNodes);
6848 if ( nbNodes != nbUniqueNodes ) { // some nodes stick
6849 // Polygons and Polyhedral volumes
6850 if (elem->IsPoly()) {
6852 if (elem->GetType() == SMDSAbs_Face) {
6854 vector<const SMDS_MeshNode *> face_nodes (nbNodes);
6856 for (; inode < nbNodes; inode++) {
6857 face_nodes[inode] = curNodes[inode];
6860 vector<const SMDS_MeshNode *> polygons_nodes;
6861 vector<int> quantities;
6862 int nbNew = SimplifyFace(face_nodes, polygons_nodes, quantities);
6865 for (int iface = 0; iface < nbNew; iface++) {
6866 int nbNodes = quantities[iface];
6867 vector<const SMDS_MeshNode *> poly_nodes (nbNodes);
6868 for (int ii = 0; ii < nbNodes; ii++, inode++) {
6869 poly_nodes[ii] = polygons_nodes[inode];
6871 SMDS_MeshElement* newElem = aMesh->AddPolygonalFace(poly_nodes);
6872 myLastCreatedElems.Append(newElem);
6874 aMesh->SetMeshElementOnShape(newElem, aShapeId);
6877 MESSAGE("ChangeElementNodes MergeNodes Polygon");
6878 //aMesh->ChangeElementNodes(elem, &polygons_nodes[inode], quantities[nbNew - 1]);
6879 vector<const SMDS_MeshNode *> polynodes(polygons_nodes.begin()+inode,polygons_nodes.end());
6881 if (nbNew > 0) quid = nbNew - 1;
6882 vector<int> newquant(quantities.begin()+quid, quantities.end());
6883 const SMDS_MeshElement* newElem = 0;
6884 newElem = aMesh->AddPolyhedralVolume(polynodes, newquant);
6885 myLastCreatedElems.Append(newElem);
6886 if ( aShapeId && newElem )
6887 aMesh->SetMeshElementOnShape( newElem, aShapeId );
6888 rmElemIds.push_back(elem->GetID());
6891 rmElemIds.push_back(elem->GetID());
6895 else if (elem->GetType() == SMDSAbs_Volume) {
6896 // Polyhedral volume
6897 if (nbUniqueNodes < 4) {
6898 rmElemIds.push_back(elem->GetID());
6901 // each face has to be analyzed in order to check volume validity
6902 const SMDS_VtkVolume* aPolyedre =
6903 dynamic_cast<const SMDS_VtkVolume*>( elem );
6905 int nbFaces = aPolyedre->NbFaces();
6907 vector<const SMDS_MeshNode *> poly_nodes;
6908 vector<int> quantities;
6910 for (int iface = 1; iface <= nbFaces; iface++) {
6911 int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
6912 vector<const SMDS_MeshNode *> faceNodes (nbFaceNodes);
6914 for (int inode = 1; inode <= nbFaceNodes; inode++) {
6915 const SMDS_MeshNode * faceNode = aPolyedre->GetFaceNode(iface, inode);
6916 TNodeNodeMap::iterator nnIt = nodeNodeMap.find(faceNode);
6917 if (nnIt != nodeNodeMap.end()) { // faceNode sticks
6918 faceNode = (*nnIt).second;
6920 faceNodes[inode - 1] = faceNode;
6923 SimplifyFace(faceNodes, poly_nodes, quantities);
6926 if (quantities.size() > 3) {
6927 // to be done: remove coincident faces
6930 if (quantities.size() > 3)
6932 MESSAGE("ChangeElementNodes MergeNodes Polyhedron");
6933 //aMesh->ChangePolyhedronNodes(elem, poly_nodes, quantities);
6934 const SMDS_MeshElement* newElem = 0;
6935 newElem = aMesh->AddPolyhedralVolume(poly_nodes, quantities);
6936 myLastCreatedElems.Append(newElem);
6937 if ( aShapeId && newElem )
6938 aMesh->SetMeshElementOnShape( newElem, aShapeId );
6939 rmElemIds.push_back(elem->GetID());
6943 rmElemIds.push_back(elem->GetID());
6954 // TODO not all the possible cases are solved. Find something more generic?
6955 switch ( nbNodes ) {
6956 case 2: ///////////////////////////////////// EDGE
6957 isOk = false; break;
6958 case 3: ///////////////////////////////////// TRIANGLE
6959 isOk = false; break;
6961 if ( elem->GetType() == SMDSAbs_Volume ) // TETRAHEDRON
6963 else { //////////////////////////////////// QUADRANGLE
6964 if ( nbUniqueNodes < 3 )
6966 else if ( nbRepl == 2 && iRepl[ 1 ] - iRepl[ 0 ] == 2 )
6967 isOk = false; // opposite nodes stick
6968 //MESSAGE("isOk " << isOk);
6971 case 6: ///////////////////////////////////// PENTAHEDRON
6972 if ( nbUniqueNodes == 4 ) {
6973 // ---------------------------------> tetrahedron
6975 iRepl[ 0 ] > 2 && iRepl[ 1 ] > 2 && iRepl[ 2 ] > 2 ) {
6976 // all top nodes stick: reverse a bottom
6977 uniqueNodes[ 0 ] = curNodes [ 1 ];
6978 uniqueNodes[ 1 ] = curNodes [ 0 ];
6980 else if (nbRepl == 3 &&
6981 iRepl[ 0 ] < 3 && iRepl[ 1 ] < 3 && iRepl[ 2 ] < 3 ) {
6982 // all bottom nodes stick: set a top before
6983 uniqueNodes[ 3 ] = uniqueNodes [ 0 ];
6984 uniqueNodes[ 0 ] = curNodes [ 3 ];
6985 uniqueNodes[ 1 ] = curNodes [ 4 ];
6986 uniqueNodes[ 2 ] = curNodes [ 5 ];
6988 else if (nbRepl == 4 &&
6989 iRepl[ 2 ] - iRepl [ 0 ] == 3 && iRepl[ 3 ] - iRepl [ 1 ] == 3 ) {
6990 // a lateral face turns into a line: reverse a bottom
6991 uniqueNodes[ 0 ] = curNodes [ 1 ];
6992 uniqueNodes[ 1 ] = curNodes [ 0 ];
6997 else if ( nbUniqueNodes == 5 ) {
6998 // PENTAHEDRON --------------------> 2 tetrahedrons
6999 if ( nbRepl == 2 && iRepl[ 1 ] - iRepl [ 0 ] == 3 ) {
7000 // a bottom node sticks with a linked top one
7002 SMDS_MeshElement* newElem =
7003 aMesh->AddVolume(curNodes[ 3 ],
7006 curNodes[ iRepl[ 0 ] == 2 ? 1 : 2 ]);
7007 myLastCreatedElems.Append(newElem);
7009 aMesh->SetMeshElementOnShape( newElem, aShapeId );
7010 // 2. : reverse a bottom
7011 uniqueNodes[ 0 ] = curNodes [ 1 ];
7012 uniqueNodes[ 1 ] = curNodes [ 0 ];
7022 if(elem->IsQuadratic()) { // Quadratic quadrangle
7034 MESSAGE("nbRepl=2: " << iRepl[0] << " " << iRepl[1]);
7037 MESSAGE("nbRepl=3: " << iRepl[0] << " " << iRepl[1] << " " << iRepl[2]);
7039 if( iRepl[0]==0 && iRepl[1]==1 && iRepl[2]==4 ) {
7040 uniqueNodes[0] = curNodes[0];
7041 uniqueNodes[1] = curNodes[2];
7042 uniqueNodes[2] = curNodes[3];
7043 uniqueNodes[3] = curNodes[5];
7044 uniqueNodes[4] = curNodes[6];
7045 uniqueNodes[5] = curNodes[7];
7048 if( iRepl[0]==0 && iRepl[1]==3 && iRepl[2]==7 ) {
7049 uniqueNodes[0] = curNodes[0];
7050 uniqueNodes[1] = curNodes[1];
7051 uniqueNodes[2] = curNodes[2];
7052 uniqueNodes[3] = curNodes[4];
7053 uniqueNodes[4] = curNodes[5];
7054 uniqueNodes[5] = curNodes[6];
7057 if( iRepl[0]==0 && iRepl[1]==4 && iRepl[2]==7 ) {
7058 uniqueNodes[0] = curNodes[1];
7059 uniqueNodes[1] = curNodes[2];
7060 uniqueNodes[2] = curNodes[3];
7061 uniqueNodes[3] = curNodes[5];
7062 uniqueNodes[4] = curNodes[6];
7063 uniqueNodes[5] = curNodes[0];
7066 if( iRepl[0]==1 && iRepl[1]==2 && iRepl[2]==5 ) {
7067 uniqueNodes[0] = curNodes[0];
7068 uniqueNodes[1] = curNodes[1];
7069 uniqueNodes[2] = curNodes[3];
7070 uniqueNodes[3] = curNodes[4];
7071 uniqueNodes[4] = curNodes[6];
7072 uniqueNodes[5] = curNodes[7];
7075 if( iRepl[0]==1 && iRepl[1]==4 && iRepl[2]==5 ) {
7076 uniqueNodes[0] = curNodes[0];
7077 uniqueNodes[1] = curNodes[2];
7078 uniqueNodes[2] = curNodes[3];
7079 uniqueNodes[3] = curNodes[1];
7080 uniqueNodes[4] = curNodes[6];
7081 uniqueNodes[5] = curNodes[7];
7084 if( iRepl[0]==2 && iRepl[1]==3 && iRepl[2]==6 ) {
7085 uniqueNodes[0] = curNodes[0];
7086 uniqueNodes[1] = curNodes[1];
7087 uniqueNodes[2] = curNodes[2];
7088 uniqueNodes[3] = curNodes[4];
7089 uniqueNodes[4] = curNodes[5];
7090 uniqueNodes[5] = curNodes[7];
7093 if( iRepl[0]==2 && iRepl[1]==5 && iRepl[2]==6 ) {
7094 uniqueNodes[0] = curNodes[0];
7095 uniqueNodes[1] = curNodes[1];
7096 uniqueNodes[2] = curNodes[3];
7097 uniqueNodes[3] = curNodes[4];
7098 uniqueNodes[4] = curNodes[2];
7099 uniqueNodes[5] = curNodes[7];
7102 if( iRepl[0]==3 && iRepl[1]==6 && iRepl[2]==7 ) {
7103 uniqueNodes[0] = curNodes[0];
7104 uniqueNodes[1] = curNodes[1];
7105 uniqueNodes[2] = curNodes[2];
7106 uniqueNodes[3] = curNodes[4];
7107 uniqueNodes[4] = curNodes[5];
7108 uniqueNodes[5] = curNodes[3];
7113 MESSAGE("nbRepl=4: " << iRepl[0] << " " << iRepl[1] << " " << iRepl[2] << " " << iRepl[3]);
7116 MESSAGE("nbRepl=5: " << iRepl[0] << " " << iRepl[1] << " " << iRepl[2] << " " << iRepl[3] << " " << iRepl[4]);
7120 //////////////////////////////////// HEXAHEDRON
7122 SMDS_VolumeTool hexa (elem);
7123 hexa.SetExternalNormal();
7124 if ( nbUniqueNodes == 4 && nbRepl == 4 ) {
7125 //////////////////////// HEX ---> 1 tetrahedron
7126 for ( int iFace = 0; iFace < 6; iFace++ ) {
7127 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7128 if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7129 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7130 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7131 // one face turns into a point ...
7132 int iOppFace = hexa.GetOppFaceIndex( iFace );
7133 ind = hexa.GetFaceNodesIndices( iOppFace );
7135 for ( iCur = 0; iCur < 4 && nbStick < 2; iCur++ ) {
7136 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7139 if ( nbStick == 1 ) {
7140 // ... and the opposite one - into a triangle.
7142 ind = hexa.GetFaceNodesIndices( iFace );
7143 uniqueNodes[ 3 ] = curNodes[ind[ 0 ]];
7150 else if ( nbUniqueNodes == 6 && nbRepl == 2 ) {
7151 //////////////////////// HEX ---> 1 prism
7152 int nbTria = 0, iTria[3];
7153 const int *ind; // indices of face nodes
7154 // look for triangular faces
7155 for ( int iFace = 0; iFace < 6 && nbTria < 3; iFace++ ) {
7156 ind = hexa.GetFaceNodesIndices( iFace );
7157 TIDSortedNodeSet faceNodes;
7158 for ( iCur = 0; iCur < 4; iCur++ )
7159 faceNodes.insert( curNodes[ind[iCur]] );
7160 if ( faceNodes.size() == 3 )
7161 iTria[ nbTria++ ] = iFace;
7163 // check if triangles are opposite
7164 if ( nbTria == 2 && iTria[0] == hexa.GetOppFaceIndex( iTria[1] ))
7167 // set nodes of the bottom triangle
7168 ind = hexa.GetFaceNodesIndices( iTria[ 0 ]);
7170 for ( iCur = 0; iCur < 4; iCur++ )
7171 if ( ind[iCur] != iRepl[0] && ind[iCur] != iRepl[1])
7172 indB.push_back( ind[iCur] );
7173 if ( !hexa.IsForward() )
7174 std::swap( indB[0], indB[2] );
7175 for ( iCur = 0; iCur < 3; iCur++ )
7176 uniqueNodes[ iCur ] = curNodes[indB[iCur]];
7177 // set nodes of the top triangle
7178 const int *indT = hexa.GetFaceNodesIndices( iTria[ 1 ]);
7179 for ( iCur = 0; iCur < 3; ++iCur )
7180 for ( int j = 0; j < 4; ++j )
7181 if ( hexa.IsLinked( indB[ iCur ], indT[ j ] ))
7183 uniqueNodes[ iCur + 3 ] = curNodes[ indT[ j ]];
7189 else if (nbUniqueNodes == 5 && nbRepl == 4 ) {
7190 //////////////////// HEXAHEDRON ---> 2 tetrahedrons
7191 for ( int iFace = 0; iFace < 6; iFace++ ) {
7192 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7193 if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7194 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7195 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7196 // one face turns into a point ...
7197 int iOppFace = hexa.GetOppFaceIndex( iFace );
7198 ind = hexa.GetFaceNodesIndices( iOppFace );
7200 iUnique = 2; // reverse a tetrahedron 1 bottom
7201 for ( iCur = 0; iCur < 4 && nbStick == 0; iCur++ ) {
7202 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7204 else if ( iUnique >= 0 )
7205 uniqueNodes[ iUnique-- ] = curNodes[ind[ iCur ]];
7207 if ( nbStick == 0 ) {
7208 // ... and the opposite one is a quadrangle
7210 const int* indTop = hexa.GetFaceNodesIndices( iFace );
7211 uniqueNodes[ 3 ] = curNodes[indTop[ 0 ]];
7214 SMDS_MeshElement* newElem =
7215 aMesh->AddVolume(curNodes[ind[ 0 ]],
7218 curNodes[indTop[ 0 ]]);
7219 myLastCreatedElems.Append(newElem);
7221 aMesh->SetMeshElementOnShape( newElem, aShapeId );
7228 else if ( nbUniqueNodes == 6 && nbRepl == 4 ) {
7229 ////////////////// HEXAHEDRON ---> 2 tetrahedrons or 1 prism
7230 // find indices of quad and tri faces
7231 int iQuadFace[ 6 ], iTriFace[ 6 ], nbQuad = 0, nbTri = 0, iFace;
7232 for ( iFace = 0; iFace < 6; iFace++ ) {
7233 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7235 for ( iCur = 0; iCur < 4; iCur++ )
7236 nodeSet.insert( curNodes[ind[ iCur ]] );
7237 nbUniqueNodes = nodeSet.size();
7238 if ( nbUniqueNodes == 3 )
7239 iTriFace[ nbTri++ ] = iFace;
7240 else if ( nbUniqueNodes == 4 )
7241 iQuadFace[ nbQuad++ ] = iFace;
7243 if (nbQuad == 2 && nbTri == 4 &&
7244 hexa.GetOppFaceIndex( iQuadFace[ 0 ] ) == iQuadFace[ 1 ]) {
7245 // 2 opposite quadrangles stuck with a diagonal;
7246 // sample groups of merged indices: (0-4)(2-6)
7247 // --------------------------------------------> 2 tetrahedrons
7248 const int *ind1 = hexa.GetFaceNodesIndices( iQuadFace[ 0 ]); // indices of quad1 nodes
7249 const int *ind2 = hexa.GetFaceNodesIndices( iQuadFace[ 1 ]);
7250 int i0, i1d, i2, i3d, i0t, i2t; // d-daigonal, t-top
7251 if (curNodes[ind1[ 0 ]] == curNodes[ind2[ 0 ]] &&
7252 curNodes[ind1[ 2 ]] == curNodes[ind2[ 2 ]]) {
7253 // stuck with 0-2 diagonal
7261 else if (curNodes[ind1[ 1 ]] == curNodes[ind2[ 3 ]] &&
7262 curNodes[ind1[ 3 ]] == curNodes[ind2[ 1 ]]) {
7263 // stuck with 1-3 diagonal
7275 uniqueNodes[ 0 ] = curNodes [ i0 ];
7276 uniqueNodes[ 1 ] = curNodes [ i1d ];
7277 uniqueNodes[ 2 ] = curNodes [ i3d ];
7278 uniqueNodes[ 3 ] = curNodes [ i0t ];
7281 SMDS_MeshElement* newElem = aMesh->AddVolume(curNodes[ i1d ],
7285 myLastCreatedElems.Append(newElem);
7287 aMesh->SetMeshElementOnShape( newElem, aShapeId );
7290 else if (( nbTri == 2 && nbQuad == 3 ) || // merged (0-4)(1-5)
7291 ( nbTri == 4 && nbQuad == 2 )) { // merged (7-4)(1-5)
7292 // --------------------------------------------> prism
7293 // find 2 opposite triangles
7295 for ( iFace = 0; iFace + 1 < nbTri; iFace++ ) {
7296 if ( hexa.GetOppFaceIndex( iTriFace[ iFace ] ) == iTriFace[ iFace + 1 ]) {
7297 // find indices of kept and replaced nodes
7298 // and fill unique nodes of 2 opposite triangles
7299 const int *ind1 = hexa.GetFaceNodesIndices( iTriFace[ iFace ]);
7300 const int *ind2 = hexa.GetFaceNodesIndices( iTriFace[ iFace + 1 ]);
7301 const SMDS_MeshNode** hexanodes = hexa.GetNodes();
7302 // fill unique nodes
7305 for ( iCur = 0; iCur < 4 && isOk; iCur++ ) {
7306 const SMDS_MeshNode* n = curNodes[ind1[ iCur ]];
7307 const SMDS_MeshNode* nInit = hexanodes[ind1[ iCur ]];
7309 // iCur of a linked node of the opposite face (make normals co-directed):
7310 int iCurOpp = ( iCur == 1 || iCur == 3 ) ? 4 - iCur : iCur;
7311 // check that correspondent corners of triangles are linked
7312 if ( !hexa.IsLinked( ind1[ iCur ], ind2[ iCurOpp ] ))
7315 uniqueNodes[ iUnique ] = n;
7316 uniqueNodes[ iUnique + 3 ] = curNodes[ind2[ iCurOpp ]];
7325 } // if ( nbUniqueNodes == 6 && nbRepl == 4 )
7328 MESSAGE("MergeNodes() removes hexahedron "<< elem);
7335 } // switch ( nbNodes )
7337 } // if ( nbNodes != nbUniqueNodes ) // some nodes stick
7339 if ( isOk ) { // the elem remains valid after sticking nodes
7340 if (elem->IsPoly() && elem->GetType() == SMDSAbs_Volume)
7342 // Change nodes of polyedre
7343 const SMDS_VtkVolume* aPolyedre =
7344 dynamic_cast<const SMDS_VtkVolume*>( elem );
7346 int nbFaces = aPolyedre->NbFaces();
7348 vector<const SMDS_MeshNode *> poly_nodes;
7349 vector<int> quantities (nbFaces);
7351 for (int iface = 1; iface <= nbFaces; iface++) {
7352 int inode, nbFaceNodes = aPolyedre->NbFaceNodes(iface);
7353 quantities[iface - 1] = nbFaceNodes;
7355 for (inode = 1; inode <= nbFaceNodes; inode++) {
7356 const SMDS_MeshNode* curNode = aPolyedre->GetFaceNode(iface, inode);
7358 TNodeNodeMap::iterator nnIt = nodeNodeMap.find( curNode );
7359 if (nnIt != nodeNodeMap.end()) { // curNode sticks
7360 curNode = (*nnIt).second;
7362 poly_nodes.push_back(curNode);
7365 aMesh->ChangePolyhedronNodes( elem, poly_nodes, quantities );
7368 else // replace non-polyhedron elements
7370 const SMDSAbs_ElementType etyp = elem->GetType();
7371 const int elemId = elem->GetID();
7372 const bool isPoly = (elem->GetEntityType() == SMDSEntity_Polygon);
7373 uniqueNodes.resize(nbUniqueNodes);
7375 SMESHDS_SubMesh * sm = aShapeId > 0 ? aMesh->MeshElements(aShapeId) : 0;
7377 aMesh->RemoveFreeElement(elem, sm, /*fromGroups=*/false);
7378 SMDS_MeshElement* newElem = this->AddElement(uniqueNodes, etyp, isPoly, elemId);
7379 if ( sm && newElem )
7380 sm->AddElement( newElem );
7381 if ( elem != newElem )
7382 ReplaceElemInGroups( elem, newElem, aMesh );
7386 // Remove invalid regular element or invalid polygon
7387 rmElemIds.push_back( elem->GetID() );
7390 } // loop on elements
7392 // Remove bad elements, then equal nodes (order important)
7394 Remove( rmElemIds, false );
7395 Remove( rmNodeIds, true );
7400 // ========================================================
7401 // class : SortableElement
7402 // purpose : allow sorting elements basing on their nodes
7403 // ========================================================
7404 class SortableElement : public set <const SMDS_MeshElement*>
7408 SortableElement( const SMDS_MeshElement* theElem )
7411 SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator();
7412 while ( nodeIt->more() )
7413 this->insert( nodeIt->next() );
7416 const SMDS_MeshElement* Get() const
7419 void Set(const SMDS_MeshElement* e) const
7424 mutable const SMDS_MeshElement* myElem;
7427 //=======================================================================
7428 //function : FindEqualElements
7429 //purpose : Return list of group of elements built on the same nodes.
7430 // Search among theElements or in the whole mesh if theElements is empty
7431 //=======================================================================
7433 void SMESH_MeshEditor::FindEqualElements(TIDSortedElemSet & theElements,
7434 TListOfListOfElementsID & theGroupsOfElementsID)
7436 myLastCreatedElems.Clear();
7437 myLastCreatedNodes.Clear();
7439 typedef map< SortableElement, int > TMapOfNodeSet;
7440 typedef list<int> TGroupOfElems;
7442 if ( theElements.empty() )
7443 { // get all elements in the mesh
7444 SMDS_ElemIteratorPtr eIt = GetMeshDS()->elementsIterator();
7445 while ( eIt->more() )
7446 theElements.insert( theElements.end(), eIt->next());
7449 vector< TGroupOfElems > arrayOfGroups;
7450 TGroupOfElems groupOfElems;
7451 TMapOfNodeSet mapOfNodeSet;
7453 TIDSortedElemSet::iterator elemIt = theElements.begin();
7454 for ( int i = 0, j=0; elemIt != theElements.end(); ++elemIt, ++j ) {
7455 const SMDS_MeshElement* curElem = *elemIt;
7456 SortableElement SE(curElem);
7459 pair< TMapOfNodeSet::iterator, bool> pp = mapOfNodeSet.insert(make_pair(SE, i));
7460 if( !(pp.second) ) {
7461 TMapOfNodeSet::iterator& itSE = pp.first;
7462 ind = (*itSE).second;
7463 arrayOfGroups[ind].push_back(curElem->GetID());
7466 groupOfElems.clear();
7467 groupOfElems.push_back(curElem->GetID());
7468 arrayOfGroups.push_back(groupOfElems);
7473 vector< TGroupOfElems >::iterator groupIt = arrayOfGroups.begin();
7474 for ( ; groupIt != arrayOfGroups.end(); ++groupIt ) {
7475 groupOfElems = *groupIt;
7476 if ( groupOfElems.size() > 1 ) {
7477 groupOfElems.sort();
7478 theGroupsOfElementsID.push_back(groupOfElems);
7483 //=======================================================================
7484 //function : MergeElements
7485 //purpose : In each given group, substitute all elements by the first one.
7486 //=======================================================================
7488 void SMESH_MeshEditor::MergeElements(TListOfListOfElementsID & theGroupsOfElementsID)
7490 myLastCreatedElems.Clear();
7491 myLastCreatedNodes.Clear();
7493 typedef list<int> TListOfIDs;
7494 TListOfIDs rmElemIds; // IDs of elems to remove
7496 SMESHDS_Mesh* aMesh = GetMeshDS();
7498 TListOfListOfElementsID::iterator groupsIt = theGroupsOfElementsID.begin();
7499 while ( groupsIt != theGroupsOfElementsID.end() ) {
7500 TListOfIDs& aGroupOfElemID = *groupsIt;
7501 aGroupOfElemID.sort();
7502 int elemIDToKeep = aGroupOfElemID.front();
7503 const SMDS_MeshElement* elemToKeep = aMesh->FindElement(elemIDToKeep);
7504 aGroupOfElemID.pop_front();
7505 TListOfIDs::iterator idIt = aGroupOfElemID.begin();
7506 while ( idIt != aGroupOfElemID.end() ) {
7507 int elemIDToRemove = *idIt;
7508 const SMDS_MeshElement* elemToRemove = aMesh->FindElement(elemIDToRemove);
7509 // add the kept element in groups of removed one (PAL15188)
7510 AddToSameGroups( elemToKeep, elemToRemove, aMesh );
7511 rmElemIds.push_back( elemIDToRemove );
7517 Remove( rmElemIds, false );
7520 //=======================================================================
7521 //function : MergeEqualElements
7522 //purpose : Remove all but one of elements built on the same nodes.
7523 //=======================================================================
7525 void SMESH_MeshEditor::MergeEqualElements()
7527 TIDSortedElemSet aMeshElements; /* empty input ==
7528 to merge equal elements in the whole mesh */
7529 TListOfListOfElementsID aGroupsOfElementsID;
7530 FindEqualElements(aMeshElements, aGroupsOfElementsID);
7531 MergeElements(aGroupsOfElementsID);
7534 //=======================================================================
7535 //function : findAdjacentFace
7537 //=======================================================================
7539 static const SMDS_MeshElement* findAdjacentFace(const SMDS_MeshNode* n1,
7540 const SMDS_MeshNode* n2,
7541 const SMDS_MeshElement* elem)
7543 TIDSortedElemSet elemSet, avoidSet;
7545 avoidSet.insert ( elem );
7546 return SMESH_MeshAlgos::FindFaceInSet( n1, n2, elemSet, avoidSet );
7549 //=======================================================================
7550 //function : FindFreeBorder
7552 //=======================================================================
7554 #define ControlFreeBorder SMESH::Controls::FreeEdges::IsFreeEdge
7556 bool SMESH_MeshEditor::FindFreeBorder (const SMDS_MeshNode* theFirstNode,
7557 const SMDS_MeshNode* theSecondNode,
7558 const SMDS_MeshNode* theLastNode,
7559 list< const SMDS_MeshNode* > & theNodes,
7560 list< const SMDS_MeshElement* >& theFaces)
7562 if ( !theFirstNode || !theSecondNode )
7564 // find border face between theFirstNode and theSecondNode
7565 const SMDS_MeshElement* curElem = findAdjacentFace( theFirstNode, theSecondNode, 0 );
7569 theFaces.push_back( curElem );
7570 theNodes.push_back( theFirstNode );
7571 theNodes.push_back( theSecondNode );
7573 //vector<const SMDS_MeshNode*> nodes;
7574 const SMDS_MeshNode *nIgnore = theFirstNode, *nStart = theSecondNode;
7575 TIDSortedElemSet foundElems;
7576 bool needTheLast = ( theLastNode != 0 );
7578 while ( nStart != theLastNode ) {
7579 if ( nStart == theFirstNode )
7580 return !needTheLast;
7582 // find all free border faces sharing form nStart
7584 list< const SMDS_MeshElement* > curElemList;
7585 list< const SMDS_MeshNode* > nStartList;
7586 SMDS_ElemIteratorPtr invElemIt = nStart->GetInverseElementIterator(SMDSAbs_Face);
7587 while ( invElemIt->more() ) {
7588 const SMDS_MeshElement* e = invElemIt->next();
7589 if ( e == curElem || foundElems.insert( e ).second ) {
7591 int iNode = 0, nbNodes = e->NbNodes();
7592 //const SMDS_MeshNode* nodes[nbNodes+1];
7593 vector<const SMDS_MeshNode*> nodes(nbNodes+1);
7595 if(e->IsQuadratic()) {
7596 const SMDS_VtkFace* F =
7597 dynamic_cast<const SMDS_VtkFace*>(e);
7598 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
7599 // use special nodes iterator
7600 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
7601 while( anIter->more() ) {
7602 nodes[ iNode++ ] = cast2Node(anIter->next());
7606 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
7607 while ( nIt->more() )
7608 nodes[ iNode++ ] = static_cast<const SMDS_MeshNode*>( nIt->next() );
7610 nodes[ iNode ] = nodes[ 0 ];
7612 for ( iNode = 0; iNode < nbNodes; iNode++ )
7613 if (((nodes[ iNode ] == nStart && nodes[ iNode + 1] != nIgnore ) ||
7614 (nodes[ iNode + 1] == nStart && nodes[ iNode ] != nIgnore )) &&
7615 ControlFreeBorder( &nodes[ iNode ], e->GetID() ))
7617 nStartList.push_back( nodes[ iNode + ( nodes[ iNode ] == nStart ? 1 : 0 )]);
7618 curElemList.push_back( e );
7622 // analyse the found
7624 int nbNewBorders = curElemList.size();
7625 if ( nbNewBorders == 0 ) {
7626 // no free border furthermore
7627 return !needTheLast;
7629 else if ( nbNewBorders == 1 ) {
7630 // one more element found
7632 nStart = nStartList.front();
7633 curElem = curElemList.front();
7634 theFaces.push_back( curElem );
7635 theNodes.push_back( nStart );
7638 // several continuations found
7639 list< const SMDS_MeshElement* >::iterator curElemIt;
7640 list< const SMDS_MeshNode* >::iterator nStartIt;
7641 // check if one of them reached the last node
7642 if ( needTheLast ) {
7643 for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
7644 curElemIt!= curElemList.end();
7645 curElemIt++, nStartIt++ )
7646 if ( *nStartIt == theLastNode ) {
7647 theFaces.push_back( *curElemIt );
7648 theNodes.push_back( *nStartIt );
7652 // find the best free border by the continuations
7653 list<const SMDS_MeshNode*> contNodes[ 2 ], *cNL;
7654 list<const SMDS_MeshElement*> contFaces[ 2 ], *cFL;
7655 for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
7656 curElemIt!= curElemList.end();
7657 curElemIt++, nStartIt++ )
7659 cNL = & contNodes[ contNodes[0].empty() ? 0 : 1 ];
7660 cFL = & contFaces[ contFaces[0].empty() ? 0 : 1 ];
7661 // find one more free border
7662 if ( ! SMESH_MeshEditor::FindFreeBorder( nStart, *nStartIt, theLastNode, *cNL, *cFL )) {
7666 else if ( !contNodes[0].empty() && !contNodes[1].empty() ) {
7667 // choice: clear a worse one
7668 int iLongest = ( contNodes[0].size() < contNodes[1].size() ? 1 : 0 );
7669 int iWorse = ( needTheLast ? 1 - iLongest : iLongest );
7670 contNodes[ iWorse ].clear();
7671 contFaces[ iWorse ].clear();
7674 if ( contNodes[0].empty() && contNodes[1].empty() )
7677 // append the best free border
7678 cNL = & contNodes[ contNodes[0].empty() ? 1 : 0 ];
7679 cFL = & contFaces[ contFaces[0].empty() ? 1 : 0 ];
7680 theNodes.pop_back(); // remove nIgnore
7681 theNodes.pop_back(); // remove nStart
7682 theFaces.pop_back(); // remove curElem
7683 list< const SMDS_MeshNode* >::iterator nIt = cNL->begin();
7684 list< const SMDS_MeshElement* >::iterator fIt = cFL->begin();
7685 for ( ; nIt != cNL->end(); nIt++ ) theNodes.push_back( *nIt );
7686 for ( ; fIt != cFL->end(); fIt++ ) theFaces.push_back( *fIt );
7689 } // several continuations found
7690 } // while ( nStart != theLastNode )
7695 //=======================================================================
7696 //function : CheckFreeBorderNodes
7697 //purpose : Return true if the tree nodes are on a free border
7698 //=======================================================================
7700 bool SMESH_MeshEditor::CheckFreeBorderNodes(const SMDS_MeshNode* theNode1,
7701 const SMDS_MeshNode* theNode2,
7702 const SMDS_MeshNode* theNode3)
7704 list< const SMDS_MeshNode* > nodes;
7705 list< const SMDS_MeshElement* > faces;
7706 return FindFreeBorder( theNode1, theNode2, theNode3, nodes, faces);
7709 //=======================================================================
7710 //function : SewFreeBorder
7712 //=======================================================================
7714 SMESH_MeshEditor::Sew_Error
7715 SMESH_MeshEditor::SewFreeBorder (const SMDS_MeshNode* theBordFirstNode,
7716 const SMDS_MeshNode* theBordSecondNode,
7717 const SMDS_MeshNode* theBordLastNode,
7718 const SMDS_MeshNode* theSideFirstNode,
7719 const SMDS_MeshNode* theSideSecondNode,
7720 const SMDS_MeshNode* theSideThirdNode,
7721 const bool theSideIsFreeBorder,
7722 const bool toCreatePolygons,
7723 const bool toCreatePolyedrs)
7725 myLastCreatedElems.Clear();
7726 myLastCreatedNodes.Clear();
7728 MESSAGE("::SewFreeBorder()");
7729 Sew_Error aResult = SEW_OK;
7731 // ====================================
7732 // find side nodes and elements
7733 // ====================================
7735 list< const SMDS_MeshNode* > nSide[ 2 ];
7736 list< const SMDS_MeshElement* > eSide[ 2 ];
7737 list< const SMDS_MeshNode* >::iterator nIt[ 2 ];
7738 list< const SMDS_MeshElement* >::iterator eIt[ 2 ];
7742 if (!FindFreeBorder(theBordFirstNode,theBordSecondNode,theBordLastNode,
7743 nSide[0], eSide[0])) {
7744 MESSAGE(" Free Border 1 not found " );
7745 aResult = SEW_BORDER1_NOT_FOUND;
7747 if (theSideIsFreeBorder) {
7750 if (!FindFreeBorder(theSideFirstNode, theSideSecondNode, theSideThirdNode,
7751 nSide[1], eSide[1])) {
7752 MESSAGE(" Free Border 2 not found " );
7753 aResult = ( aResult != SEW_OK ? SEW_BOTH_BORDERS_NOT_FOUND : SEW_BORDER2_NOT_FOUND );
7756 if ( aResult != SEW_OK )
7759 if (!theSideIsFreeBorder) {
7763 // -------------------------------------------------------------------------
7765 // 1. If nodes to merge are not coincident, move nodes of the free border
7766 // from the coord sys defined by the direction from the first to last
7767 // nodes of the border to the correspondent sys of the side 2
7768 // 2. On the side 2, find the links most co-directed with the correspondent
7769 // links of the free border
7770 // -------------------------------------------------------------------------
7772 // 1. Since sewing may break if there are volumes to split on the side 2,
7773 // we wont move nodes but just compute new coordinates for them
7774 typedef map<const SMDS_MeshNode*, gp_XYZ> TNodeXYZMap;
7775 TNodeXYZMap nBordXYZ;
7776 list< const SMDS_MeshNode* >& bordNodes = nSide[ 0 ];
7777 list< const SMDS_MeshNode* >::iterator nBordIt;
7779 gp_XYZ Pb1( theBordFirstNode->X(), theBordFirstNode->Y(), theBordFirstNode->Z() );
7780 gp_XYZ Pb2( theBordLastNode->X(), theBordLastNode->Y(), theBordLastNode->Z() );
7781 gp_XYZ Ps1( theSideFirstNode->X(), theSideFirstNode->Y(), theSideFirstNode->Z() );
7782 gp_XYZ Ps2( theSideSecondNode->X(), theSideSecondNode->Y(), theSideSecondNode->Z() );
7783 double tol2 = 1.e-8;
7784 gp_Vec Vbs1( Pb1 - Ps1 ),Vbs2( Pb2 - Ps2 );
7785 if ( Vbs1.SquareMagnitude() > tol2 || Vbs2.SquareMagnitude() > tol2 ) {
7786 // Need node movement.
7788 // find X and Z axes to create trsf
7789 gp_Vec Zb( Pb1 - Pb2 ), Zs( Ps1 - Ps2 );
7791 if ( X.SquareMagnitude() <= gp::Resolution() * gp::Resolution() )
7793 X = gp_Ax2( gp::Origin(), Zb ).XDirection();
7796 gp_Ax3 toBordAx( Pb1, Zb, X );
7797 gp_Ax3 fromSideAx( Ps1, Zs, X );
7798 gp_Ax3 toGlobalAx( gp::Origin(), gp::DZ(), gp::DX() );
7800 gp_Trsf toBordSys, fromSide2Sys;
7801 toBordSys.SetTransformation( toBordAx );
7802 fromSide2Sys.SetTransformation( fromSideAx, toGlobalAx );
7803 fromSide2Sys.SetScaleFactor( Zs.Magnitude() / Zb.Magnitude() );
7806 for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
7807 const SMDS_MeshNode* n = *nBordIt;
7808 gp_XYZ xyz( n->X(),n->Y(),n->Z() );
7809 toBordSys.Transforms( xyz );
7810 fromSide2Sys.Transforms( xyz );
7811 nBordXYZ.insert( TNodeXYZMap::value_type( n, xyz ));
7815 // just insert nodes XYZ in the nBordXYZ map
7816 for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
7817 const SMDS_MeshNode* n = *nBordIt;
7818 nBordXYZ.insert( TNodeXYZMap::value_type( n, gp_XYZ( n->X(),n->Y(),n->Z() )));
7822 // 2. On the side 2, find the links most co-directed with the correspondent
7823 // links of the free border
7825 list< const SMDS_MeshElement* >& sideElems = eSide[ 1 ];
7826 list< const SMDS_MeshNode* >& sideNodes = nSide[ 1 ];
7827 sideNodes.push_back( theSideFirstNode );
7829 bool hasVolumes = false;
7830 LinkID_Gen aLinkID_Gen( GetMeshDS() );
7831 set<long> foundSideLinkIDs, checkedLinkIDs;
7832 SMDS_VolumeTool volume;
7833 //const SMDS_MeshNode* faceNodes[ 4 ];
7835 const SMDS_MeshNode* sideNode;
7836 const SMDS_MeshElement* sideElem;
7837 const SMDS_MeshNode* prevSideNode = theSideFirstNode;
7838 const SMDS_MeshNode* prevBordNode = theBordFirstNode;
7839 nBordIt = bordNodes.begin();
7841 // border node position and border link direction to compare with
7842 gp_XYZ bordPos = nBordXYZ[ *nBordIt ];
7843 gp_XYZ bordDir = bordPos - nBordXYZ[ prevBordNode ];
7844 // choose next side node by link direction or by closeness to
7845 // the current border node:
7846 bool searchByDir = ( *nBordIt != theBordLastNode );
7848 // find the next node on the Side 2
7850 double maxDot = -DBL_MAX, minDist = DBL_MAX;
7852 checkedLinkIDs.clear();
7853 gp_XYZ prevXYZ( prevSideNode->X(), prevSideNode->Y(), prevSideNode->Z() );
7855 // loop on inverse elements of current node (prevSideNode) on the Side 2
7856 SMDS_ElemIteratorPtr invElemIt = prevSideNode->GetInverseElementIterator();
7857 while ( invElemIt->more() )
7859 const SMDS_MeshElement* elem = invElemIt->next();
7860 // prepare data for a loop on links coming to prevSideNode, of a face or a volume
7861 int iPrevNode, iNode = 0, nbNodes = elem->NbNodes();
7862 vector< const SMDS_MeshNode* > faceNodes( nbNodes, (const SMDS_MeshNode*)0 );
7863 bool isVolume = volume.Set( elem );
7864 const SMDS_MeshNode** nodes = isVolume ? volume.GetNodes() : & faceNodes[0];
7865 if ( isVolume ) // --volume
7867 else if ( elem->GetType()==SMDSAbs_Face ) { // --face
7868 // retrieve all face nodes and find iPrevNode - an index of the prevSideNode
7869 if(elem->IsQuadratic()) {
7870 const SMDS_VtkFace* F =
7871 dynamic_cast<const SMDS_VtkFace*>(elem);
7872 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
7873 // use special nodes iterator
7874 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
7875 while( anIter->more() ) {
7876 nodes[ iNode ] = cast2Node(anIter->next());
7877 if ( nodes[ iNode++ ] == prevSideNode )
7878 iPrevNode = iNode - 1;
7882 SMDS_ElemIteratorPtr nIt = elem->nodesIterator();
7883 while ( nIt->more() ) {
7884 nodes[ iNode ] = cast2Node( nIt->next() );
7885 if ( nodes[ iNode++ ] == prevSideNode )
7886 iPrevNode = iNode - 1;
7889 // there are 2 links to check
7894 // loop on links, to be precise, on the second node of links
7895 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
7896 const SMDS_MeshNode* n = nodes[ iNode ];
7898 if ( !volume.IsLinked( n, prevSideNode ))
7902 if ( iNode ) // a node before prevSideNode
7903 n = nodes[ iPrevNode == 0 ? elem->NbNodes() - 1 : iPrevNode - 1 ];
7904 else // a node after prevSideNode
7905 n = nodes[ iPrevNode + 1 == elem->NbNodes() ? 0 : iPrevNode + 1 ];
7907 // check if this link was already used
7908 long iLink = aLinkID_Gen.GetLinkID( prevSideNode, n );
7909 bool isJustChecked = !checkedLinkIDs.insert( iLink ).second;
7910 if (!isJustChecked &&
7911 foundSideLinkIDs.find( iLink ) == foundSideLinkIDs.end() )
7913 // test a link geometrically
7914 gp_XYZ nextXYZ ( n->X(), n->Y(), n->Z() );
7915 bool linkIsBetter = false;
7916 double dot = 0.0, dist = 0.0;
7917 if ( searchByDir ) { // choose most co-directed link
7918 dot = bordDir * ( nextXYZ - prevXYZ ).Normalized();
7919 linkIsBetter = ( dot > maxDot );
7921 else { // choose link with the node closest to bordPos
7922 dist = ( nextXYZ - bordPos ).SquareModulus();
7923 linkIsBetter = ( dist < minDist );
7925 if ( linkIsBetter ) {
7934 } // loop on inverse elements of prevSideNode
7937 MESSAGE(" Cant find path by links of the Side 2 ");
7938 return SEW_BAD_SIDE_NODES;
7940 sideNodes.push_back( sideNode );
7941 sideElems.push_back( sideElem );
7942 foundSideLinkIDs.insert ( linkID );
7943 prevSideNode = sideNode;
7945 if ( *nBordIt == theBordLastNode )
7946 searchByDir = false;
7948 // find the next border link to compare with
7949 gp_XYZ sidePos( sideNode->X(), sideNode->Y(), sideNode->Z() );
7950 searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
7951 // move to next border node if sideNode is before forward border node (bordPos)
7952 while ( *nBordIt != theBordLastNode && !searchByDir ) {
7953 prevBordNode = *nBordIt;
7955 bordPos = nBordXYZ[ *nBordIt ];
7956 bordDir = bordPos - nBordXYZ[ prevBordNode ];
7957 searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
7961 while ( sideNode != theSideSecondNode );
7963 if ( hasVolumes && sideNodes.size () != bordNodes.size() && !toCreatePolyedrs) {
7964 MESSAGE("VOLUME SPLITTING IS FORBIDDEN");
7965 return SEW_VOLUMES_TO_SPLIT; // volume splitting is forbidden
7967 } // end nodes search on the side 2
7969 // ============================
7970 // sew the border to the side 2
7971 // ============================
7973 int nbNodes[] = { nSide[0].size(), nSide[1].size() };
7974 int maxNbNodes = Max( nbNodes[0], nbNodes[1] );
7976 TListOfListOfNodes nodeGroupsToMerge;
7977 if ( nbNodes[0] == nbNodes[1] ||
7978 ( theSideIsFreeBorder && !theSideThirdNode)) {
7980 // all nodes are to be merged
7982 for (nIt[0] = nSide[0].begin(), nIt[1] = nSide[1].begin();
7983 nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end();
7984 nIt[0]++, nIt[1]++ )
7986 nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
7987 nodeGroupsToMerge.back().push_back( *nIt[1] ); // to keep
7988 nodeGroupsToMerge.back().push_back( *nIt[0] ); // to remove
7993 // insert new nodes into the border and the side to get equal nb of segments
7995 // get normalized parameters of nodes on the borders
7996 //double param[ 2 ][ maxNbNodes ];
7998 param[0] = new double [ maxNbNodes ];
7999 param[1] = new double [ maxNbNodes ];
8001 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8002 list< const SMDS_MeshNode* >& nodes = nSide[ iBord ];
8003 list< const SMDS_MeshNode* >::iterator nIt = nodes.begin();
8004 const SMDS_MeshNode* nPrev = *nIt;
8005 double bordLength = 0;
8006 for ( iNode = 0; nIt != nodes.end(); nIt++, iNode++ ) { // loop on border nodes
8007 const SMDS_MeshNode* nCur = *nIt;
8008 gp_XYZ segment (nCur->X() - nPrev->X(),
8009 nCur->Y() - nPrev->Y(),
8010 nCur->Z() - nPrev->Z());
8011 double segmentLen = segment.Modulus();
8012 bordLength += segmentLen;
8013 param[ iBord ][ iNode ] = bordLength;
8016 // normalize within [0,1]
8017 for ( iNode = 0; iNode < nbNodes[ iBord ]; iNode++ ) {
8018 param[ iBord ][ iNode ] /= bordLength;
8022 // loop on border segments
8023 const SMDS_MeshNode *nPrev[ 2 ] = { 0, 0 };
8024 int i[ 2 ] = { 0, 0 };
8025 nIt[0] = nSide[0].begin(); eIt[0] = eSide[0].begin();
8026 nIt[1] = nSide[1].begin(); eIt[1] = eSide[1].begin();
8028 TElemOfNodeListMap insertMap;
8029 TElemOfNodeListMap::iterator insertMapIt;
8031 // key: elem to insert nodes into
8032 // value: 2 nodes to insert between + nodes to be inserted
8034 bool next[ 2 ] = { false, false };
8036 // find min adjacent segment length after sewing
8037 double nextParam = 10., prevParam = 0;
8038 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8039 if ( i[ iBord ] + 1 < nbNodes[ iBord ])
8040 nextParam = Min( nextParam, param[iBord][ i[iBord] + 1 ]);
8041 if ( i[ iBord ] > 0 )
8042 prevParam = Max( prevParam, param[iBord][ i[iBord] - 1 ]);
8044 double minParam = Min( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8045 double maxParam = Max( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8046 double minSegLen = Min( nextParam - minParam, maxParam - prevParam );
8048 // choose to insert or to merge nodes
8049 double du = param[ 1 ][ i[1] ] - param[ 0 ][ i[0] ];
8050 if ( Abs( du ) <= minSegLen * 0.2 ) {
8053 nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8054 const SMDS_MeshNode* n0 = *nIt[0];
8055 const SMDS_MeshNode* n1 = *nIt[1];
8056 nodeGroupsToMerge.back().push_back( n1 );
8057 nodeGroupsToMerge.back().push_back( n0 );
8058 // position of node of the border changes due to merge
8059 param[ 0 ][ i[0] ] += du;
8060 // move n1 for the sake of elem shape evaluation during insertion.
8061 // n1 will be removed by MergeNodes() anyway
8062 const_cast<SMDS_MeshNode*>( n0 )->setXYZ( n1->X(), n1->Y(), n1->Z() );
8063 next[0] = next[1] = true;
8068 int intoBord = ( du < 0 ) ? 0 : 1;
8069 const SMDS_MeshElement* elem = *eIt[ intoBord ];
8070 const SMDS_MeshNode* n1 = nPrev[ intoBord ];
8071 const SMDS_MeshNode* n2 = *nIt[ intoBord ];
8072 const SMDS_MeshNode* nIns = *nIt[ 1 - intoBord ];
8073 if ( intoBord == 1 ) {
8074 // move node of the border to be on a link of elem of the side
8075 gp_XYZ p1 (n1->X(), n1->Y(), n1->Z());
8076 gp_XYZ p2 (n2->X(), n2->Y(), n2->Z());
8077 double ratio = du / ( param[ 1 ][ i[1] ] - param[ 1 ][ i[1]-1 ]);
8078 gp_XYZ p = p2 * ( 1 - ratio ) + p1 * ratio;
8079 GetMeshDS()->MoveNode( nIns, p.X(), p.Y(), p.Z() );
8081 insertMapIt = insertMap.find( elem );
8082 bool notFound = ( insertMapIt == insertMap.end() );
8083 bool otherLink = ( !notFound && (*insertMapIt).second.front() != n1 );
8085 // insert into another link of the same element:
8086 // 1. perform insertion into the other link of the elem
8087 list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8088 const SMDS_MeshNode* n12 = nodeList.front(); nodeList.pop_front();
8089 const SMDS_MeshNode* n22 = nodeList.front(); nodeList.pop_front();
8090 InsertNodesIntoLink( elem, n12, n22, nodeList, toCreatePolygons );
8091 // 2. perform insertion into the link of adjacent faces
8093 const SMDS_MeshElement* adjElem = findAdjacentFace( n12, n22, elem );
8095 InsertNodesIntoLink( adjElem, n12, n22, nodeList, toCreatePolygons );
8099 if (toCreatePolyedrs) {
8100 // perform insertion into the links of adjacent volumes
8101 UpdateVolumes(n12, n22, nodeList);
8103 // 3. find an element appeared on n1 and n2 after the insertion
8104 insertMap.erase( elem );
8105 elem = findAdjacentFace( n1, n2, 0 );
8107 if ( notFound || otherLink ) {
8108 // add element and nodes of the side into the insertMap
8109 insertMapIt = insertMap.insert
8110 ( TElemOfNodeListMap::value_type( elem, list<const SMDS_MeshNode*>() )).first;
8111 (*insertMapIt).second.push_back( n1 );
8112 (*insertMapIt).second.push_back( n2 );
8114 // add node to be inserted into elem
8115 (*insertMapIt).second.push_back( nIns );
8116 next[ 1 - intoBord ] = true;
8119 // go to the next segment
8120 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8121 if ( next[ iBord ] ) {
8122 if ( i[ iBord ] != 0 && eIt[ iBord ] != eSide[ iBord ].end())
8124 nPrev[ iBord ] = *nIt[ iBord ];
8125 nIt[ iBord ]++; i[ iBord ]++;
8129 while ( nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end());
8131 // perform insertion of nodes into elements
8133 for (insertMapIt = insertMap.begin();
8134 insertMapIt != insertMap.end();
8137 const SMDS_MeshElement* elem = (*insertMapIt).first;
8138 list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8139 const SMDS_MeshNode* n1 = nodeList.front(); nodeList.pop_front();
8140 const SMDS_MeshNode* n2 = nodeList.front(); nodeList.pop_front();
8142 InsertNodesIntoLink( elem, n1, n2, nodeList, toCreatePolygons );
8144 if ( !theSideIsFreeBorder ) {
8145 // look for and insert nodes into the faces adjacent to elem
8147 const SMDS_MeshElement* adjElem = findAdjacentFace( n1, n2, elem );
8149 InsertNodesIntoLink( adjElem, n1, n2, nodeList, toCreatePolygons );
8154 if (toCreatePolyedrs) {
8155 // perform insertion into the links of adjacent volumes
8156 UpdateVolumes(n1, n2, nodeList);
8162 } // end: insert new nodes
8164 MergeNodes ( nodeGroupsToMerge );
8169 //=======================================================================
8170 //function : InsertNodesIntoLink
8171 //purpose : insert theNodesToInsert into theFace between theBetweenNode1
8172 // and theBetweenNode2 and split theElement
8173 //=======================================================================
8175 void SMESH_MeshEditor::InsertNodesIntoLink(const SMDS_MeshElement* theFace,
8176 const SMDS_MeshNode* theBetweenNode1,
8177 const SMDS_MeshNode* theBetweenNode2,
8178 list<const SMDS_MeshNode*>& theNodesToInsert,
8179 const bool toCreatePoly)
8181 if ( theFace->GetType() != SMDSAbs_Face ) return;
8183 // find indices of 2 link nodes and of the rest nodes
8184 int iNode = 0, il1, il2, i3, i4;
8185 il1 = il2 = i3 = i4 = -1;
8186 //const SMDS_MeshNode* nodes[ theFace->NbNodes() ];
8187 vector<const SMDS_MeshNode*> nodes( theFace->NbNodes() );
8189 if(theFace->IsQuadratic()) {
8190 const SMDS_VtkFace* F =
8191 dynamic_cast<const SMDS_VtkFace*>(theFace);
8192 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8193 // use special nodes iterator
8194 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8195 while( anIter->more() ) {
8196 const SMDS_MeshNode* n = cast2Node(anIter->next());
8197 if ( n == theBetweenNode1 )
8199 else if ( n == theBetweenNode2 )
8205 nodes[ iNode++ ] = n;
8209 SMDS_ElemIteratorPtr nodeIt = theFace->nodesIterator();
8210 while ( nodeIt->more() ) {
8211 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8212 if ( n == theBetweenNode1 )
8214 else if ( n == theBetweenNode2 )
8220 nodes[ iNode++ ] = n;
8223 if ( il1 < 0 || il2 < 0 || i3 < 0 )
8226 // arrange link nodes to go one after another regarding the face orientation
8227 bool reverse = ( Abs( il2 - il1 ) == 1 ? il2 < il1 : il1 < il2 );
8228 list<const SMDS_MeshNode *> aNodesToInsert = theNodesToInsert;
8233 aNodesToInsert.reverse();
8235 // check that not link nodes of a quadrangles are in good order
8236 int nbFaceNodes = theFace->NbNodes();
8237 if ( nbFaceNodes == 4 && i4 - i3 != 1 ) {
8243 if (toCreatePoly || theFace->IsPoly()) {
8246 vector<const SMDS_MeshNode *> poly_nodes (nbFaceNodes + aNodesToInsert.size());
8248 // add nodes of face up to first node of link
8251 if(theFace->IsQuadratic()) {
8252 const SMDS_VtkFace* F =
8253 dynamic_cast<const SMDS_VtkFace*>(theFace);
8254 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8255 // use special nodes iterator
8256 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8257 while( anIter->more() && !isFLN ) {
8258 const SMDS_MeshNode* n = cast2Node(anIter->next());
8259 poly_nodes[iNode++] = n;
8260 if (n == nodes[il1]) {
8264 // add nodes to insert
8265 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8266 for (; nIt != aNodesToInsert.end(); nIt++) {
8267 poly_nodes[iNode++] = *nIt;
8269 // add nodes of face starting from last node of link
8270 while ( anIter->more() ) {
8271 poly_nodes[iNode++] = cast2Node(anIter->next());
8275 SMDS_ElemIteratorPtr nodeIt = theFace->nodesIterator();
8276 while ( nodeIt->more() && !isFLN ) {
8277 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8278 poly_nodes[iNode++] = n;
8279 if (n == nodes[il1]) {
8283 // add nodes to insert
8284 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8285 for (; nIt != aNodesToInsert.end(); nIt++) {
8286 poly_nodes[iNode++] = *nIt;
8288 // add nodes of face starting from last node of link
8289 while ( nodeIt->more() ) {
8290 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8291 poly_nodes[iNode++] = n;
8295 // edit or replace the face
8296 SMESHDS_Mesh *aMesh = GetMeshDS();
8298 if (theFace->IsPoly()) {
8299 aMesh->ChangePolygonNodes(theFace, poly_nodes);
8302 int aShapeId = FindShape( theFace );
8304 SMDS_MeshElement* newElem = aMesh->AddPolygonalFace(poly_nodes);
8305 myLastCreatedElems.Append(newElem);
8306 if ( aShapeId && newElem )
8307 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8309 aMesh->RemoveElement(theFace);
8314 SMESHDS_Mesh *aMesh = GetMeshDS();
8315 if( !theFace->IsQuadratic() ) {
8317 // put aNodesToInsert between theBetweenNode1 and theBetweenNode2
8318 int nbLinkNodes = 2 + aNodesToInsert.size();
8319 //const SMDS_MeshNode* linkNodes[ nbLinkNodes ];
8320 vector<const SMDS_MeshNode*> linkNodes( nbLinkNodes );
8321 linkNodes[ 0 ] = nodes[ il1 ];
8322 linkNodes[ nbLinkNodes - 1 ] = nodes[ il2 ];
8323 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8324 for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
8325 linkNodes[ iNode++ ] = *nIt;
8327 // decide how to split a quadrangle: compare possible variants
8328 // and choose which of splits to be a quadrangle
8329 int i1, i2, iSplit, nbSplits = nbLinkNodes - 1, iBestQuad;
8330 if ( nbFaceNodes == 3 ) {
8331 iBestQuad = nbSplits;
8334 else if ( nbFaceNodes == 4 ) {
8335 SMESH::Controls::NumericalFunctorPtr aCrit( new SMESH::Controls::AspectRatio);
8336 double aBestRate = DBL_MAX;
8337 for ( int iQuad = 0; iQuad < nbSplits; iQuad++ ) {
8339 double aBadRate = 0;
8340 // evaluate elements quality
8341 for ( iSplit = 0; iSplit < nbSplits; iSplit++ ) {
8342 if ( iSplit == iQuad ) {
8343 SMDS_FaceOfNodes quad (linkNodes[ i1++ ],
8347 aBadRate += getBadRate( &quad, aCrit );
8350 SMDS_FaceOfNodes tria (linkNodes[ i1++ ],
8352 nodes[ iSplit < iQuad ? i4 : i3 ]);
8353 aBadRate += getBadRate( &tria, aCrit );
8357 if ( aBadRate < aBestRate ) {
8359 aBestRate = aBadRate;
8364 // create new elements
8365 int aShapeId = FindShape( theFace );
8368 for ( iSplit = 0; iSplit < nbSplits - 1; iSplit++ ) {
8369 SMDS_MeshElement* newElem = 0;
8370 if ( iSplit == iBestQuad )
8371 newElem = aMesh->AddFace (linkNodes[ i1++ ],
8376 newElem = aMesh->AddFace (linkNodes[ i1++ ],
8378 nodes[ iSplit < iBestQuad ? i4 : i3 ]);
8379 myLastCreatedElems.Append(newElem);
8380 if ( aShapeId && newElem )
8381 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8384 // change nodes of theFace
8385 const SMDS_MeshNode* newNodes[ 4 ];
8386 newNodes[ 0 ] = linkNodes[ i1 ];
8387 newNodes[ 1 ] = linkNodes[ i2 ];
8388 newNodes[ 2 ] = nodes[ iSplit >= iBestQuad ? i3 : i4 ];
8389 newNodes[ 3 ] = nodes[ i4 ];
8390 //aMesh->ChangeElementNodes( theFace, newNodes, iSplit == iBestQuad ? 4 : 3 );
8391 const SMDS_MeshElement* newElem = 0;
8392 if (iSplit == iBestQuad)
8393 newElem = aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2], newNodes[3] );
8395 newElem = aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2] );
8396 myLastCreatedElems.Append(newElem);
8397 if ( aShapeId && newElem )
8398 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8399 } // end if(!theFace->IsQuadratic())
8400 else { // theFace is quadratic
8401 // we have to split theFace on simple triangles and one simple quadrangle
8403 int nbshift = tmp*2;
8404 // shift nodes in nodes[] by nbshift
8406 for(i=0; i<nbshift; i++) {
8407 const SMDS_MeshNode* n = nodes[0];
8408 for(j=0; j<nbFaceNodes-1; j++) {
8409 nodes[j] = nodes[j+1];
8411 nodes[nbFaceNodes-1] = n;
8413 il1 = il1 - nbshift;
8414 // now have to insert nodes between n0 and n1 or n1 and n2 (see below)
8415 // n0 n1 n2 n0 n1 n2
8416 // +-----+-----+ +-----+-----+
8425 // create new elements
8426 int aShapeId = FindShape( theFace );
8429 if(nbFaceNodes==6) { // quadratic triangle
8430 SMDS_MeshElement* newElem =
8431 aMesh->AddFace(nodes[3],nodes[4],nodes[5]);
8432 myLastCreatedElems.Append(newElem);
8433 if ( aShapeId && newElem )
8434 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8435 if(theFace->IsMediumNode(nodes[il1])) {
8436 // create quadrangle
8437 newElem = aMesh->AddFace(nodes[0],nodes[1],nodes[3],nodes[5]);
8438 myLastCreatedElems.Append(newElem);
8439 if ( aShapeId && newElem )
8440 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8446 // create quadrangle
8447 newElem = aMesh->AddFace(nodes[1],nodes[2],nodes[3],nodes[5]);
8448 myLastCreatedElems.Append(newElem);
8449 if ( aShapeId && newElem )
8450 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8456 else { // nbFaceNodes==8 - quadratic quadrangle
8457 SMDS_MeshElement* newElem =
8458 aMesh->AddFace(nodes[3],nodes[4],nodes[5]);
8459 myLastCreatedElems.Append(newElem);
8460 if ( aShapeId && newElem )
8461 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8462 newElem = aMesh->AddFace(nodes[5],nodes[6],nodes[7]);
8463 myLastCreatedElems.Append(newElem);
8464 if ( aShapeId && newElem )
8465 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8466 newElem = aMesh->AddFace(nodes[5],nodes[7],nodes[3]);
8467 myLastCreatedElems.Append(newElem);
8468 if ( aShapeId && newElem )
8469 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8470 if(theFace->IsMediumNode(nodes[il1])) {
8471 // create quadrangle
8472 newElem = aMesh->AddFace(nodes[0],nodes[1],nodes[3],nodes[7]);
8473 myLastCreatedElems.Append(newElem);
8474 if ( aShapeId && newElem )
8475 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8481 // create quadrangle
8482 newElem = aMesh->AddFace(nodes[1],nodes[2],nodes[3],nodes[7]);
8483 myLastCreatedElems.Append(newElem);
8484 if ( aShapeId && newElem )
8485 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8491 // create needed triangles using n1,n2,n3 and inserted nodes
8492 int nbn = 2 + aNodesToInsert.size();
8493 //const SMDS_MeshNode* aNodes[nbn];
8494 vector<const SMDS_MeshNode*> aNodes(nbn);
8495 aNodes[0] = nodes[n1];
8496 aNodes[nbn-1] = nodes[n2];
8497 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8498 for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
8499 aNodes[iNode++] = *nIt;
8501 for(i=1; i<nbn; i++) {
8502 SMDS_MeshElement* newElem =
8503 aMesh->AddFace(aNodes[i-1],aNodes[i],nodes[n3]);
8504 myLastCreatedElems.Append(newElem);
8505 if ( aShapeId && newElem )
8506 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8510 aMesh->RemoveElement(theFace);
8513 //=======================================================================
8514 //function : UpdateVolumes
8516 //=======================================================================
8517 void SMESH_MeshEditor::UpdateVolumes (const SMDS_MeshNode* theBetweenNode1,
8518 const SMDS_MeshNode* theBetweenNode2,
8519 list<const SMDS_MeshNode*>& theNodesToInsert)
8521 myLastCreatedElems.Clear();
8522 myLastCreatedNodes.Clear();
8524 SMDS_ElemIteratorPtr invElemIt = theBetweenNode1->GetInverseElementIterator(SMDSAbs_Volume);
8525 while (invElemIt->more()) { // loop on inverse elements of theBetweenNode1
8526 const SMDS_MeshElement* elem = invElemIt->next();
8528 // check, if current volume has link theBetweenNode1 - theBetweenNode2
8529 SMDS_VolumeTool aVolume (elem);
8530 if (!aVolume.IsLinked(theBetweenNode1, theBetweenNode2))
8533 // insert new nodes in all faces of the volume, sharing link theBetweenNode1 - theBetweenNode2
8534 int iface, nbFaces = aVolume.NbFaces();
8535 vector<const SMDS_MeshNode *> poly_nodes;
8536 vector<int> quantities (nbFaces);
8538 for (iface = 0; iface < nbFaces; iface++) {
8539 int nbFaceNodes = aVolume.NbFaceNodes(iface), nbInserted = 0;
8540 // faceNodes will contain (nbFaceNodes + 1) nodes, last = first
8541 const SMDS_MeshNode** faceNodes = aVolume.GetFaceNodes(iface);
8543 for (int inode = 0; inode < nbFaceNodes; inode++) {
8544 poly_nodes.push_back(faceNodes[inode]);
8546 if (nbInserted == 0) {
8547 if (faceNodes[inode] == theBetweenNode1) {
8548 if (faceNodes[inode + 1] == theBetweenNode2) {
8549 nbInserted = theNodesToInsert.size();
8551 // add nodes to insert
8552 list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.begin();
8553 for (; nIt != theNodesToInsert.end(); nIt++) {
8554 poly_nodes.push_back(*nIt);
8558 else if (faceNodes[inode] == theBetweenNode2) {
8559 if (faceNodes[inode + 1] == theBetweenNode1) {
8560 nbInserted = theNodesToInsert.size();
8562 // add nodes to insert in reversed order
8563 list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.end();
8565 for (; nIt != theNodesToInsert.begin(); nIt--) {
8566 poly_nodes.push_back(*nIt);
8568 poly_nodes.push_back(*nIt);
8575 quantities[iface] = nbFaceNodes + nbInserted;
8578 // Replace or update the volume
8579 SMESHDS_Mesh *aMesh = GetMeshDS();
8581 if (elem->IsPoly()) {
8582 aMesh->ChangePolyhedronNodes(elem, poly_nodes, quantities);
8586 int aShapeId = FindShape( elem );
8588 SMDS_MeshElement* newElem =
8589 aMesh->AddPolyhedralVolume(poly_nodes, quantities);
8590 myLastCreatedElems.Append(newElem);
8591 if (aShapeId && newElem)
8592 aMesh->SetMeshElementOnShape(newElem, aShapeId);
8594 aMesh->RemoveElement(elem);
8601 //================================================================================
8603 * \brief Transform any volume into data of SMDSEntity_Polyhedra
8605 //================================================================================
8607 void volumeToPolyhedron( const SMDS_MeshElement* elem,
8608 vector<const SMDS_MeshNode *> & nodes,
8609 vector<int> & nbNodeInFaces )
8612 nbNodeInFaces.clear();
8613 SMDS_VolumeTool vTool ( elem );
8614 for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
8616 const SMDS_MeshNode** fNodes = vTool.GetFaceNodes( iF );
8617 nodes.insert( nodes.end(), fNodes, fNodes + vTool.NbFaceNodes( iF ));
8618 nbNodeInFaces.push_back( vTool.NbFaceNodes( iF ));
8623 //=======================================================================
8625 * \brief Convert elements contained in a submesh to quadratic
8626 * \return int - nb of checked elements
8628 //=======================================================================
8630 int SMESH_MeshEditor::convertElemToQuadratic(SMESHDS_SubMesh * theSm,
8631 SMESH_MesherHelper& theHelper,
8632 const bool theForce3d)
8635 if( !theSm ) return nbElem;
8637 vector<int> nbNodeInFaces;
8638 vector<const SMDS_MeshNode *> nodes;
8639 SMDS_ElemIteratorPtr ElemItr = theSm->GetElements();
8640 while(ElemItr->more())
8643 const SMDS_MeshElement* elem = ElemItr->next();
8644 if( !elem ) continue;
8646 // analyse a necessity of conversion
8647 const SMDSAbs_ElementType aType = elem->GetType();
8648 if ( aType < SMDSAbs_Edge || aType > SMDSAbs_Volume )
8650 const SMDSAbs_EntityType aGeomType = elem->GetEntityType();
8651 bool hasCentralNodes = false;
8652 if ( elem->IsQuadratic() )
8655 switch ( aGeomType ) {
8656 case SMDSEntity_Quad_Triangle:
8657 case SMDSEntity_Quad_Quadrangle:
8658 case SMDSEntity_Quad_Hexa:
8659 alreadyOK = !theHelper.GetIsBiQuadratic(); break;
8661 case SMDSEntity_BiQuad_Triangle:
8662 case SMDSEntity_BiQuad_Quadrangle:
8663 case SMDSEntity_TriQuad_Hexa:
8664 alreadyOK = theHelper.GetIsBiQuadratic();
8665 hasCentralNodes = true;
8670 // take into account already present modium nodes
8672 case SMDSAbs_Volume:
8673 theHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( elem )); break;
8675 theHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( elem )); break;
8677 theHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( elem )); break;
8683 // get elem data needed to re-create it
8685 const int id = elem->GetID();
8686 const int nbNodes = elem->NbCornerNodes();
8687 nodes.assign(elem->begin_nodes(), elem->end_nodes());
8688 if ( aGeomType == SMDSEntity_Polyhedra )
8689 nbNodeInFaces = static_cast<const SMDS_VtkVolume* >( elem )->GetQuantities();
8690 else if ( aGeomType == SMDSEntity_Hexagonal_Prism )
8691 volumeToPolyhedron( elem, nodes, nbNodeInFaces );
8693 // remove a linear element
8694 GetMeshDS()->RemoveFreeElement(elem, theSm, /*fromGroups=*/false);
8696 // remove central nodes of biquadratic elements (biquad->quad convertion)
8697 if ( hasCentralNodes )
8698 for ( size_t i = nbNodes * 2; i < nodes.size(); ++i )
8699 if ( nodes[i]->NbInverseElements() == 0 )
8700 GetMeshDS()->RemoveFreeNode( nodes[i], theSm, /*fromGroups=*/true );
8702 const SMDS_MeshElement* NewElem = 0;
8708 NewElem = theHelper.AddEdge(nodes[0], nodes[1], id, theForce3d);
8716 NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
8719 NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
8722 NewElem = theHelper.AddPolygonalFace(nodes, id, theForce3d);
8726 case SMDSAbs_Volume :
8730 case SMDSEntity_Tetra:
8731 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
8733 case SMDSEntity_Pyramid:
8734 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], id, theForce3d);
8736 case SMDSEntity_Penta:
8737 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], nodes[5], id, theForce3d);
8739 case SMDSEntity_Hexa:
8740 case SMDSEntity_Quad_Hexa:
8741 case SMDSEntity_TriQuad_Hexa:
8742 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
8743 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
8745 case SMDSEntity_Hexagonal_Prism:
8747 NewElem = theHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
8754 ReplaceElemInGroups( elem, NewElem, GetMeshDS());
8755 if( NewElem && NewElem->getshapeId() < 1 )
8756 theSm->AddElement( NewElem );
8760 //=======================================================================
8761 //function : ConvertToQuadratic
8763 //=======================================================================
8765 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d, const bool theToBiQuad)
8767 SMESHDS_Mesh* meshDS = GetMeshDS();
8769 SMESH_MesherHelper aHelper(*myMesh);
8771 aHelper.SetIsQuadratic( true );
8772 aHelper.SetIsBiQuadratic( theToBiQuad );
8773 aHelper.SetElementsOnShape(true);
8774 aHelper.ToFixNodeParameters( true );
8776 // convert elements assigned to sub-meshes
8777 int nbCheckedElems = 0;
8778 if ( myMesh->HasShapeToMesh() )
8780 if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
8782 SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
8783 while ( smIt->more() ) {
8784 SMESH_subMesh* sm = smIt->next();
8785 if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() ) {
8786 aHelper.SetSubShape( sm->GetSubShape() );
8787 nbCheckedElems += convertElemToQuadratic(smDS, aHelper, theForce3d);
8793 // convert elements NOT assigned to sub-meshes
8794 int totalNbElems = meshDS->NbEdges() + meshDS->NbFaces() + meshDS->NbVolumes();
8795 if ( nbCheckedElems < totalNbElems ) // not all elements are in sub-meshes
8797 aHelper.SetElementsOnShape(false);
8798 SMESHDS_SubMesh *smDS = 0;
8801 SMDS_EdgeIteratorPtr aEdgeItr = meshDS->edgesIterator();
8802 while( aEdgeItr->more() )
8804 const SMDS_MeshEdge* edge = aEdgeItr->next();
8805 if ( !edge->IsQuadratic() )
8807 int id = edge->GetID();
8808 const SMDS_MeshNode* n1 = edge->GetNode(0);
8809 const SMDS_MeshNode* n2 = edge->GetNode(1);
8811 meshDS->RemoveFreeElement(edge, smDS, /*fromGroups=*/false);
8813 const SMDS_MeshEdge* NewEdge = aHelper.AddEdge(n1, n2, id, theForce3d);
8814 ReplaceElemInGroups( edge, NewEdge, GetMeshDS());
8818 aHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( edge ));
8823 SMDS_FaceIteratorPtr aFaceItr = meshDS->facesIterator();
8824 while( aFaceItr->more() )
8826 const SMDS_MeshFace* face = aFaceItr->next();
8827 if ( !face ) continue;
8829 const SMDSAbs_EntityType type = face->GetEntityType();
8833 case SMDSEntity_Quad_Triangle:
8834 case SMDSEntity_Quad_Quadrangle:
8835 alreadyOK = !theToBiQuad;
8836 aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
8838 case SMDSEntity_BiQuad_Triangle:
8839 case SMDSEntity_BiQuad_Quadrangle:
8840 alreadyOK = theToBiQuad;
8841 aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
8843 default: alreadyOK = false;
8848 const int id = face->GetID();
8849 vector<const SMDS_MeshNode *> nodes ( face->begin_nodes(), face->end_nodes());
8851 meshDS->RemoveFreeElement(face, smDS, /*fromGroups=*/false);
8853 SMDS_MeshFace * NewFace = 0;
8856 case SMDSEntity_Triangle:
8857 case SMDSEntity_Quad_Triangle:
8858 case SMDSEntity_BiQuad_Triangle:
8859 NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
8860 if ( nodes.size() == 7 && nodes[6]->NbInverseElements() == 0 ) // rm a central node
8861 GetMeshDS()->RemoveFreeNode( nodes[6], /*sm=*/0, /*fromGroups=*/true );
8864 case SMDSEntity_Quadrangle:
8865 case SMDSEntity_Quad_Quadrangle:
8866 case SMDSEntity_BiQuad_Quadrangle:
8867 NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
8868 if ( nodes.size() == 9 && nodes[8]->NbInverseElements() == 0 ) // rm a central node
8869 GetMeshDS()->RemoveFreeNode( nodes[8], /*sm=*/0, /*fromGroups=*/true );
8873 NewFace = aHelper.AddPolygonalFace(nodes, id, theForce3d);
8875 ReplaceElemInGroups( face, NewFace, GetMeshDS());
8879 vector<int> nbNodeInFaces;
8880 SMDS_VolumeIteratorPtr aVolumeItr = meshDS->volumesIterator();
8881 while(aVolumeItr->more())
8883 const SMDS_MeshVolume* volume = aVolumeItr->next();
8884 if ( !volume ) continue;
8886 const SMDSAbs_EntityType type = volume->GetEntityType();
8887 if ( volume->IsQuadratic() )
8892 case SMDSEntity_Quad_Hexa: alreadyOK = !theToBiQuad; break;
8893 case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; break;
8894 default: alreadyOK = true;
8898 aHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( volume ));
8902 const int id = volume->GetID();
8903 vector<const SMDS_MeshNode *> nodes (volume->begin_nodes(), volume->end_nodes());
8904 if ( type == SMDSEntity_Polyhedra )
8905 nbNodeInFaces = static_cast<const SMDS_VtkVolume* >(volume)->GetQuantities();
8906 else if ( type == SMDSEntity_Hexagonal_Prism )
8907 volumeToPolyhedron( volume, nodes, nbNodeInFaces );
8909 meshDS->RemoveFreeElement(volume, smDS, /*fromGroups=*/false);
8911 SMDS_MeshVolume * NewVolume = 0;
8914 case SMDSEntity_Tetra:
8915 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d );
8917 case SMDSEntity_Hexa:
8918 case SMDSEntity_Quad_Hexa:
8919 case SMDSEntity_TriQuad_Hexa:
8920 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
8921 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
8922 for ( size_t i = 20; i < nodes.size(); ++i ) // rm central nodes
8923 if ( nodes[i]->NbInverseElements() == 0 )
8924 GetMeshDS()->RemoveFreeNode( nodes[i], /*sm=*/0, /*fromGroups=*/true );
8926 case SMDSEntity_Pyramid:
8927 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
8928 nodes[3], nodes[4], id, theForce3d);
8930 case SMDSEntity_Penta:
8931 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
8932 nodes[3], nodes[4], nodes[5], id, theForce3d);
8934 case SMDSEntity_Hexagonal_Prism:
8936 NewVolume = aHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
8938 ReplaceElemInGroups(volume, NewVolume, meshDS);
8943 { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
8944 // aHelper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
8945 // aHelper.FixQuadraticElements(myError);
8946 SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
8950 //================================================================================
8952 * \brief Makes given elements quadratic
8953 * \param theForce3d - if true, the medium nodes will be placed in the middle of link
8954 * \param theElements - elements to make quadratic
8956 //================================================================================
8958 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d,
8959 TIDSortedElemSet& theElements,
8960 const bool theToBiQuad)
8962 if ( theElements.empty() ) return;
8964 // we believe that all theElements are of the same type
8965 const SMDSAbs_ElementType elemType = (*theElements.begin())->GetType();
8967 // get all nodes shared by theElements
8968 TIDSortedNodeSet allNodes;
8969 TIDSortedElemSet::iterator eIt = theElements.begin();
8970 for ( ; eIt != theElements.end(); ++eIt )
8971 allNodes.insert( (*eIt)->begin_nodes(), (*eIt)->end_nodes() );
8973 // complete theElements with elements of lower dim whose all nodes are in allNodes
8975 TIDSortedElemSet quadAdjacentElems [ SMDSAbs_NbElementTypes ]; // quadratic adjacent elements
8976 TIDSortedElemSet checkedAdjacentElems [ SMDSAbs_NbElementTypes ];
8977 TIDSortedNodeSet::iterator nIt = allNodes.begin();
8978 for ( ; nIt != allNodes.end(); ++nIt )
8980 const SMDS_MeshNode* n = *nIt;
8981 SMDS_ElemIteratorPtr invIt = n->GetInverseElementIterator();
8982 while ( invIt->more() )
8984 const SMDS_MeshElement* e = invIt->next();
8985 const SMDSAbs_ElementType type = e->GetType();
8986 if ( e->IsQuadratic() )
8988 quadAdjacentElems[ type ].insert( e );
8991 switch ( e->GetEntityType() ) {
8992 case SMDSEntity_Quad_Triangle:
8993 case SMDSEntity_Quad_Quadrangle:
8994 case SMDSEntity_Quad_Hexa: alreadyOK = !theToBiQuad; break;
8995 case SMDSEntity_BiQuad_Triangle:
8996 case SMDSEntity_BiQuad_Quadrangle:
8997 case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; break;
8998 default: alreadyOK = true;
9003 if ( type >= elemType )
9004 continue; // same type or more complex linear element
9006 if ( !checkedAdjacentElems[ type ].insert( e ).second )
9007 continue; // e is already checked
9011 SMDS_NodeIteratorPtr nodeIt = e->nodeIterator();
9012 while ( nodeIt->more() && allIn )
9013 allIn = allNodes.count( nodeIt->next() );
9015 theElements.insert(e );
9019 SMESH_MesherHelper helper(*myMesh);
9020 helper.SetIsQuadratic( true );
9021 helper.SetIsBiQuadratic( theToBiQuad );
9023 // add links of quadratic adjacent elements to the helper
9025 if ( !quadAdjacentElems[SMDSAbs_Edge].empty() )
9026 for ( eIt = quadAdjacentElems[SMDSAbs_Edge].begin();
9027 eIt != quadAdjacentElems[SMDSAbs_Edge].end(); ++eIt )
9029 helper.AddTLinks( static_cast< const SMDS_MeshEdge*> (*eIt) );
9031 if ( !quadAdjacentElems[SMDSAbs_Face].empty() )
9032 for ( eIt = quadAdjacentElems[SMDSAbs_Face].begin();
9033 eIt != quadAdjacentElems[SMDSAbs_Face].end(); ++eIt )
9035 helper.AddTLinks( static_cast< const SMDS_MeshFace*> (*eIt) );
9037 if ( !quadAdjacentElems[SMDSAbs_Volume].empty() )
9038 for ( eIt = quadAdjacentElems[SMDSAbs_Volume].begin();
9039 eIt != quadAdjacentElems[SMDSAbs_Volume].end(); ++eIt )
9041 helper.AddTLinks( static_cast< const SMDS_MeshVolume*> (*eIt) );
9044 // make quadratic (or bi-tri-quadratic) elements instead of linear ones
9046 SMESHDS_Mesh* meshDS = GetMeshDS();
9047 SMESHDS_SubMesh* smDS = 0;
9048 for ( eIt = theElements.begin(); eIt != theElements.end(); ++eIt )
9050 const SMDS_MeshElement* elem = *eIt;
9053 int nbCentralNodes = 0;
9054 switch ( elem->GetEntityType() ) {
9055 // linear convertible
9056 case SMDSEntity_Edge:
9057 case SMDSEntity_Triangle:
9058 case SMDSEntity_Quadrangle:
9059 case SMDSEntity_Tetra:
9060 case SMDSEntity_Pyramid:
9061 case SMDSEntity_Hexa:
9062 case SMDSEntity_Penta: alreadyOK = false; nbCentralNodes = 0; break;
9063 // quadratic that can become bi-quadratic
9064 case SMDSEntity_Quad_Triangle:
9065 case SMDSEntity_Quad_Quadrangle:
9066 case SMDSEntity_Quad_Hexa: alreadyOK =!theToBiQuad; nbCentralNodes = 0; break;
9068 case SMDSEntity_BiQuad_Triangle:
9069 case SMDSEntity_BiQuad_Quadrangle: alreadyOK = theToBiQuad; nbCentralNodes = 1; break;
9070 case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; nbCentralNodes = 7; break;
9072 default: alreadyOK = true;
9074 if ( alreadyOK ) continue;
9076 const SMDSAbs_ElementType type = elem->GetType();
9077 const int id = elem->GetID();
9078 const int nbNodes = elem->NbCornerNodes();
9079 vector<const SMDS_MeshNode *> nodes ( elem->begin_nodes(), elem->end_nodes());
9081 helper.SetSubShape( elem->getshapeId() );
9083 if ( !smDS || !smDS->Contains( elem ))
9084 smDS = meshDS->MeshElements( elem->getshapeId() );
9085 meshDS->RemoveFreeElement(elem, smDS, /*fromGroups=*/false);
9087 SMDS_MeshElement * newElem = 0;
9090 case 4: // cases for most frequently used element types go first (for optimization)
9091 if ( type == SMDSAbs_Volume )
9092 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9094 newElem = helper.AddFace (nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9097 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9098 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9101 newElem = helper.AddFace (nodes[0], nodes[1], nodes[2], id, theForce3d);
9104 newElem = helper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9107 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9108 nodes[4], id, theForce3d);
9111 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9112 nodes[4], nodes[5], id, theForce3d);
9116 ReplaceElemInGroups( elem, newElem, meshDS);
9117 if( newElem && smDS )
9118 smDS->AddElement( newElem );
9120 // remove central nodes
9121 for ( size_t i = nodes.size() - nbCentralNodes; i < nodes.size(); ++i )
9122 if ( nodes[i]->NbInverseElements() == 0 )
9123 meshDS->RemoveFreeNode( nodes[i], smDS, /*fromGroups=*/true );
9125 } // loop on theElements
9128 { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9129 // helper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9130 // helper.FixQuadraticElements( myError );
9131 SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
9135 //=======================================================================
9137 * \brief Convert quadratic elements to linear ones and remove quadratic nodes
9138 * \return int - nb of checked elements
9140 //=======================================================================
9142 int SMESH_MeshEditor::removeQuadElem(SMESHDS_SubMesh * theSm,
9143 SMDS_ElemIteratorPtr theItr,
9144 const int theShapeID)
9147 SMESHDS_Mesh* meshDS = GetMeshDS();
9149 while( theItr->more() )
9151 const SMDS_MeshElement* elem = theItr->next();
9153 if( elem && elem->IsQuadratic())
9155 int id = elem->GetID();
9156 int nbCornerNodes = elem->NbCornerNodes();
9157 SMDSAbs_ElementType aType = elem->GetType();
9159 vector<const SMDS_MeshNode *> nodes( elem->begin_nodes(), elem->end_nodes() );
9161 //remove a quadratic element
9162 if ( !theSm || !theSm->Contains( elem ))
9163 theSm = meshDS->MeshElements( elem->getshapeId() );
9164 meshDS->RemoveFreeElement( elem, theSm, /*fromGroups=*/false );
9166 // remove medium nodes
9167 for ( unsigned i = nbCornerNodes; i < nodes.size(); ++i )
9168 if ( nodes[i]->NbInverseElements() == 0 )
9169 meshDS->RemoveFreeNode( nodes[i], theSm );
9171 // add a linear element
9172 nodes.resize( nbCornerNodes );
9173 SMDS_MeshElement * newElem = AddElement( nodes, aType, false, id );
9174 ReplaceElemInGroups(elem, newElem, meshDS);
9175 if( theSm && newElem )
9176 theSm->AddElement( newElem );
9182 //=======================================================================
9183 //function : ConvertFromQuadratic
9185 //=======================================================================
9187 bool SMESH_MeshEditor::ConvertFromQuadratic()
9189 int nbCheckedElems = 0;
9190 if ( myMesh->HasShapeToMesh() )
9192 if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9194 SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9195 while ( smIt->more() ) {
9196 SMESH_subMesh* sm = smIt->next();
9197 if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() )
9198 nbCheckedElems += removeQuadElem( smDS, smDS->GetElements(), sm->GetId() );
9204 GetMeshDS()->NbEdges() + GetMeshDS()->NbFaces() + GetMeshDS()->NbVolumes();
9205 if ( nbCheckedElems < totalNbElems ) // not all elements are in submeshes
9207 SMESHDS_SubMesh *aSM = 0;
9208 removeQuadElem( aSM, GetMeshDS()->elementsIterator(), 0 );
9216 //================================================================================
9218 * \brief Return true if all medium nodes of the element are in the node set
9220 //================================================================================
9222 bool allMediumNodesIn(const SMDS_MeshElement* elem, TIDSortedNodeSet& nodeSet )
9224 for ( int i = elem->NbCornerNodes(); i < elem->NbNodes(); ++i )
9225 if ( !nodeSet.count( elem->GetNode(i) ))
9231 //================================================================================
9233 * \brief Makes given elements linear
9235 //================================================================================
9237 void SMESH_MeshEditor::ConvertFromQuadratic(TIDSortedElemSet& theElements)
9239 if ( theElements.empty() ) return;
9241 // collect IDs of medium nodes of theElements; some of these nodes will be removed
9242 set<int> mediumNodeIDs;
9243 TIDSortedElemSet::iterator eIt = theElements.begin();
9244 for ( ; eIt != theElements.end(); ++eIt )
9246 const SMDS_MeshElement* e = *eIt;
9247 for ( int i = e->NbCornerNodes(); i < e->NbNodes(); ++i )
9248 mediumNodeIDs.insert( e->GetNode(i)->GetID() );
9251 // replace given elements by linear ones
9252 SMDS_ElemIteratorPtr elemIt = elemSetIterator( theElements );
9253 removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9255 // we need to convert remaining elements whose all medium nodes are in mediumNodeIDs
9256 // except those elements sharing medium nodes of quadratic element whose medium nodes
9257 // are not all in mediumNodeIDs
9259 // get remaining medium nodes
9260 TIDSortedNodeSet mediumNodes;
9261 set<int>::iterator nIdsIt = mediumNodeIDs.begin();
9262 for ( ; nIdsIt != mediumNodeIDs.end(); ++nIdsIt )
9263 if ( const SMDS_MeshNode* n = GetMeshDS()->FindNode( *nIdsIt ))
9264 mediumNodes.insert( mediumNodes.end(), n );
9266 // find more quadratic elements to convert
9267 TIDSortedElemSet moreElemsToConvert;
9268 TIDSortedNodeSet::iterator nIt = mediumNodes.begin();
9269 for ( ; nIt != mediumNodes.end(); ++nIt )
9271 SMDS_ElemIteratorPtr invIt = (*nIt)->GetInverseElementIterator();
9272 while ( invIt->more() )
9274 const SMDS_MeshElement* e = invIt->next();
9275 if ( e->IsQuadratic() && allMediumNodesIn( e, mediumNodes ))
9277 // find a more complex element including e and
9278 // whose medium nodes are not in mediumNodes
9279 bool complexFound = false;
9280 for ( int type = e->GetType() + 1; type < SMDSAbs_0DElement; ++type )
9282 SMDS_ElemIteratorPtr invIt2 =
9283 (*nIt)->GetInverseElementIterator( SMDSAbs_ElementType( type ));
9284 while ( invIt2->more() )
9286 const SMDS_MeshElement* eComplex = invIt2->next();
9287 if ( eComplex->IsQuadratic() && !allMediumNodesIn( eComplex, mediumNodes))
9289 int nbCommonNodes = SMESH_MeshAlgos::GetCommonNodes( e, eComplex ).size();
9290 if ( nbCommonNodes == e->NbNodes())
9292 complexFound = true;
9293 type = SMDSAbs_NbElementTypes; // to quit from the outer loop
9299 if ( !complexFound )
9300 moreElemsToConvert.insert( e );
9304 elemIt = elemSetIterator( moreElemsToConvert );
9305 removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9308 //=======================================================================
9309 //function : SewSideElements
9311 //=======================================================================
9313 SMESH_MeshEditor::Sew_Error
9314 SMESH_MeshEditor::SewSideElements (TIDSortedElemSet& theSide1,
9315 TIDSortedElemSet& theSide2,
9316 const SMDS_MeshNode* theFirstNode1,
9317 const SMDS_MeshNode* theFirstNode2,
9318 const SMDS_MeshNode* theSecondNode1,
9319 const SMDS_MeshNode* theSecondNode2)
9321 myLastCreatedElems.Clear();
9322 myLastCreatedNodes.Clear();
9324 MESSAGE ("::::SewSideElements()");
9325 if ( theSide1.size() != theSide2.size() )
9326 return SEW_DIFF_NB_OF_ELEMENTS;
9328 Sew_Error aResult = SEW_OK;
9330 // 1. Build set of faces representing each side
9331 // 2. Find which nodes of the side 1 to merge with ones on the side 2
9332 // 3. Replace nodes in elements of the side 1 and remove replaced nodes
9334 // =======================================================================
9335 // 1. Build set of faces representing each side:
9336 // =======================================================================
9337 // a. build set of nodes belonging to faces
9338 // b. complete set of faces: find missing faces whose nodes are in set of nodes
9339 // c. create temporary faces representing side of volumes if correspondent
9340 // face does not exist
9342 SMESHDS_Mesh* aMesh = GetMeshDS();
9343 // TODO algoritm not OK with vtkUnstructuredGrid: 2 meshes can't share nodes
9344 //SMDS_Mesh aTmpFacesMesh; // try to use the same mesh
9345 TIDSortedElemSet faceSet1, faceSet2;
9346 set<const SMDS_MeshElement*> volSet1, volSet2;
9347 set<const SMDS_MeshNode*> nodeSet1, nodeSet2;
9348 TIDSortedElemSet * faceSetPtr[] = { &faceSet1, &faceSet2 };
9349 set<const SMDS_MeshElement*> * volSetPtr[] = { &volSet1, &volSet2 };
9350 set<const SMDS_MeshNode*> * nodeSetPtr[] = { &nodeSet1, &nodeSet2 };
9351 TIDSortedElemSet * elemSetPtr[] = { &theSide1, &theSide2 };
9352 int iSide, iFace, iNode;
9354 list<const SMDS_MeshElement* > tempFaceList;
9355 for ( iSide = 0; iSide < 2; iSide++ ) {
9356 set<const SMDS_MeshNode*> * nodeSet = nodeSetPtr[ iSide ];
9357 TIDSortedElemSet * elemSet = elemSetPtr[ iSide ];
9358 TIDSortedElemSet * faceSet = faceSetPtr[ iSide ];
9359 set<const SMDS_MeshElement*> * volSet = volSetPtr [ iSide ];
9360 set<const SMDS_MeshElement*>::iterator vIt;
9361 TIDSortedElemSet::iterator eIt;
9362 set<const SMDS_MeshNode*>::iterator nIt;
9364 // check that given nodes belong to given elements
9365 const SMDS_MeshNode* n1 = ( iSide == 0 ) ? theFirstNode1 : theFirstNode2;
9366 const SMDS_MeshNode* n2 = ( iSide == 0 ) ? theSecondNode1 : theSecondNode2;
9367 int firstIndex = -1, secondIndex = -1;
9368 for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9369 const SMDS_MeshElement* elem = *eIt;
9370 if ( firstIndex < 0 ) firstIndex = elem->GetNodeIndex( n1 );
9371 if ( secondIndex < 0 ) secondIndex = elem->GetNodeIndex( n2 );
9372 if ( firstIndex > -1 && secondIndex > -1 ) break;
9374 if ( firstIndex < 0 || secondIndex < 0 ) {
9375 // we can simply return until temporary faces created
9376 return (iSide == 0 ) ? SEW_BAD_SIDE1_NODES : SEW_BAD_SIDE2_NODES;
9379 // -----------------------------------------------------------
9380 // 1a. Collect nodes of existing faces
9381 // and build set of face nodes in order to detect missing
9382 // faces corresponding to sides of volumes
9383 // -----------------------------------------------------------
9385 set< set <const SMDS_MeshNode*> > setOfFaceNodeSet;
9387 // loop on the given element of a side
9388 for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9389 //const SMDS_MeshElement* elem = *eIt;
9390 const SMDS_MeshElement* elem = *eIt;
9391 if ( elem->GetType() == SMDSAbs_Face ) {
9392 faceSet->insert( elem );
9393 set <const SMDS_MeshNode*> faceNodeSet;
9394 SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
9395 while ( nodeIt->more() ) {
9396 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9397 nodeSet->insert( n );
9398 faceNodeSet.insert( n );
9400 setOfFaceNodeSet.insert( faceNodeSet );
9402 else if ( elem->GetType() == SMDSAbs_Volume )
9403 volSet->insert( elem );
9405 // ------------------------------------------------------------------------------
9406 // 1b. Complete set of faces: find missing faces whose nodes are in set of nodes
9407 // ------------------------------------------------------------------------------
9409 for ( nIt = nodeSet->begin(); nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
9410 SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
9411 while ( fIt->more() ) { // loop on faces sharing a node
9412 const SMDS_MeshElement* f = fIt->next();
9413 if ( faceSet->find( f ) == faceSet->end() ) {
9414 // check if all nodes are in nodeSet and
9415 // complete setOfFaceNodeSet if they are
9416 set <const SMDS_MeshNode*> faceNodeSet;
9417 SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
9418 bool allInSet = true;
9419 while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
9420 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9421 if ( nodeSet->find( n ) == nodeSet->end() )
9424 faceNodeSet.insert( n );
9427 faceSet->insert( f );
9428 setOfFaceNodeSet.insert( faceNodeSet );
9434 // -------------------------------------------------------------------------
9435 // 1c. Create temporary faces representing sides of volumes if correspondent
9436 // face does not exist
9437 // -------------------------------------------------------------------------
9439 if ( !volSet->empty() ) {
9440 //int nodeSetSize = nodeSet->size();
9442 // loop on given volumes
9443 for ( vIt = volSet->begin(); vIt != volSet->end(); vIt++ ) {
9444 SMDS_VolumeTool vol (*vIt);
9445 // loop on volume faces: find free faces
9446 // --------------------------------------
9447 list<const SMDS_MeshElement* > freeFaceList;
9448 for ( iFace = 0; iFace < vol.NbFaces(); iFace++ ) {
9449 if ( !vol.IsFreeFace( iFace ))
9451 // check if there is already a face with same nodes in a face set
9452 const SMDS_MeshElement* aFreeFace = 0;
9453 const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iFace );
9454 int nbNodes = vol.NbFaceNodes( iFace );
9455 set <const SMDS_MeshNode*> faceNodeSet;
9456 vol.GetFaceNodes( iFace, faceNodeSet );
9457 bool isNewFace = setOfFaceNodeSet.insert( faceNodeSet ).second;
9459 // no such a face is given but it still can exist, check it
9460 vector<const SMDS_MeshNode *> nodes ( fNodes, fNodes + nbNodes);
9461 aFreeFace = aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false );
9464 // create a temporary face
9465 if ( nbNodes == 3 ) {
9466 //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2] );
9467 aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2] );
9469 else if ( nbNodes == 4 ) {
9470 //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
9471 aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
9474 vector<const SMDS_MeshNode *> poly_nodes ( fNodes, & fNodes[nbNodes]);
9475 //aFreeFace = aTmpFacesMesh.AddPolygonalFace(poly_nodes);
9476 aFreeFace = aMesh->AddPolygonalFace(poly_nodes);
9479 tempFaceList.push_back( aFreeFace );
9483 freeFaceList.push_back( aFreeFace );
9485 } // loop on faces of a volume
9487 // choose one of several free faces of a volume
9488 // --------------------------------------------
9489 if ( freeFaceList.size() > 1 ) {
9490 // choose a face having max nb of nodes shared by other elems of a side
9491 int maxNbNodes = -1;
9492 list<const SMDS_MeshElement* >::iterator fIt = freeFaceList.begin();
9493 while ( fIt != freeFaceList.end() ) { // loop on free faces
9494 int nbSharedNodes = 0;
9495 SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
9496 while ( nodeIt->more() ) { // loop on free face nodes
9497 const SMDS_MeshNode* n =
9498 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9499 SMDS_ElemIteratorPtr invElemIt = n->GetInverseElementIterator();
9500 while ( invElemIt->more() ) {
9501 const SMDS_MeshElement* e = invElemIt->next();
9502 nbSharedNodes += faceSet->count( e );
9503 nbSharedNodes += elemSet->count( e );
9506 if ( nbSharedNodes > maxNbNodes ) {
9507 maxNbNodes = nbSharedNodes;
9508 freeFaceList.erase( freeFaceList.begin(), fIt++ );
9510 else if ( nbSharedNodes == maxNbNodes ) {
9514 freeFaceList.erase( fIt++ ); // here fIt++ occurs before erase
9517 if ( freeFaceList.size() > 1 )
9519 // could not choose one face, use another way
9520 // choose a face most close to the bary center of the opposite side
9521 gp_XYZ aBC( 0., 0., 0. );
9522 set <const SMDS_MeshNode*> addedNodes;
9523 TIDSortedElemSet * elemSet2 = elemSetPtr[ 1 - iSide ];
9524 eIt = elemSet2->begin();
9525 for ( eIt = elemSet2->begin(); eIt != elemSet2->end(); eIt++ ) {
9526 SMDS_ElemIteratorPtr nodeIt = (*eIt)->nodesIterator();
9527 while ( nodeIt->more() ) { // loop on free face nodes
9528 const SMDS_MeshNode* n =
9529 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9530 if ( addedNodes.insert( n ).second )
9531 aBC += gp_XYZ( n->X(),n->Y(),n->Z() );
9534 aBC /= addedNodes.size();
9535 double minDist = DBL_MAX;
9536 fIt = freeFaceList.begin();
9537 while ( fIt != freeFaceList.end() ) { // loop on free faces
9539 SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
9540 while ( nodeIt->more() ) { // loop on free face nodes
9541 const SMDS_MeshNode* n =
9542 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9543 gp_XYZ p( n->X(),n->Y(),n->Z() );
9544 dist += ( aBC - p ).SquareModulus();
9546 if ( dist < minDist ) {
9548 freeFaceList.erase( freeFaceList.begin(), fIt++ );
9551 fIt = freeFaceList.erase( fIt++ );
9554 } // choose one of several free faces of a volume
9556 if ( freeFaceList.size() == 1 ) {
9557 const SMDS_MeshElement* aFreeFace = freeFaceList.front();
9558 faceSet->insert( aFreeFace );
9559 // complete a node set with nodes of a found free face
9560 // for ( iNode = 0; iNode < ; iNode++ )
9561 // nodeSet->insert( fNodes[ iNode ] );
9564 } // loop on volumes of a side
9566 // // complete a set of faces if new nodes in a nodeSet appeared
9567 // // ----------------------------------------------------------
9568 // if ( nodeSetSize != nodeSet->size() ) {
9569 // for ( ; nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
9570 // SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
9571 // while ( fIt->more() ) { // loop on faces sharing a node
9572 // const SMDS_MeshElement* f = fIt->next();
9573 // if ( faceSet->find( f ) == faceSet->end() ) {
9574 // // check if all nodes are in nodeSet and
9575 // // complete setOfFaceNodeSet if they are
9576 // set <const SMDS_MeshNode*> faceNodeSet;
9577 // SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
9578 // bool allInSet = true;
9579 // while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
9580 // const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9581 // if ( nodeSet->find( n ) == nodeSet->end() )
9582 // allInSet = false;
9584 // faceNodeSet.insert( n );
9586 // if ( allInSet ) {
9587 // faceSet->insert( f );
9588 // setOfFaceNodeSet.insert( faceNodeSet );
9594 } // Create temporary faces, if there are volumes given
9597 if ( faceSet1.size() != faceSet2.size() ) {
9598 // delete temporary faces: they are in reverseElements of actual nodes
9599 // SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
9600 // while ( tmpFaceIt->more() )
9601 // aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
9602 // list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
9603 // for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
9604 // aMesh->RemoveElement(*tmpFaceIt);
9605 MESSAGE("Diff nb of faces");
9606 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9609 // ============================================================
9610 // 2. Find nodes to merge:
9611 // bind a node to remove to a node to put instead
9612 // ============================================================
9614 TNodeNodeMap nReplaceMap; // bind a node to remove to a node to put instead
9615 if ( theFirstNode1 != theFirstNode2 )
9616 nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
9617 if ( theSecondNode1 != theSecondNode2 )
9618 nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
9620 LinkID_Gen aLinkID_Gen( GetMeshDS() );
9621 set< long > linkIdSet; // links to process
9622 linkIdSet.insert( aLinkID_Gen.GetLinkID( theFirstNode1, theSecondNode1 ));
9624 typedef pair< const SMDS_MeshNode*, const SMDS_MeshNode* > NLink;
9625 list< NLink > linkList[2];
9626 linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
9627 linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
9628 // loop on links in linkList; find faces by links and append links
9629 // of the found faces to linkList
9630 list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
9631 for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ )
9633 NLink link[] = { *linkIt[0], *linkIt[1] };
9634 long linkID = aLinkID_Gen.GetLinkID( link[0].first, link[0].second );
9635 if ( !linkIdSet.count( linkID ) )
9638 // by links, find faces in the face sets,
9639 // and find indices of link nodes in the found faces;
9640 // in a face set, there is only one or no face sharing a link
9641 // ---------------------------------------------------------------
9643 const SMDS_MeshElement* face[] = { 0, 0 };
9644 vector<const SMDS_MeshNode*> fnodes[2];
9645 int iLinkNode[2][2];
9646 TIDSortedElemSet avoidSet;
9647 for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
9648 const SMDS_MeshNode* n1 = link[iSide].first;
9649 const SMDS_MeshNode* n2 = link[iSide].second;
9650 //cout << "Side " << iSide << " ";
9651 //cout << "L( " << n1->GetID() << ", " << n2->GetID() << " ) " << endl;
9652 // find a face by two link nodes
9653 face[ iSide ] = SMESH_MeshAlgos::FindFaceInSet( n1, n2,
9654 *faceSetPtr[ iSide ], avoidSet,
9655 &iLinkNode[iSide][0],
9656 &iLinkNode[iSide][1] );
9659 //cout << " F " << face[ iSide]->GetID() <<endl;
9660 faceSetPtr[ iSide ]->erase( face[ iSide ]);
9661 // put face nodes to fnodes
9662 if ( face[ iSide ]->IsQuadratic() )
9664 // use interlaced nodes iterator
9665 const SMDS_VtkFace* F = dynamic_cast<const SMDS_VtkFace*>( face[ iSide ]);
9666 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
9667 SMDS_ElemIteratorPtr nIter = F->interlacedNodesElemIterator();
9668 while ( nIter->more() )
9669 fnodes[ iSide ].push_back( cast2Node( nIter->next() ));
9673 fnodes[ iSide ].assign( face[ iSide ]->begin_nodes(),
9674 face[ iSide ]->end_nodes() );
9676 fnodes[ iSide ].push_back( fnodes[ iSide ].front());
9680 // check similarity of elements of the sides
9681 if (aResult == SEW_OK && (( face[0] && !face[1] ) || ( !face[0] && face[1] ))) {
9682 MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
9683 if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
9684 aResult = ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
9687 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9689 break; // do not return because it's necessary to remove tmp faces
9692 // set nodes to merge
9693 // -------------------
9695 if ( face[0] && face[1] ) {
9696 const int nbNodes = face[0]->NbNodes();
9697 if ( nbNodes != face[1]->NbNodes() ) {
9698 MESSAGE("Diff nb of face nodes");
9699 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9700 break; // do not return because it s necessary to remove tmp faces
9702 bool reverse[] = { false, false }; // order of nodes in the link
9703 for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
9704 // analyse link orientation in faces
9705 int i1 = iLinkNode[ iSide ][ 0 ];
9706 int i2 = iLinkNode[ iSide ][ 1 ];
9707 reverse[ iSide ] = Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1;
9709 int di1 = reverse[0] ? -1 : +1, i1 = iLinkNode[0][1] + di1;
9710 int di2 = reverse[1] ? -1 : +1, i2 = iLinkNode[1][1] + di2;
9711 for ( int i = nbNodes - 2; i > 0; --i, i1 += di1, i2 += di2 )
9713 nReplaceMap.insert ( make_pair ( fnodes[0][ ( i1 + nbNodes ) % nbNodes ],
9714 fnodes[1][ ( i2 + nbNodes ) % nbNodes ]));
9717 // add other links of the faces to linkList
9718 // -----------------------------------------
9720 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
9721 linkID = aLinkID_Gen.GetLinkID( fnodes[0][iNode], fnodes[0][iNode+1] );
9722 pair< set<long>::iterator, bool > iter_isnew = linkIdSet.insert( linkID );
9723 if ( !iter_isnew.second ) { // already in a set: no need to process
9724 linkIdSet.erase( iter_isnew.first );
9726 else // new in set == encountered for the first time: add
9728 const SMDS_MeshNode* n1 = fnodes[0][ iNode ];
9729 const SMDS_MeshNode* n2 = fnodes[0][ iNode + 1];
9730 linkList[0].push_back ( NLink( n1, n2 ));
9731 linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
9736 if ( faceSetPtr[0]->empty() || faceSetPtr[1]->empty() )
9739 } // loop on link lists
9741 if ( aResult == SEW_OK &&
9742 ( //linkIt[0] != linkList[0].end() ||
9743 !faceSetPtr[0]->empty() || !faceSetPtr[1]->empty() )) {
9744 MESSAGE( (linkIt[0] != linkList[0].end()) <<" "<< (faceSetPtr[0]->empty()) <<
9745 " " << (faceSetPtr[1]->empty()));
9746 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9749 // ====================================================================
9750 // 3. Replace nodes in elements of the side 1 and remove replaced nodes
9751 // ====================================================================
9753 // delete temporary faces
9754 // SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
9755 // while ( tmpFaceIt->more() )
9756 // aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
9757 list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
9758 for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
9759 aMesh->RemoveElement(*tmpFaceIt);
9761 if ( aResult != SEW_OK)
9764 list< int > nodeIDsToRemove/*, elemIDsToRemove*/;
9765 // loop on nodes replacement map
9766 TNodeNodeMap::iterator nReplaceMapIt = nReplaceMap.begin(), nnIt;
9767 for ( ; nReplaceMapIt != nReplaceMap.end(); nReplaceMapIt++ )
9768 if ( (*nReplaceMapIt).first != (*nReplaceMapIt).second ) {
9769 const SMDS_MeshNode* nToRemove = (*nReplaceMapIt).first;
9770 nodeIDsToRemove.push_back( nToRemove->GetID() );
9771 // loop on elements sharing nToRemove
9772 SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
9773 while ( invElemIt->more() ) {
9774 const SMDS_MeshElement* e = invElemIt->next();
9775 // get a new suite of nodes: make replacement
9776 int nbReplaced = 0, i = 0, nbNodes = e->NbNodes();
9777 vector< const SMDS_MeshNode*> nodes( nbNodes );
9778 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
9779 while ( nIt->more() ) {
9780 const SMDS_MeshNode* n =
9781 static_cast<const SMDS_MeshNode*>( nIt->next() );
9782 nnIt = nReplaceMap.find( n );
9783 if ( nnIt != nReplaceMap.end() ) {
9789 // if ( nbReplaced == nbNodes && e->GetType() == SMDSAbs_Face )
9790 // elemIDsToRemove.push_back( e->GetID() );
9794 SMDSAbs_ElementType etyp = e->GetType();
9795 SMDS_MeshElement* newElem = this->AddElement(nodes, etyp, false);
9798 myLastCreatedElems.Append(newElem);
9799 AddToSameGroups(newElem, e, aMesh);
9800 int aShapeId = e->getshapeId();
9803 aMesh->SetMeshElementOnShape( newElem, aShapeId );
9806 aMesh->RemoveElement(e);
9811 Remove( nodeIDsToRemove, true );
9816 //================================================================================
9818 * \brief Find corresponding nodes in two sets of faces
9819 * \param theSide1 - first face set
9820 * \param theSide2 - second first face
9821 * \param theFirstNode1 - a boundary node of set 1
9822 * \param theFirstNode2 - a node of set 2 corresponding to theFirstNode1
9823 * \param theSecondNode1 - a boundary node of set 1 linked with theFirstNode1
9824 * \param theSecondNode2 - a node of set 2 corresponding to theSecondNode1
9825 * \param nReplaceMap - output map of corresponding nodes
9826 * \return bool - is a success or not
9828 //================================================================================
9831 //#define DEBUG_MATCHING_NODES
9834 SMESH_MeshEditor::Sew_Error
9835 SMESH_MeshEditor::FindMatchingNodes(set<const SMDS_MeshElement*>& theSide1,
9836 set<const SMDS_MeshElement*>& theSide2,
9837 const SMDS_MeshNode* theFirstNode1,
9838 const SMDS_MeshNode* theFirstNode2,
9839 const SMDS_MeshNode* theSecondNode1,
9840 const SMDS_MeshNode* theSecondNode2,
9841 TNodeNodeMap & nReplaceMap)
9843 set<const SMDS_MeshElement*> * faceSetPtr[] = { &theSide1, &theSide2 };
9845 nReplaceMap.clear();
9846 if ( theFirstNode1 != theFirstNode2 )
9847 nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
9848 if ( theSecondNode1 != theSecondNode2 )
9849 nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
9851 set< SMESH_TLink > linkSet; // set of nodes where order of nodes is ignored
9852 linkSet.insert( SMESH_TLink( theFirstNode1, theSecondNode1 ));
9854 list< NLink > linkList[2];
9855 linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
9856 linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
9858 // loop on links in linkList; find faces by links and append links
9859 // of the found faces to linkList
9860 list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
9861 for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ ) {
9862 NLink link[] = { *linkIt[0], *linkIt[1] };
9863 if ( linkSet.find( link[0] ) == linkSet.end() )
9866 // by links, find faces in the face sets,
9867 // and find indices of link nodes in the found faces;
9868 // in a face set, there is only one or no face sharing a link
9869 // ---------------------------------------------------------------
9871 const SMDS_MeshElement* face[] = { 0, 0 };
9872 list<const SMDS_MeshNode*> notLinkNodes[2];
9873 //bool reverse[] = { false, false }; // order of notLinkNodes
9875 for ( int iSide = 0; iSide < 2; iSide++ ) // loop on 2 sides
9877 const SMDS_MeshNode* n1 = link[iSide].first;
9878 const SMDS_MeshNode* n2 = link[iSide].second;
9879 set<const SMDS_MeshElement*> * faceSet = faceSetPtr[ iSide ];
9880 set< const SMDS_MeshElement* > facesOfNode1;
9881 for ( int iNode = 0; iNode < 2; iNode++ ) // loop on 2 nodes of a link
9883 // during a loop of the first node, we find all faces around n1,
9884 // during a loop of the second node, we find one face sharing both n1 and n2
9885 const SMDS_MeshNode* n = iNode ? n1 : n2; // a node of a link
9886 SMDS_ElemIteratorPtr fIt = n->GetInverseElementIterator(SMDSAbs_Face);
9887 while ( fIt->more() ) { // loop on faces sharing a node
9888 const SMDS_MeshElement* f = fIt->next();
9889 if (faceSet->find( f ) != faceSet->end() && // f is in face set
9890 ! facesOfNode1.insert( f ).second ) // f encounters twice
9892 if ( face[ iSide ] ) {
9893 MESSAGE( "2 faces per link " );
9894 return ( iSide ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
9897 faceSet->erase( f );
9899 // get not link nodes
9900 int nbN = f->NbNodes();
9901 if ( f->IsQuadratic() )
9903 nbNodes[ iSide ] = nbN;
9904 list< const SMDS_MeshNode* > & nodes = notLinkNodes[ iSide ];
9905 int i1 = f->GetNodeIndex( n1 );
9906 int i2 = f->GetNodeIndex( n2 );
9907 int iEnd = nbN, iBeg = -1, iDelta = 1;
9908 bool reverse = ( Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1 );
9910 std::swap( iEnd, iBeg ); iDelta = -1;
9915 if ( i == iEnd ) i = iBeg + iDelta;
9916 if ( i == i1 ) break;
9917 nodes.push_back ( f->GetNode( i ) );
9923 // check similarity of elements of the sides
9924 if (( face[0] && !face[1] ) || ( !face[0] && face[1] )) {
9925 MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
9926 if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
9927 return ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
9930 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9934 // set nodes to merge
9935 // -------------------
9937 if ( face[0] && face[1] ) {
9938 if ( nbNodes[0] != nbNodes[1] ) {
9939 MESSAGE("Diff nb of face nodes");
9940 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9942 #ifdef DEBUG_MATCHING_NODES
9943 MESSAGE ( " Link 1: " << link[0].first->GetID() <<" "<< link[0].second->GetID()
9944 << " F 1: " << face[0] << "| Link 2: " << link[1].first->GetID() <<" "
9945 << link[1].second->GetID() << " F 2: " << face[1] << " | Bind: " ) ;
9947 int nbN = nbNodes[0];
9949 list<const SMDS_MeshNode*>::iterator n1 = notLinkNodes[0].begin();
9950 list<const SMDS_MeshNode*>::iterator n2 = notLinkNodes[1].begin();
9951 for ( int i = 0 ; i < nbN - 2; ++i ) {
9952 #ifdef DEBUG_MATCHING_NODES
9953 MESSAGE ( (*n1)->GetID() << " to " << (*n2)->GetID() );
9955 nReplaceMap.insert( make_pair( *(n1++), *(n2++) ));
9959 // add other links of the face 1 to linkList
9960 // -----------------------------------------
9962 const SMDS_MeshElement* f0 = face[0];
9963 const SMDS_MeshNode* n1 = f0->GetNode( nbN - 1 );
9964 for ( int i = 0; i < nbN; i++ )
9966 const SMDS_MeshNode* n2 = f0->GetNode( i );
9967 pair< set< SMESH_TLink >::iterator, bool > iter_isnew =
9968 linkSet.insert( SMESH_TLink( n1, n2 ));
9969 if ( !iter_isnew.second ) { // already in a set: no need to process
9970 linkSet.erase( iter_isnew.first );
9972 else // new in set == encountered for the first time: add
9974 #ifdef DEBUG_MATCHING_NODES
9975 MESSAGE ( "Add link 1: " << n1->GetID() << " " << n2->GetID() << " "
9976 << " | link 2: " << nReplaceMap[n1]->GetID() << " " << nReplaceMap[n2]->GetID() << " " );
9978 linkList[0].push_back ( NLink( n1, n2 ));
9979 linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
9984 } // loop on link lists
9989 //================================================================================
9991 * \brief Create elements equal (on same nodes) to given ones
9992 * \param [in] theElements - a set of elems to duplicate. If it is empty, all
9993 * elements of the uppest dimension are duplicated.
9995 //================================================================================
9997 void SMESH_MeshEditor::DoubleElements( const TIDSortedElemSet& theElements )
10000 SMESHDS_Mesh* mesh = GetMeshDS();
10002 // get an element type and an iterator over elements
10004 SMDSAbs_ElementType type;
10005 SMDS_ElemIteratorPtr elemIt;
10006 vector< const SMDS_MeshElement* > allElems;
10007 if ( theElements.empty() )
10009 if ( mesh->NbNodes() == 0 )
10011 // get most complex type
10012 SMDSAbs_ElementType types[SMDSAbs_NbElementTypes] = {
10013 SMDSAbs_Volume, SMDSAbs_Face, SMDSAbs_Edge,
10014 SMDSAbs_0DElement, SMDSAbs_Ball, SMDSAbs_Node
10016 for ( int i = 0; i < SMDSAbs_NbElementTypes; ++i )
10017 if ( mesh->GetMeshInfo().NbElements( types[i] ))
10022 // put all elements in the vector <allElems>
10023 allElems.reserve( mesh->GetMeshInfo().NbElements( type ));
10024 elemIt = mesh->elementsIterator( type );
10025 while ( elemIt->more() )
10026 allElems.push_back( elemIt->next());
10027 elemIt = elemSetIterator( allElems );
10031 type = (*theElements.begin())->GetType();
10032 elemIt = elemSetIterator( theElements );
10035 // duplicate elements
10037 if ( type == SMDSAbs_Ball )
10039 SMDS_UnstructuredGrid* vtkGrid = mesh->getGrid();
10040 while ( elemIt->more() )
10042 const SMDS_MeshElement* elem = elemIt->next();
10043 if ( elem->GetType() != SMDSAbs_Ball )
10045 if (( elem = mesh->AddBall( elem->GetNode(0),
10046 vtkGrid->GetBallDiameter( elem->getVtkId() ))))
10047 myLastCreatedElems.Append( elem );
10052 vector< const SMDS_MeshNode* > nodes;
10053 while ( elemIt->more() )
10055 const SMDS_MeshElement* elem = elemIt->next();
10056 if ( elem->GetType() != type )
10059 nodes.assign( elem->begin_nodes(), elem->end_nodes() );
10061 if ( type == SMDSAbs_Volume && elem->GetVtkType() == VTK_POLYHEDRON )
10063 std::vector<int> quantities =
10064 static_cast< const SMDS_VtkVolume* >( elem )->GetQuantities();
10065 elem = mesh->AddPolyhedralVolume( nodes, quantities );
10069 AddElement( nodes, type, elem->IsPoly() );
10070 elem = 0; // myLastCreatedElems is already filled
10073 myLastCreatedElems.Append( elem );
10078 //================================================================================
10080 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10081 \param theElems - the list of elements (edges or faces) to be replicated
10082 The nodes for duplication could be found from these elements
10083 \param theNodesNot - list of nodes to NOT replicate
10084 \param theAffectedElems - the list of elements (cells and edges) to which the
10085 replicated nodes should be associated to.
10086 \return TRUE if operation has been completed successfully, FALSE otherwise
10088 //================================================================================
10090 bool SMESH_MeshEditor::DoubleNodes( const TIDSortedElemSet& theElems,
10091 const TIDSortedElemSet& theNodesNot,
10092 const TIDSortedElemSet& theAffectedElems )
10094 myLastCreatedElems.Clear();
10095 myLastCreatedNodes.Clear();
10097 if ( theElems.size() == 0 )
10100 SMESHDS_Mesh* aMeshDS = GetMeshDS();
10105 std::map< const SMDS_MeshNode*, const SMDS_MeshNode* > anOldNodeToNewNode;
10106 // duplicate elements and nodes
10107 res = doubleNodes( aMeshDS, theElems, theNodesNot, anOldNodeToNewNode, true );
10108 // replce nodes by duplications
10109 res = doubleNodes( aMeshDS, theAffectedElems, theNodesNot, anOldNodeToNewNode, false );
10113 //================================================================================
10115 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10116 \param theMeshDS - mesh instance
10117 \param theElems - the elements replicated or modified (nodes should be changed)
10118 \param theNodesNot - nodes to NOT replicate
10119 \param theNodeNodeMap - relation of old node to new created node
10120 \param theIsDoubleElem - flag os to replicate element or modify
10121 \return TRUE if operation has been completed successfully, FALSE otherwise
10123 //================================================================================
10125 bool SMESH_MeshEditor::doubleNodes( SMESHDS_Mesh* theMeshDS,
10126 const TIDSortedElemSet& theElems,
10127 const TIDSortedElemSet& theNodesNot,
10128 std::map< const SMDS_MeshNode*,
10129 const SMDS_MeshNode* >& theNodeNodeMap,
10130 const bool theIsDoubleElem )
10132 MESSAGE("doubleNodes");
10133 // iterate on through element and duplicate them (by nodes duplication)
10135 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10136 for ( ; elemItr != theElems.end(); ++elemItr )
10138 const SMDS_MeshElement* anElem = *elemItr;
10142 bool isDuplicate = false;
10143 // duplicate nodes to duplicate element
10144 std::vector<const SMDS_MeshNode*> newNodes( anElem->NbNodes() );
10145 SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
10147 while ( anIter->more() )
10150 SMDS_MeshNode* aCurrNode = (SMDS_MeshNode*)anIter->next();
10151 SMDS_MeshNode* aNewNode = aCurrNode;
10152 if ( theNodeNodeMap.find( aCurrNode ) != theNodeNodeMap.end() )
10153 aNewNode = (SMDS_MeshNode*)theNodeNodeMap[ aCurrNode ];
10154 else if ( theIsDoubleElem && theNodesNot.find( aCurrNode ) == theNodesNot.end() )
10157 aNewNode = theMeshDS->AddNode( aCurrNode->X(), aCurrNode->Y(), aCurrNode->Z() );
10158 theNodeNodeMap[ aCurrNode ] = aNewNode;
10159 myLastCreatedNodes.Append( aNewNode );
10161 isDuplicate |= (aCurrNode != aNewNode);
10162 newNodes[ ind++ ] = aNewNode;
10164 if ( !isDuplicate )
10167 if ( theIsDoubleElem )
10168 AddElement(newNodes, anElem->GetType(), anElem->IsPoly());
10171 MESSAGE("ChangeElementNodes");
10172 theMeshDS->ChangeElementNodes( anElem, &newNodes[ 0 ], anElem->NbNodes() );
10179 //================================================================================
10181 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10182 \param theNodes - identifiers of nodes to be doubled
10183 \param theModifiedElems - identifiers of elements to be updated by the new (doubled)
10184 nodes. If list of element identifiers is empty then nodes are doubled but
10185 they not assigned to elements
10186 \return TRUE if operation has been completed successfully, FALSE otherwise
10188 //================================================================================
10190 bool SMESH_MeshEditor::DoubleNodes( const std::list< int >& theListOfNodes,
10191 const std::list< int >& theListOfModifiedElems )
10193 MESSAGE("DoubleNodes");
10194 myLastCreatedElems.Clear();
10195 myLastCreatedNodes.Clear();
10197 if ( theListOfNodes.size() == 0 )
10200 SMESHDS_Mesh* aMeshDS = GetMeshDS();
10204 // iterate through nodes and duplicate them
10206 std::map< const SMDS_MeshNode*, const SMDS_MeshNode* > anOldNodeToNewNode;
10208 std::list< int >::const_iterator aNodeIter;
10209 for ( aNodeIter = theListOfNodes.begin(); aNodeIter != theListOfNodes.end(); ++aNodeIter )
10211 int aCurr = *aNodeIter;
10212 SMDS_MeshNode* aNode = (SMDS_MeshNode*)aMeshDS->FindNode( aCurr );
10218 const SMDS_MeshNode* aNewNode = aMeshDS->AddNode( aNode->X(), aNode->Y(), aNode->Z() );
10221 anOldNodeToNewNode[ aNode ] = aNewNode;
10222 myLastCreatedNodes.Append( aNewNode );
10226 // Create map of new nodes for modified elements
10228 std::map< SMDS_MeshElement*, vector<const SMDS_MeshNode*> > anElemToNodes;
10230 std::list< int >::const_iterator anElemIter;
10231 for ( anElemIter = theListOfModifiedElems.begin();
10232 anElemIter != theListOfModifiedElems.end(); ++anElemIter )
10234 int aCurr = *anElemIter;
10235 SMDS_MeshElement* anElem = (SMDS_MeshElement*)aMeshDS->FindElement( aCurr );
10239 vector<const SMDS_MeshNode*> aNodeArr( anElem->NbNodes() );
10241 SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
10243 while ( anIter->more() )
10245 SMDS_MeshNode* aCurrNode = (SMDS_MeshNode*)anIter->next();
10246 if ( aCurr && anOldNodeToNewNode.find( aCurrNode ) != anOldNodeToNewNode.end() )
10248 const SMDS_MeshNode* aNewNode = anOldNodeToNewNode[ aCurrNode ];
10249 aNodeArr[ ind++ ] = aNewNode;
10252 aNodeArr[ ind++ ] = aCurrNode;
10254 anElemToNodes[ anElem ] = aNodeArr;
10257 // Change nodes of elements
10259 std::map< SMDS_MeshElement*, vector<const SMDS_MeshNode*> >::iterator
10260 anElemToNodesIter = anElemToNodes.begin();
10261 for ( ; anElemToNodesIter != anElemToNodes.end(); ++anElemToNodesIter )
10263 const SMDS_MeshElement* anElem = anElemToNodesIter->first;
10264 vector<const SMDS_MeshNode*> aNodeArr = anElemToNodesIter->second;
10267 MESSAGE("ChangeElementNodes");
10268 aMeshDS->ChangeElementNodes( anElem, &aNodeArr[ 0 ], anElem->NbNodes() );
10277 //================================================================================
10279 \brief Check if element located inside shape
10280 \return TRUE if IN or ON shape, FALSE otherwise
10282 //================================================================================
10284 template<class Classifier>
10285 bool isInside(const SMDS_MeshElement* theElem,
10286 Classifier& theClassifier,
10287 const double theTol)
10289 gp_XYZ centerXYZ (0, 0, 0);
10290 SMDS_ElemIteratorPtr aNodeItr = theElem->nodesIterator();
10291 while (aNodeItr->more())
10292 centerXYZ += SMESH_TNodeXYZ(cast2Node( aNodeItr->next()));
10294 gp_Pnt aPnt = centerXYZ / theElem->NbNodes();
10295 theClassifier.Perform(aPnt, theTol);
10296 TopAbs_State aState = theClassifier.State();
10297 return (aState == TopAbs_IN || aState == TopAbs_ON );
10300 //================================================================================
10302 * \brief Classifier of the 3D point on the TopoDS_Face
10303 * with interaface suitable for isInside()
10305 //================================================================================
10307 struct _FaceClassifier
10309 Extrema_ExtPS _extremum;
10310 BRepAdaptor_Surface _surface;
10311 TopAbs_State _state;
10313 _FaceClassifier(const TopoDS_Face& face):_extremum(),_surface(face),_state(TopAbs_OUT)
10315 _extremum.Initialize( _surface,
10316 _surface.FirstUParameter(), _surface.LastUParameter(),
10317 _surface.FirstVParameter(), _surface.LastVParameter(),
10318 _surface.Tolerance(), _surface.Tolerance() );
10320 void Perform(const gp_Pnt& aPnt, double theTol)
10322 _state = TopAbs_OUT;
10323 _extremum.Perform(aPnt);
10324 if ( _extremum.IsDone() )
10325 for ( int iSol = 1; iSol <= _extremum.NbExt() && _state == TopAbs_OUT; ++iSol)
10326 #if OCC_VERSION_LARGE > 0x06040000 // Porting to OCCT6.5.1
10327 _state = ( _extremum.SquareDistance(iSol) <= theTol ? TopAbs_IN : TopAbs_OUT );
10329 _state = ( _extremum.Value(iSol) <= theTol ? TopAbs_IN : TopAbs_OUT );
10332 TopAbs_State State() const
10339 //================================================================================
10341 \brief Identify the elements that will be affected by node duplication (actual duplication is not performed).
10342 This method is the first step of DoubleNodeElemGroupsInRegion.
10343 \param theElems - list of groups of elements (edges or faces) to be replicated
10344 \param theNodesNot - list of groups of nodes not to replicated
10345 \param theShape - shape to detect affected elements (element which geometric center
10346 located on or inside shape). If the shape is null, detection is done on faces orientations
10347 (select elements with a gravity center on the side given by faces normals).
10348 This mode (null shape) is faster, but works only when theElems are faces, with coherents orientations.
10349 The replicated nodes should be associated to affected elements.
10350 \return groups of affected elements
10351 \sa DoubleNodeElemGroupsInRegion()
10353 //================================================================================
10355 bool SMESH_MeshEditor::AffectedElemGroupsInRegion( const TIDSortedElemSet& theElems,
10356 const TIDSortedElemSet& theNodesNot,
10357 const TopoDS_Shape& theShape,
10358 TIDSortedElemSet& theAffectedElems)
10360 if ( theShape.IsNull() )
10362 std::set<const SMDS_MeshNode*> alreadyCheckedNodes;
10363 std::set<const SMDS_MeshElement*> alreadyCheckedElems;
10364 std::set<const SMDS_MeshElement*> edgesToCheck;
10365 alreadyCheckedNodes.clear();
10366 alreadyCheckedElems.clear();
10367 edgesToCheck.clear();
10369 // --- iterates on elements to be replicated and get elements by back references from their nodes
10371 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10373 for ( ielem=1; elemItr != theElems.end(); ++elemItr )
10375 SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
10376 if (!anElem || (anElem->GetType() != SMDSAbs_Face))
10379 SMESH_MeshAlgos::FaceNormal( anElem, normal, /*normalized=*/true );
10380 MESSAGE("element " << ielem++ << " normal " << normal.X() << " " << normal.Y() << " " << normal.Z());
10381 std::set<const SMDS_MeshNode*> nodesElem;
10383 SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
10384 while ( nodeItr->more() )
10386 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10387 nodesElem.insert(aNode);
10389 std::set<const SMDS_MeshNode*>::iterator nodit = nodesElem.begin();
10390 for (; nodit != nodesElem.end(); nodit++)
10392 MESSAGE(" noeud ");
10393 const SMDS_MeshNode* aNode = *nodit;
10394 if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
10396 if (alreadyCheckedNodes.find(aNode) != alreadyCheckedNodes.end())
10398 alreadyCheckedNodes.insert(aNode);
10399 SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
10400 while ( backElemItr->more() )
10402 MESSAGE(" backelem ");
10403 const SMDS_MeshElement* curElem = backElemItr->next();
10404 if (alreadyCheckedElems.find(curElem) != alreadyCheckedElems.end())
10406 if (theElems.find(curElem) != theElems.end())
10408 alreadyCheckedElems.insert(curElem);
10409 double x=0, y=0, z=0;
10411 SMDS_ElemIteratorPtr nodeItr2 = curElem->nodesIterator();
10412 while ( nodeItr2->more() )
10414 const SMDS_MeshNode* anotherNode = cast2Node(nodeItr2->next());
10415 x += anotherNode->X();
10416 y += anotherNode->Y();
10417 z += anotherNode->Z();
10421 p.SetCoord( x/nb -aNode->X(),
10423 z/nb -aNode->Z() );
10424 MESSAGE(" check " << p.X() << " " << p.Y() << " " << p.Z());
10427 MESSAGE(" --- inserted")
10428 theAffectedElems.insert( curElem );
10430 else if (curElem->GetType() == SMDSAbs_Edge)
10431 edgesToCheck.insert(curElem);
10435 // --- add also edges lying on the set of faces (all nodes in alreadyCheckedNodes)
10436 std::set<const SMDS_MeshElement*>::iterator eit = edgesToCheck.begin();
10437 for( ; eit != edgesToCheck.end(); eit++)
10439 bool onside = true;
10440 const SMDS_MeshElement* anEdge = *eit;
10441 SMDS_ElemIteratorPtr nodeItr = anEdge->nodesIterator();
10442 while ( nodeItr->more() )
10444 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10445 if (alreadyCheckedNodes.find(aNode) == alreadyCheckedNodes.end())
10453 MESSAGE(" --- edge onside inserted")
10454 theAffectedElems.insert(anEdge);
10460 const double aTol = Precision::Confusion();
10461 auto_ptr< BRepClass3d_SolidClassifier> bsc3d;
10462 auto_ptr<_FaceClassifier> aFaceClassifier;
10463 if ( theShape.ShapeType() == TopAbs_SOLID )
10465 bsc3d.reset( new BRepClass3d_SolidClassifier(theShape));;
10466 bsc3d->PerformInfinitePoint(aTol);
10468 else if (theShape.ShapeType() == TopAbs_FACE )
10470 aFaceClassifier.reset( new _FaceClassifier(TopoDS::Face(theShape)));
10473 // iterates on indicated elements and get elements by back references from their nodes
10474 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10476 for ( ielem = 1; elemItr != theElems.end(); ++elemItr )
10478 MESSAGE("element " << ielem++);
10479 SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
10482 SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
10483 while ( nodeItr->more() )
10485 MESSAGE(" noeud ");
10486 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10487 if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
10489 SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
10490 while ( backElemItr->more() )
10492 MESSAGE(" backelem ");
10493 const SMDS_MeshElement* curElem = backElemItr->next();
10494 if ( curElem && theElems.find(curElem) == theElems.end() &&
10496 isInside( curElem, *bsc3d, aTol ) :
10497 isInside( curElem, *aFaceClassifier, aTol )))
10498 theAffectedElems.insert( curElem );
10506 //================================================================================
10508 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10509 \param theElems - group of of elements (edges or faces) to be replicated
10510 \param theNodesNot - group of nodes not to replicate
10511 \param theShape - shape to detect affected elements (element which geometric center
10512 located on or inside shape).
10513 The replicated nodes should be associated to affected elements.
10514 \return TRUE if operation has been completed successfully, FALSE otherwise
10516 //================================================================================
10518 bool SMESH_MeshEditor::DoubleNodesInRegion( const TIDSortedElemSet& theElems,
10519 const TIDSortedElemSet& theNodesNot,
10520 const TopoDS_Shape& theShape )
10522 if ( theShape.IsNull() )
10525 const double aTol = Precision::Confusion();
10526 auto_ptr< BRepClass3d_SolidClassifier> bsc3d;
10527 auto_ptr<_FaceClassifier> aFaceClassifier;
10528 if ( theShape.ShapeType() == TopAbs_SOLID )
10530 bsc3d.reset( new BRepClass3d_SolidClassifier(theShape));;
10531 bsc3d->PerformInfinitePoint(aTol);
10533 else if (theShape.ShapeType() == TopAbs_FACE )
10535 aFaceClassifier.reset( new _FaceClassifier(TopoDS::Face(theShape)));
10538 // iterates on indicated elements and get elements by back references from their nodes
10539 TIDSortedElemSet anAffected;
10540 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10541 for ( ; elemItr != theElems.end(); ++elemItr )
10543 SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
10547 SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
10548 while ( nodeItr->more() )
10550 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10551 if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
10553 SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
10554 while ( backElemItr->more() )
10556 const SMDS_MeshElement* curElem = backElemItr->next();
10557 if ( curElem && theElems.find(curElem) == theElems.end() &&
10559 isInside( curElem, *bsc3d, aTol ) :
10560 isInside( curElem, *aFaceClassifier, aTol )))
10561 anAffected.insert( curElem );
10565 return DoubleNodes( theElems, theNodesNot, anAffected );
10569 * \brief compute an oriented angle between two planes defined by four points.
10570 * The vector (p0,p1) defines the intersection of the 2 planes (p0,p1,g1) and (p0,p1,g2)
10571 * @param p0 base of the rotation axe
10572 * @param p1 extremity of the rotation axe
10573 * @param g1 belongs to the first plane
10574 * @param g2 belongs to the second plane
10576 double SMESH_MeshEditor::OrientedAngle(const gp_Pnt& p0, const gp_Pnt& p1, const gp_Pnt& g1, const gp_Pnt& g2)
10578 // MESSAGE(" p0: " << p0.X() << " " << p0.Y() << " " << p0.Z());
10579 // MESSAGE(" p1: " << p1.X() << " " << p1.Y() << " " << p1.Z());
10580 // MESSAGE(" g1: " << g1.X() << " " << g1.Y() << " " << g1.Z());
10581 // MESSAGE(" g2: " << g2.X() << " " << g2.Y() << " " << g2.Z());
10582 gp_Vec vref(p0, p1);
10585 gp_Vec n1 = vref.Crossed(v1);
10586 gp_Vec n2 = vref.Crossed(v2);
10588 return n2.AngleWithRef(n1, vref);
10590 catch ( Standard_Failure ) {
10592 return Max( v1.Magnitude(), v2.Magnitude() );
10596 * \brief Double nodes on shared faces between groups of volumes and create flat elements on demand.
10597 * The list of groups must contain at least two groups. The groups have to be disjoint: no common element into two different groups.
10598 * The nodes of the internal faces at the boundaries of the groups are doubled. Optionally, the internal faces are replaced by flat elements.
10599 * Triangles are transformed into prisms, and quadrangles into hexahedrons.
10600 * The flat elements are stored in groups of volumes. These groups are named according to the position of the group in the list:
10601 * 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.
10602 * 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.
10603 * All the flat elements are gathered into the group named "joints3D" (or "joints2D" in 2D situation).
10604 * The flat element of the multiple junctions between the simple junction are stored in a group named "jointsMultiples".
10605 * \param theElems - list of groups of volumes, where a group of volume is a set of
10606 * SMDS_MeshElements sorted by Id.
10607 * \param createJointElems - if TRUE, create the elements
10608 * \param onAllBoundaries - if TRUE, the nodes and elements are also created on
10609 * the boundary between \a theDomains and the rest mesh
10610 * \return TRUE if operation has been completed successfully, FALSE otherwise
10612 bool SMESH_MeshEditor::DoubleNodesOnGroupBoundaries( const std::vector<TIDSortedElemSet>& theElems,
10613 bool createJointElems,
10614 bool onAllBoundaries)
10616 MESSAGE("----------------------------------------------");
10617 MESSAGE("SMESH_MeshEditor::doubleNodesOnGroupBoundaries");
10618 MESSAGE("----------------------------------------------");
10620 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
10621 meshDS->BuildDownWardConnectivity(true);
10623 SMDS_UnstructuredGrid *grid = meshDS->getGrid();
10625 // --- build the list of faces shared by 2 domains (group of elements), with their domain and volume indexes
10626 // build the list of cells with only a node or an edge on the border, with their domain and volume indexes
10627 // build the list of nodes shared by 2 or more domains, with their domain indexes
10629 std::map<DownIdType, std::map<int,int>, DownIdCompare> faceDomains; // face --> (id domain --> id volume)
10630 std::map<int,int>celldom; // cell vtkId --> domain
10631 std::map<DownIdType, std::map<int,int>, DownIdCompare> cellDomains; // oldNode --> (id domain --> id cell)
10632 std::map<int, std::map<int,int> > nodeDomains; // oldId --> (domainId --> newId)
10633 faceDomains.clear();
10635 cellDomains.clear();
10636 nodeDomains.clear();
10637 std::map<int,int> emptyMap;
10638 std::set<int> emptySet;
10641 MESSAGE(".. Number of domains :"<<theElems.size());
10643 TIDSortedElemSet theRestDomElems;
10644 const int iRestDom = -1;
10645 const int idom0 = onAllBoundaries ? iRestDom : 0;
10646 const int nbDomains = theElems.size();
10648 // Check if the domains do not share an element
10649 for (int idom = 0; idom < nbDomains-1; idom++)
10651 // MESSAGE("... Check of domain #" << idom);
10652 const TIDSortedElemSet& domain = theElems[idom];
10653 TIDSortedElemSet::const_iterator elemItr = domain.begin();
10654 for (; elemItr != domain.end(); ++elemItr)
10656 const SMDS_MeshElement* anElem = *elemItr;
10657 int idombisdeb = idom + 1 ;
10658 for (int idombis = idombisdeb; idombis < theElems.size(); idombis++) // check if the element belongs to a domain further in the list
10660 const TIDSortedElemSet& domainbis = theElems[idombis];
10661 if ( domainbis.count(anElem) )
10663 MESSAGE(".... Domain #" << idom);
10664 MESSAGE(".... Domain #" << idombis);
10665 throw SALOME_Exception("The domains are not disjoint.");
10672 for (int idom = 0; idom < nbDomains; idom++)
10675 // --- build a map (face to duplicate --> volume to modify)
10676 // with all the faces shared by 2 domains (group of elements)
10677 // and corresponding volume of this domain, for each shared face.
10678 // a volume has a face shared by 2 domains if it has a neighbor which is not in his domain.
10680 MESSAGE("... Neighbors of domain #" << idom);
10681 const TIDSortedElemSet& domain = theElems[idom];
10682 TIDSortedElemSet::const_iterator elemItr = domain.begin();
10683 for (; elemItr != domain.end(); ++elemItr)
10685 const SMDS_MeshElement* anElem = *elemItr;
10688 int vtkId = anElem->getVtkId();
10689 //MESSAGE(" vtkId " << vtkId << " smdsId " << anElem->GetID());
10690 int neighborsVtkIds[NBMAXNEIGHBORS];
10691 int downIds[NBMAXNEIGHBORS];
10692 unsigned char downTypes[NBMAXNEIGHBORS];
10693 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
10694 for (int n = 0; n < nbNeighbors; n++)
10696 int smdsId = meshDS->fromVtkToSmds(neighborsVtkIds[n]);
10697 const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
10698 if (elem && ! domain.count(elem)) // neighbor is in another domain : face is shared
10701 for (int idombis = 0; idombis < theElems.size() && !ok; idombis++) // check if the neighbor belongs to another domain of the list
10703 // MESSAGE("Domain " << idombis);
10704 const TIDSortedElemSet& domainbis = theElems[idombis];
10705 if ( domainbis.count(elem)) ok = true ; // neighbor is in a correct domain : face is kept
10707 if ( ok || onAllBoundaries ) // the characteristics of the face is stored
10709 DownIdType face(downIds[n], downTypes[n]);
10710 if (!faceDomains[face].count(idom))
10712 faceDomains[face][idom] = vtkId; // volume associated to face in this domain
10713 celldom[vtkId] = idom;
10714 //MESSAGE(" cell with a border " << vtkId << " domain " << idom);
10718 theRestDomElems.insert( elem );
10719 faceDomains[face][iRestDom] = neighborsVtkIds[n];
10720 celldom[neighborsVtkIds[n]] = iRestDom;
10728 //MESSAGE("Number of shared faces " << faceDomains.size());
10729 std::map<DownIdType, std::map<int, int>, DownIdCompare>::iterator itface;
10731 // --- explore the shared faces domain by domain,
10732 // explore the nodes of the face and see if they belong to a cell in the domain,
10733 // which has only a node or an edge on the border (not a shared face)
10735 for (int idomain = idom0; idomain < nbDomains; idomain++)
10737 //MESSAGE("Domain " << idomain);
10738 const TIDSortedElemSet& domain = (idomain == iRestDom) ? theRestDomElems : theElems[idomain];
10739 itface = faceDomains.begin();
10740 for (; itface != faceDomains.end(); ++itface)
10742 const std::map<int, int>& domvol = itface->second;
10743 if (!domvol.count(idomain))
10745 DownIdType face = itface->first;
10746 //MESSAGE(" --- face " << face.cellId);
10747 std::set<int> oldNodes;
10749 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
10750 std::set<int>::iterator itn = oldNodes.begin();
10751 for (; itn != oldNodes.end(); ++itn)
10754 //MESSAGE(" node " << oldId);
10755 vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
10756 for (int i=0; i<l.ncells; i++)
10758 int vtkId = l.cells[i];
10759 const SMDS_MeshElement* anElem = GetMeshDS()->FindElement(GetMeshDS()->fromVtkToSmds(vtkId));
10760 if (!domain.count(anElem))
10762 int vtkType = grid->GetCellType(vtkId);
10763 int downId = grid->CellIdToDownId(vtkId);
10766 MESSAGE("doubleNodesOnGroupBoundaries: internal algorithm problem");
10767 continue; // not OK at this stage of the algorithm:
10768 //no cells created after BuildDownWardConnectivity
10770 DownIdType aCell(downId, vtkType);
10771 cellDomains[aCell][idomain] = vtkId;
10772 celldom[vtkId] = idomain;
10773 //MESSAGE(" cell " << vtkId << " domain " << idomain);
10779 // --- explore the shared faces domain by domain, to duplicate the nodes in a coherent way
10780 // for each shared face, get the nodes
10781 // for each node, for each domain of the face, create a clone of the node
10783 // --- edges at the intersection of 3 or 4 domains, with the order of domains to build
10784 // junction elements of type prism or hexa. the key is the pair of nodesId (lower first)
10785 // the value is the ordered domain ids. (more than 4 domains not taken into account)
10787 std::map<std::vector<int>, std::vector<int> > edgesMultiDomains; // nodes of edge --> ordered domains
10788 std::map<int, std::vector<int> > mutipleNodes; // nodes multi domains with domain order
10789 std::map<int, std::vector<int> > mutipleNodesToFace; // nodes multi domains with domain order to transform in Face (junction between 3 or more 2D domains)
10791 MESSAGE(".. Duplication of the nodes");
10792 for (int idomain = idom0; idomain < nbDomains; idomain++)
10794 itface = faceDomains.begin();
10795 for (; itface != faceDomains.end(); ++itface)
10797 const std::map<int, int>& domvol = itface->second;
10798 if (!domvol.count(idomain))
10800 DownIdType face = itface->first;
10801 //MESSAGE(" --- face " << face.cellId);
10802 std::set<int> oldNodes;
10804 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
10805 std::set<int>::iterator itn = oldNodes.begin();
10806 for (; itn != oldNodes.end(); ++itn)
10809 if (nodeDomains[oldId].empty())
10811 nodeDomains[oldId][idomain] = oldId; // keep the old node in the first domain
10812 //MESSAGE("-+-+-b oldNode " << oldId << " domain " << idomain);
10814 std::map<int, int>::const_iterator itdom = domvol.begin();
10815 for (; itdom != domvol.end(); ++itdom)
10817 int idom = itdom->first;
10818 //MESSAGE(" domain " << idom);
10819 if (!nodeDomains[oldId].count(idom)) // --- node to clone
10821 if (nodeDomains[oldId].size() >= 2) // a multiple node
10823 vector<int> orderedDoms;
10824 //MESSAGE("multiple node " << oldId);
10825 if (mutipleNodes.count(oldId))
10826 orderedDoms = mutipleNodes[oldId];
10829 map<int,int>::iterator it = nodeDomains[oldId].begin();
10830 for (; it != nodeDomains[oldId].end(); ++it)
10831 orderedDoms.push_back(it->first);
10833 orderedDoms.push_back(idom); // TODO order ==> push_front or back
10834 //stringstream txt;
10835 //for (int i=0; i<orderedDoms.size(); i++)
10836 // txt << orderedDoms[i] << " ";
10837 //MESSAGE("orderedDoms " << txt.str());
10838 mutipleNodes[oldId] = orderedDoms;
10840 double *coords = grid->GetPoint(oldId);
10841 SMDS_MeshNode *newNode = meshDS->AddNode(coords[0], coords[1], coords[2]);
10842 int newId = newNode->getVtkId();
10843 nodeDomains[oldId][idom] = newId; // cloned node for other domains
10844 //MESSAGE("-+-+-c oldNode " << oldId << " domain " << idomain << " newNode " << newId << " domain " << idom << " size=" <<nodeDomains[oldId].size());
10851 MESSAGE(".. Creation of elements");
10852 for (int idomain = idom0; idomain < nbDomains; idomain++)
10854 itface = faceDomains.begin();
10855 for (; itface != faceDomains.end(); ++itface)
10857 std::map<int, int> domvol = itface->second;
10858 if (!domvol.count(idomain))
10860 DownIdType face = itface->first;
10861 //MESSAGE(" --- face " << face.cellId);
10862 std::set<int> oldNodes;
10864 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
10865 int nbMultipleNodes = 0;
10866 std::set<int>::iterator itn = oldNodes.begin();
10867 for (; itn != oldNodes.end(); ++itn)
10870 if (mutipleNodes.count(oldId))
10873 if (nbMultipleNodes > 1) // check if an edge of the face is shared between 3 or more domains
10875 //MESSAGE("multiple Nodes detected on a shared face");
10876 int downId = itface->first.cellId;
10877 unsigned char cellType = itface->first.cellType;
10878 // --- shared edge or shared face ?
10879 if ((cellType == VTK_LINE) || (cellType == VTK_QUADRATIC_EDGE)) // shared edge (between two faces)
10882 int nbNodes = grid->getDownArray(cellType)->getNodes(downId, nodes);
10883 for (int i=0; i< nbNodes; i=i+nbNodes-1) // i=0 , i=nbNodes-1
10884 if (mutipleNodes.count(nodes[i]))
10885 if (!mutipleNodesToFace.count(nodes[i]))
10886 mutipleNodesToFace[nodes[i]] = mutipleNodes[nodes[i]];
10888 else // shared face (between two volumes)
10890 int nbEdges = grid->getDownArray(cellType)->getNumberOfDownCells(downId);
10891 const int* downEdgeIds = grid->getDownArray(cellType)->getDownCells(downId);
10892 const unsigned char* edgeType = grid->getDownArray(cellType)->getDownTypes(downId);
10893 for (int ie =0; ie < nbEdges; ie++)
10896 int nbNodes = grid->getDownArray(edgeType[ie])->getNodes(downEdgeIds[ie], nodes);
10897 if (mutipleNodes.count(nodes[0]) && mutipleNodes.count(nodes[nbNodes-1]))
10899 vector<int> vn0 = mutipleNodes[nodes[0]];
10900 vector<int> vn1 = mutipleNodes[nodes[nbNodes - 1]];
10902 for (int i0 = 0; i0 < vn0.size(); i0++)
10903 for (int i1 = 0; i1 < vn1.size(); i1++)
10904 if (vn0[i0] == vn1[i1])
10905 doms.push_back(vn0[i0]);
10906 if (doms.size() >2)
10908 //MESSAGE(" detect edgesMultiDomains " << nodes[0] << " " << nodes[nbNodes - 1]);
10909 double *coords = grid->GetPoint(nodes[0]);
10910 gp_Pnt p0(coords[0], coords[1], coords[2]);
10911 coords = grid->GetPoint(nodes[nbNodes - 1]);
10912 gp_Pnt p1(coords[0], coords[1], coords[2]);
10914 int vtkVolIds[1000]; // an edge can belong to a lot of volumes
10915 map<int, SMDS_VtkVolume*> domvol; // domain --> a volume with the edge
10916 map<int, double> angleDom; // oriented angles between planes defined by edge and volume centers
10917 int nbvol = grid->GetParentVolumes(vtkVolIds, downEdgeIds[ie], edgeType[ie]);
10918 for (int id=0; id < doms.size(); id++)
10920 int idom = doms[id];
10921 const TIDSortedElemSet& domain = (idom == iRestDom) ? theRestDomElems : theElems[idom];
10922 for (int ivol=0; ivol<nbvol; ivol++)
10924 int smdsId = meshDS->fromVtkToSmds(vtkVolIds[ivol]);
10925 SMDS_MeshElement* elem = (SMDS_MeshElement*)meshDS->FindElement(smdsId);
10926 if (domain.count(elem))
10928 SMDS_VtkVolume* svol = dynamic_cast<SMDS_VtkVolume*>(elem);
10929 domvol[idom] = svol;
10930 //MESSAGE(" domain " << idom << " volume " << elem->GetID());
10932 vtkIdType npts = 0;
10933 vtkIdType* pts = 0;
10934 grid->GetCellPoints(vtkVolIds[ivol], npts, pts);
10935 SMDS_VtkVolume::gravityCenter(grid, pts, npts, values);
10938 gref.SetXYZ(gp_XYZ(values[0], values[1], values[2]));
10939 angleDom[idom] = 0;
10943 gp_Pnt g(values[0], values[1], values[2]);
10944 angleDom[idom] = OrientedAngle(p0, p1, gref, g); // -pi<angle<+pi
10945 //MESSAGE(" angle=" << angleDom[idom]);
10951 map<double, int> sortedDom; // sort domains by angle
10952 for (map<int, double>::iterator ia = angleDom.begin(); ia != angleDom.end(); ++ia)
10953 sortedDom[ia->second] = ia->first;
10954 vector<int> vnodes;
10956 for (map<double, int>::iterator ib = sortedDom.begin(); ib != sortedDom.end(); ++ib)
10958 vdom.push_back(ib->second);
10959 //MESSAGE(" ordered domain " << ib->second << " angle " << ib->first);
10961 for (int ino = 0; ino < nbNodes; ino++)
10962 vnodes.push_back(nodes[ino]);
10963 edgesMultiDomains[vnodes] = vdom; // nodes vector --> ordered domains
10972 // --- iterate on shared faces (volumes to modify, face to extrude)
10973 // get node id's of the face (id SMDS = id VTK)
10974 // create flat element with old and new nodes if requested
10976 // --- new quad nodes on flat quad elements: oldId --> ((domain1 X domain2) --> newId)
10977 // (domain1 X domain2) = domain1 + MAXINT*domain2
10979 std::map<int, std::map<long,int> > nodeQuadDomains;
10980 std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
10982 MESSAGE(".. Creation of elements: simple junction");
10983 if (createJointElems)
10986 string joints2DName = "joints2D";
10987 mapOfJunctionGroups[joints2DName] = this->myMesh->AddGroup(SMDSAbs_Face, joints2DName.c_str(), idg);
10988 SMESHDS_Group *joints2DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints2DName]->GetGroupDS());
10989 string joints3DName = "joints3D";
10990 mapOfJunctionGroups[joints3DName] = this->myMesh->AddGroup(SMDSAbs_Volume, joints3DName.c_str(), idg);
10991 SMESHDS_Group *joints3DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints3DName]->GetGroupDS());
10993 itface = faceDomains.begin();
10994 for (; itface != faceDomains.end(); ++itface)
10996 DownIdType face = itface->first;
10997 std::set<int> oldNodes;
10998 std::set<int>::iterator itn;
11000 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11002 std::map<int, int> domvol = itface->second;
11003 std::map<int, int>::iterator itdom = domvol.begin();
11004 int dom1 = itdom->first;
11005 int vtkVolId = itdom->second;
11007 int dom2 = itdom->first;
11008 SMDS_MeshCell *vol = grid->extrudeVolumeFromFace(vtkVolId, dom1, dom2, oldNodes, nodeDomains,
11010 stringstream grpname;
11013 grpname << dom1 << "_" << dom2;
11015 grpname << dom2 << "_" << dom1;
11016 string namegrp = grpname.str();
11017 if (!mapOfJunctionGroups.count(namegrp))
11018 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(vol->GetType(), namegrp.c_str(), idg);
11019 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11021 sgrp->Add(vol->GetID());
11022 if (vol->GetType() == SMDSAbs_Volume)
11023 joints3DGrp->Add(vol->GetID());
11024 else if (vol->GetType() == SMDSAbs_Face)
11025 joints2DGrp->Add(vol->GetID());
11029 // --- create volumes on multiple domain intersection if requested
11030 // iterate on mutipleNodesToFace
11031 // iterate on edgesMultiDomains
11033 MESSAGE(".. Creation of elements: multiple junction");
11034 if (createJointElems)
11036 // --- iterate on mutipleNodesToFace
11038 std::map<int, std::vector<int> >::iterator itn = mutipleNodesToFace.begin();
11039 for (; itn != mutipleNodesToFace.end(); ++itn)
11041 int node = itn->first;
11042 vector<int> orderDom = itn->second;
11043 vector<vtkIdType> orderedNodes;
11044 for (int idom = 0; idom <orderDom.size(); idom++)
11045 orderedNodes.push_back( nodeDomains[node][orderDom[idom]] );
11046 SMDS_MeshFace* face = this->GetMeshDS()->AddFaceFromVtkIds(orderedNodes);
11048 stringstream grpname;
11050 grpname << 0 << "_" << 0;
11052 string namegrp = grpname.str();
11053 if (!mapOfJunctionGroups.count(namegrp))
11054 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Face, namegrp.c_str(), idg);
11055 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11057 sgrp->Add(face->GetID());
11060 // --- iterate on edgesMultiDomains
11062 std::map<std::vector<int>, std::vector<int> >::iterator ite = edgesMultiDomains.begin();
11063 for (; ite != edgesMultiDomains.end(); ++ite)
11065 vector<int> nodes = ite->first;
11066 vector<int> orderDom = ite->second;
11067 vector<vtkIdType> orderedNodes;
11068 if (nodes.size() == 2)
11070 //MESSAGE(" use edgesMultiDomains " << nodes[0] << " " << nodes[1]);
11071 for (int ino=0; ino < nodes.size(); ino++)
11072 if (orderDom.size() == 3)
11073 for (int idom = 0; idom <orderDom.size(); idom++)
11074 orderedNodes.push_back( nodeDomains[nodes[ino]][orderDom[idom]] );
11076 for (int idom = orderDom.size()-1; idom >=0; idom--)
11077 orderedNodes.push_back( nodeDomains[nodes[ino]][orderDom[idom]] );
11078 SMDS_MeshVolume* vol = this->GetMeshDS()->AddVolumeFromVtkIds(orderedNodes);
11081 string namegrp = "jointsMultiples";
11082 if (!mapOfJunctionGroups.count(namegrp))
11083 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
11084 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11086 sgrp->Add(vol->GetID());
11090 INFOS("Quadratic multiple joints not implemented");
11091 // TODO quadratic nodes
11096 // --- list the explicit faces and edges of the mesh that need to be modified,
11097 // i.e. faces and edges built with one or more duplicated nodes.
11098 // associate these faces or edges to their corresponding domain.
11099 // only the first domain found is kept when a face or edge is shared
11101 std::map<DownIdType, std::map<int,int>, DownIdCompare> faceOrEdgeDom; // cellToModify --> (id domain --> id cell)
11102 std::map<int,int> feDom; // vtk id of cell to modify --> id domain
11103 faceOrEdgeDom.clear();
11106 MESSAGE(".. Modification of elements");
11107 for (int idomain = idom0; idomain < nbDomains; idomain++)
11109 std::map<int, std::map<int, int> >::const_iterator itnod = nodeDomains.begin();
11110 for (; itnod != nodeDomains.end(); ++itnod)
11112 int oldId = itnod->first;
11113 //MESSAGE(" node " << oldId);
11114 vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
11115 for (int i = 0; i < l.ncells; i++)
11117 int vtkId = l.cells[i];
11118 int vtkType = grid->GetCellType(vtkId);
11119 int downId = grid->CellIdToDownId(vtkId);
11121 continue; // new cells: not to be modified
11122 DownIdType aCell(downId, vtkType);
11123 int volParents[1000];
11124 int nbvol = grid->GetParentVolumes(volParents, vtkId);
11125 for (int j = 0; j < nbvol; j++)
11126 if (celldom.count(volParents[j]) && (celldom[volParents[j]] == idomain))
11127 if (!feDom.count(vtkId))
11129 feDom[vtkId] = idomain;
11130 faceOrEdgeDom[aCell] = emptyMap;
11131 faceOrEdgeDom[aCell][idomain] = vtkId; // affect face or edge to the first domain only
11132 //MESSAGE("affect cell " << this->GetMeshDS()->fromVtkToSmds(vtkId) << " domain " << idomain
11133 // << " type " << vtkType << " downId " << downId);
11139 // --- iterate on shared faces (volumes to modify, face to extrude)
11140 // get node id's of the face
11141 // replace old nodes by new nodes in volumes, and update inverse connectivity
11143 std::map<DownIdType, std::map<int,int>, DownIdCompare>* maps[3] = {&faceDomains, &cellDomains, &faceOrEdgeDom};
11144 for (int m=0; m<3; m++)
11146 std::map<DownIdType, std::map<int,int>, DownIdCompare>* amap = maps[m];
11147 itface = (*amap).begin();
11148 for (; itface != (*amap).end(); ++itface)
11150 DownIdType face = itface->first;
11151 std::set<int> oldNodes;
11152 std::set<int>::iterator itn;
11154 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11155 //MESSAGE("examine cell, downId " << face.cellId << " type " << int(face.cellType));
11156 std::map<int, int> localClonedNodeIds;
11158 std::map<int, int> domvol = itface->second;
11159 std::map<int, int>::iterator itdom = domvol.begin();
11160 for (; itdom != domvol.end(); ++itdom)
11162 int idom = itdom->first;
11163 int vtkVolId = itdom->second;
11164 //MESSAGE("modify nodes of cell " << this->GetMeshDS()->fromVtkToSmds(vtkVolId) << " domain " << idom);
11165 localClonedNodeIds.clear();
11166 for (itn = oldNodes.begin(); itn != oldNodes.end(); ++itn)
11169 if (nodeDomains[oldId].count(idom))
11171 localClonedNodeIds[oldId] = nodeDomains[oldId][idom];
11172 //MESSAGE(" node " << oldId << " --> " << localClonedNodeIds[oldId]);
11175 meshDS->ModifyCellNodes(vtkVolId, localClonedNodeIds);
11180 meshDS->CleanDownWardConnectivity(); // Mesh has been modified, downward connectivity is no more usable, free memory
11181 grid->BuildLinks();
11189 * \brief Double nodes on some external faces and create flat elements.
11190 * Flat elements are mainly used by some types of mechanic calculations.
11192 * Each group of the list must be constituted of faces.
11193 * Triangles are transformed in prisms, and quadrangles in hexahedrons.
11194 * @param theElems - list of groups of faces, where a group of faces is a set of
11195 * SMDS_MeshElements sorted by Id.
11196 * @return TRUE if operation has been completed successfully, FALSE otherwise
11198 bool SMESH_MeshEditor::CreateFlatElementsOnFacesGroups(const std::vector<TIDSortedElemSet>& theElems)
11200 MESSAGE("-------------------------------------------------");
11201 MESSAGE("SMESH_MeshEditor::CreateFlatElementsOnFacesGroups");
11202 MESSAGE("-------------------------------------------------");
11204 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11206 // --- For each group of faces
11207 // duplicate the nodes, create a flat element based on the face
11208 // replace the nodes of the faces by their clones
11210 std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> clonedNodes;
11211 std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> intermediateNodes;
11212 clonedNodes.clear();
11213 intermediateNodes.clear();
11214 std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11215 mapOfJunctionGroups.clear();
11217 for (int idom = 0; idom < theElems.size(); idom++)
11219 const TIDSortedElemSet& domain = theElems[idom];
11220 TIDSortedElemSet::const_iterator elemItr = domain.begin();
11221 for (; elemItr != domain.end(); ++elemItr)
11223 SMDS_MeshElement* anElem = (SMDS_MeshElement*) *elemItr;
11224 SMDS_MeshFace* aFace = dynamic_cast<SMDS_MeshFace*> (anElem);
11227 // MESSAGE("aFace=" << aFace->GetID());
11228 bool isQuad = aFace->IsQuadratic();
11229 vector<const SMDS_MeshNode*> ln0, ln1, ln2, ln3, ln4;
11231 // --- clone the nodes, create intermediate nodes for non medium nodes of a quad face
11233 SMDS_ElemIteratorPtr nodeIt = aFace->nodesIterator();
11234 while (nodeIt->more())
11236 const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*> (nodeIt->next());
11237 bool isMedium = isQuad && (aFace->IsMediumNode(node));
11239 ln2.push_back(node);
11241 ln0.push_back(node);
11243 const SMDS_MeshNode* clone = 0;
11244 if (!clonedNodes.count(node))
11246 clone = meshDS->AddNode(node->X(), node->Y(), node->Z());
11247 clonedNodes[node] = clone;
11250 clone = clonedNodes[node];
11253 ln3.push_back(clone);
11255 ln1.push_back(clone);
11257 const SMDS_MeshNode* inter = 0;
11258 if (isQuad && (!isMedium))
11260 if (!intermediateNodes.count(node))
11262 inter = meshDS->AddNode(node->X(), node->Y(), node->Z());
11263 intermediateNodes[node] = inter;
11266 inter = intermediateNodes[node];
11267 ln4.push_back(inter);
11271 // --- extrude the face
11273 vector<const SMDS_MeshNode*> ln;
11274 SMDS_MeshVolume* vol = 0;
11275 vtkIdType aType = aFace->GetVtkType();
11279 vol = meshDS->AddVolume(ln0[2], ln0[1], ln0[0], ln1[2], ln1[1], ln1[0]);
11280 // MESSAGE("vol prism " << vol->GetID());
11281 ln.push_back(ln1[0]);
11282 ln.push_back(ln1[1]);
11283 ln.push_back(ln1[2]);
11286 vol = meshDS->AddVolume(ln0[3], ln0[2], ln0[1], ln0[0], ln1[3], ln1[2], ln1[1], ln1[0]);
11287 // MESSAGE("vol hexa " << vol->GetID());
11288 ln.push_back(ln1[0]);
11289 ln.push_back(ln1[1]);
11290 ln.push_back(ln1[2]);
11291 ln.push_back(ln1[3]);
11293 case VTK_QUADRATIC_TRIANGLE:
11294 vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln0[0], ln0[1], ln0[2], ln3[0], ln3[1], ln3[2],
11295 ln2[0], ln2[1], ln2[2], ln4[0], ln4[1], ln4[2]);
11296 // MESSAGE("vol quad prism " << vol->GetID());
11297 ln.push_back(ln1[0]);
11298 ln.push_back(ln1[1]);
11299 ln.push_back(ln1[2]);
11300 ln.push_back(ln3[0]);
11301 ln.push_back(ln3[1]);
11302 ln.push_back(ln3[2]);
11304 case VTK_QUADRATIC_QUAD:
11305 // vol = meshDS->AddVolume(ln0[0], ln0[1], ln0[2], ln0[3], ln1[0], ln1[1], ln1[2], ln1[3],
11306 // ln2[0], ln2[1], ln2[2], ln2[3], ln3[0], ln3[1], ln3[2], ln3[3],
11307 // ln4[0], ln4[1], ln4[2], ln4[3]);
11308 vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln1[3], ln0[0], ln0[1], ln0[2], ln0[3],
11309 ln3[0], ln3[1], ln3[2], ln3[3], ln2[0], ln2[1], ln2[2], ln2[3],
11310 ln4[0], ln4[1], ln4[2], ln4[3]);
11311 // MESSAGE("vol quad hexa " << vol->GetID());
11312 ln.push_back(ln1[0]);
11313 ln.push_back(ln1[1]);
11314 ln.push_back(ln1[2]);
11315 ln.push_back(ln1[3]);
11316 ln.push_back(ln3[0]);
11317 ln.push_back(ln3[1]);
11318 ln.push_back(ln3[2]);
11319 ln.push_back(ln3[3]);
11329 stringstream grpname;
11333 string namegrp = grpname.str();
11334 if (!mapOfJunctionGroups.count(namegrp))
11335 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
11336 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11338 sgrp->Add(vol->GetID());
11341 // --- modify the face
11343 aFace->ChangeNodes(&ln[0], ln.size());
11350 * \brief identify all the elements around a geom shape, get the faces delimiting the hole
11351 * Build groups of volume to remove, groups of faces to replace on the skin of the object,
11352 * groups of faces to remove inside the object, (idem edges).
11353 * Build ordered list of nodes at the border of each group of faces to replace (to be used to build a geom subshape)
11355 void SMESH_MeshEditor::CreateHoleSkin(double radius,
11356 const TopoDS_Shape& theShape,
11357 SMESH_NodeSearcher* theNodeSearcher,
11358 const char* groupName,
11359 std::vector<double>& nodesCoords,
11360 std::vector<std::vector<int> >& listOfListOfNodes)
11362 MESSAGE("--------------------------------");
11363 MESSAGE("SMESH_MeshEditor::CreateHoleSkin");
11364 MESSAGE("--------------------------------");
11366 // --- zone of volumes to remove is given :
11367 // 1 either by a geom shape (one or more vertices) and a radius,
11368 // 2 either by a group of nodes (representative of the shape)to use with the radius,
11369 // 3 either by a group of nodes where all the elements build on one of this nodes are to remove,
11370 // In the case 2, the group of nodes is an external group of nodes from another mesh,
11371 // In the case 3, the group of nodes is an internal group of the mesh (obtained for instance by a filter),
11372 // defined by it's name.
11374 SMESHDS_GroupBase* groupDS = 0;
11375 SMESH_Mesh::GroupIteratorPtr groupIt = this->myMesh->GetGroups();
11376 while ( groupIt->more() )
11379 SMESH_Group * group = groupIt->next();
11380 if ( !group ) continue;
11381 groupDS = group->GetGroupDS();
11382 if ( !groupDS || groupDS->IsEmpty() ) continue;
11383 std::string grpName = group->GetName();
11384 //MESSAGE("grpName=" << grpName);
11385 if (grpName == groupName)
11391 bool isNodeGroup = false;
11392 bool isNodeCoords = false;
11395 if (groupDS->GetType() != SMDSAbs_Node)
11397 isNodeGroup = true; // a group of nodes exists and it is in this mesh
11400 if (nodesCoords.size() > 0)
11401 isNodeCoords = true; // a list o nodes given by their coordinates
11402 //MESSAGE("---" << isNodeGroup << " " << isNodeCoords);
11404 // --- define groups to build
11406 int idg; // --- group of SMDS volumes
11407 string grpvName = groupName;
11408 grpvName += "_vol";
11409 SMESH_Group *grp = this->myMesh->AddGroup(SMDSAbs_Volume, grpvName.c_str(), idg);
11412 MESSAGE("group not created " << grpvName);
11415 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(grp->GetGroupDS());
11417 int idgs; // --- group of SMDS faces on the skin
11418 string grpsName = groupName;
11419 grpsName += "_skin";
11420 SMESH_Group *grps = this->myMesh->AddGroup(SMDSAbs_Face, grpsName.c_str(), idgs);
11423 MESSAGE("group not created " << grpsName);
11426 SMESHDS_Group *sgrps = dynamic_cast<SMESHDS_Group*>(grps->GetGroupDS());
11428 int idgi; // --- group of SMDS faces internal (several shapes)
11429 string grpiName = groupName;
11430 grpiName += "_internalFaces";
11431 SMESH_Group *grpi = this->myMesh->AddGroup(SMDSAbs_Face, grpiName.c_str(), idgi);
11434 MESSAGE("group not created " << grpiName);
11437 SMESHDS_Group *sgrpi = dynamic_cast<SMESHDS_Group*>(grpi->GetGroupDS());
11439 int idgei; // --- group of SMDS faces internal (several shapes)
11440 string grpeiName = groupName;
11441 grpeiName += "_internalEdges";
11442 SMESH_Group *grpei = this->myMesh->AddGroup(SMDSAbs_Edge, grpeiName.c_str(), idgei);
11445 MESSAGE("group not created " << grpeiName);
11448 SMESHDS_Group *sgrpei = dynamic_cast<SMESHDS_Group*>(grpei->GetGroupDS());
11450 // --- build downward connectivity
11452 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11453 meshDS->BuildDownWardConnectivity(true);
11454 SMDS_UnstructuredGrid* grid = meshDS->getGrid();
11456 // --- set of volumes detected inside
11458 std::set<int> setOfInsideVol;
11459 std::set<int> setOfVolToCheck;
11461 std::vector<gp_Pnt> gpnts;
11464 if (isNodeGroup) // --- a group of nodes is provided : find all the volumes using one or more of this nodes
11466 MESSAGE("group of nodes provided");
11467 SMDS_ElemIteratorPtr elemIt = groupDS->GetElements();
11468 while ( elemIt->more() )
11470 const SMDS_MeshElement* elem = elemIt->next();
11473 const SMDS_MeshNode* node = dynamic_cast<const SMDS_MeshNode*>(elem);
11476 SMDS_MeshElement* vol = 0;
11477 SMDS_ElemIteratorPtr volItr = node->GetInverseElementIterator(SMDSAbs_Volume);
11478 while (volItr->more())
11480 vol = (SMDS_MeshElement*)volItr->next();
11481 setOfInsideVol.insert(vol->getVtkId());
11482 sgrp->Add(vol->GetID());
11486 else if (isNodeCoords)
11488 MESSAGE("list of nodes coordinates provided");
11491 while (i < nodesCoords.size()-2)
11493 double x = nodesCoords[i++];
11494 double y = nodesCoords[i++];
11495 double z = nodesCoords[i++];
11496 gp_Pnt p = gp_Pnt(x, y ,z);
11497 gpnts.push_back(p);
11498 MESSAGE("TopoDS_Vertex " << k << " " << p.X() << " " << p.Y() << " " << p.Z());
11502 else // --- no group, no coordinates : use the vertices of the geom shape provided, and radius
11504 MESSAGE("no group of nodes provided, using vertices from geom shape, and radius");
11505 TopTools_IndexedMapOfShape vertexMap;
11506 TopExp::MapShapes( theShape, TopAbs_VERTEX, vertexMap );
11507 gp_Pnt p = gp_Pnt(0,0,0);
11508 if (vertexMap.Extent() < 1)
11511 for ( int i = 1; i <= vertexMap.Extent(); ++i )
11513 const TopoDS_Vertex& vertex = TopoDS::Vertex( vertexMap( i ));
11514 p = BRep_Tool::Pnt(vertex);
11515 gpnts.push_back(p);
11516 MESSAGE("TopoDS_Vertex " << i << " " << p.X() << " " << p.Y() << " " << p.Z());
11520 if (gpnts.size() > 0)
11523 const SMDS_MeshNode* startNode = theNodeSearcher->FindClosestTo(gpnts[0]);
11525 nodeId = startNode->GetID();
11526 MESSAGE("nodeId " << nodeId);
11528 double radius2 = radius*radius;
11529 MESSAGE("radius2 " << radius2);
11531 // --- volumes on start node
11533 setOfVolToCheck.clear();
11534 SMDS_MeshElement* startVol = 0;
11535 SMDS_ElemIteratorPtr volItr = startNode->GetInverseElementIterator(SMDSAbs_Volume);
11536 while (volItr->more())
11538 startVol = (SMDS_MeshElement*)volItr->next();
11539 setOfVolToCheck.insert(startVol->getVtkId());
11541 if (setOfVolToCheck.empty())
11543 MESSAGE("No volumes found");
11547 // --- starting with central volumes then their neighbors, check if they are inside
11548 // or outside the domain, until no more new neighbor volume is inside.
11549 // Fill the group of inside volumes
11551 std::map<int, double> mapOfNodeDistance2;
11552 mapOfNodeDistance2.clear();
11553 std::set<int> setOfOutsideVol;
11554 while (!setOfVolToCheck.empty())
11556 std::set<int>::iterator it = setOfVolToCheck.begin();
11558 MESSAGE("volume to check, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
11559 bool volInside = false;
11560 vtkIdType npts = 0;
11561 vtkIdType* pts = 0;
11562 grid->GetCellPoints(vtkId, npts, pts);
11563 for (int i=0; i<npts; i++)
11565 double distance2 = 0;
11566 if (mapOfNodeDistance2.count(pts[i]))
11568 distance2 = mapOfNodeDistance2[pts[i]];
11569 MESSAGE("point " << pts[i] << " distance2 " << distance2);
11573 double *coords = grid->GetPoint(pts[i]);
11574 gp_Pnt aPoint = gp_Pnt(coords[0], coords[1], coords[2]);
11576 for (int j=0; j<gpnts.size(); j++)
11578 double d2 = aPoint.SquareDistance(gpnts[j]);
11579 if (d2 < distance2)
11582 if (distance2 < radius2)
11586 mapOfNodeDistance2[pts[i]] = distance2;
11587 MESSAGE(" point " << pts[i] << " distance2 " << distance2 << " coords " << coords[0] << " " << coords[1] << " " << coords[2]);
11589 if (distance2 < radius2)
11591 volInside = true; // one or more nodes inside the domain
11592 sgrp->Add(meshDS->fromVtkToSmds(vtkId));
11598 setOfInsideVol.insert(vtkId);
11599 MESSAGE(" volume inside, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
11600 int neighborsVtkIds[NBMAXNEIGHBORS];
11601 int downIds[NBMAXNEIGHBORS];
11602 unsigned char downTypes[NBMAXNEIGHBORS];
11603 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
11604 for (int n = 0; n < nbNeighbors; n++)
11605 if (!setOfInsideVol.count(neighborsVtkIds[n]) ||setOfOutsideVol.count(neighborsVtkIds[n]))
11606 setOfVolToCheck.insert(neighborsVtkIds[n]);
11610 setOfOutsideVol.insert(vtkId);
11611 MESSAGE(" volume outside, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
11613 setOfVolToCheck.erase(vtkId);
11617 // --- for outside hexahedrons, check if they have more than one neighbor volume inside
11618 // If yes, add the volume to the inside set
11620 bool addedInside = true;
11621 std::set<int> setOfVolToReCheck;
11622 while (addedInside)
11624 MESSAGE(" --------------------------- re check");
11625 addedInside = false;
11626 std::set<int>::iterator itv = setOfInsideVol.begin();
11627 for (; itv != setOfInsideVol.end(); ++itv)
11630 int neighborsVtkIds[NBMAXNEIGHBORS];
11631 int downIds[NBMAXNEIGHBORS];
11632 unsigned char downTypes[NBMAXNEIGHBORS];
11633 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
11634 for (int n = 0; n < nbNeighbors; n++)
11635 if (!setOfInsideVol.count(neighborsVtkIds[n]))
11636 setOfVolToReCheck.insert(neighborsVtkIds[n]);
11638 setOfVolToCheck = setOfVolToReCheck;
11639 setOfVolToReCheck.clear();
11640 while (!setOfVolToCheck.empty())
11642 std::set<int>::iterator it = setOfVolToCheck.begin();
11644 if (grid->GetCellType(vtkId) == VTK_HEXAHEDRON)
11646 MESSAGE("volume to recheck, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
11647 int countInside = 0;
11648 int neighborsVtkIds[NBMAXNEIGHBORS];
11649 int downIds[NBMAXNEIGHBORS];
11650 unsigned char downTypes[NBMAXNEIGHBORS];
11651 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
11652 for (int n = 0; n < nbNeighbors; n++)
11653 if (setOfInsideVol.count(neighborsVtkIds[n]))
11655 MESSAGE("countInside " << countInside);
11656 if (countInside > 1)
11658 MESSAGE(" volume inside, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
11659 setOfInsideVol.insert(vtkId);
11660 sgrp->Add(meshDS->fromVtkToSmds(vtkId));
11661 addedInside = true;
11664 setOfVolToReCheck.insert(vtkId);
11666 setOfVolToCheck.erase(vtkId);
11670 // --- map of Downward faces at the boundary, inside the global volume
11671 // map of Downward faces on the skin of the global volume (equivalent to SMDS faces on the skin)
11672 // fill group of SMDS faces inside the volume (when several volume shapes)
11673 // fill group of SMDS faces on the skin of the global volume (if skin)
11675 std::map<DownIdType, int, DownIdCompare> boundaryFaces; // boundary faces inside the volume --> corresponding cell
11676 std::map<DownIdType, int, DownIdCompare> skinFaces; // faces on the skin of the global volume --> corresponding cell
11677 std::set<int>::iterator it = setOfInsideVol.begin();
11678 for (; it != setOfInsideVol.end(); ++it)
11681 //MESSAGE(" vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
11682 int neighborsVtkIds[NBMAXNEIGHBORS];
11683 int downIds[NBMAXNEIGHBORS];
11684 unsigned char downTypes[NBMAXNEIGHBORS];
11685 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId, true);
11686 for (int n = 0; n < nbNeighbors; n++)
11688 int neighborDim = SMDS_Downward::getCellDimension(grid->GetCellType(neighborsVtkIds[n]));
11689 if (neighborDim == 3)
11691 if (! setOfInsideVol.count(neighborsVtkIds[n])) // neighbor volume is not inside : face is boundary
11693 DownIdType face(downIds[n], downTypes[n]);
11694 boundaryFaces[face] = vtkId;
11696 // if the face between to volumes is in the mesh, get it (internal face between shapes)
11697 int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
11698 if (vtkFaceId >= 0)
11700 sgrpi->Add(meshDS->fromVtkToSmds(vtkFaceId));
11701 // find also the smds edges on this face
11702 int nbEdges = grid->getDownArray(downTypes[n])->getNumberOfDownCells(downIds[n]);
11703 const int* dEdges = grid->getDownArray(downTypes[n])->getDownCells(downIds[n]);
11704 const unsigned char* dTypes = grid->getDownArray(downTypes[n])->getDownTypes(downIds[n]);
11705 for (int i = 0; i < nbEdges; i++)
11707 int vtkEdgeId = grid->getDownArray(dTypes[i])->getVtkCellId(dEdges[i]);
11708 if (vtkEdgeId >= 0)
11709 sgrpei->Add(meshDS->fromVtkToSmds(vtkEdgeId));
11713 else if (neighborDim == 2) // skin of the volume
11715 DownIdType face(downIds[n], downTypes[n]);
11716 skinFaces[face] = vtkId;
11717 int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
11718 if (vtkFaceId >= 0)
11719 sgrps->Add(meshDS->fromVtkToSmds(vtkFaceId));
11724 // --- identify the edges constituting the wire of each subshape on the skin
11725 // define polylines with the nodes of edges, equivalent to wires
11726 // project polylines on subshapes, and partition, to get geom faces
11728 std::map<int, std::set<int> > shapeIdToVtkIdSet; // shapeId --> set of vtkId on skin
11729 std::set<int> emptySet;
11731 std::set<int> shapeIds;
11733 SMDS_ElemIteratorPtr itelem = sgrps->GetElements();
11734 while (itelem->more())
11736 const SMDS_MeshElement *elem = itelem->next();
11737 int shapeId = elem->getshapeId();
11738 int vtkId = elem->getVtkId();
11739 if (!shapeIdToVtkIdSet.count(shapeId))
11741 shapeIdToVtkIdSet[shapeId] = emptySet;
11742 shapeIds.insert(shapeId);
11744 shapeIdToVtkIdSet[shapeId].insert(vtkId);
11747 std::map<int, std::set<DownIdType, DownIdCompare> > shapeIdToEdges; // shapeId --> set of downward edges
11748 std::set<DownIdType, DownIdCompare> emptyEdges;
11749 emptyEdges.clear();
11751 std::map<int, std::set<int> >::iterator itShape = shapeIdToVtkIdSet.begin();
11752 for (; itShape != shapeIdToVtkIdSet.end(); ++itShape)
11754 int shapeId = itShape->first;
11755 MESSAGE(" --- Shape ID --- "<< shapeId);
11756 shapeIdToEdges[shapeId] = emptyEdges;
11758 std::vector<int> nodesEdges;
11760 std::set<int>::iterator its = itShape->second.begin();
11761 for (; its != itShape->second.end(); ++its)
11764 MESSAGE(" " << vtkId);
11765 int neighborsVtkIds[NBMAXNEIGHBORS];
11766 int downIds[NBMAXNEIGHBORS];
11767 unsigned char downTypes[NBMAXNEIGHBORS];
11768 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
11769 for (int n = 0; n < nbNeighbors; n++)
11771 if (neighborsVtkIds[n]<0) // only smds faces are considered as neighbors here
11773 int smdsId = meshDS->fromVtkToSmds(neighborsVtkIds[n]);
11774 const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
11775 if ( shapeIds.count(elem->getshapeId()) && !sgrps->Contains(elem)) // edge : neighbor in the set of shape, not in the group
11777 DownIdType edge(downIds[n], downTypes[n]);
11778 if (!shapeIdToEdges[shapeId].count(edge))
11780 shapeIdToEdges[shapeId].insert(edge);
11782 int nbNodes = grid->getDownArray(downTypes[n])->getNodes(downIds[n],vtkNodeId);
11783 nodesEdges.push_back(vtkNodeId[0]);
11784 nodesEdges.push_back(vtkNodeId[nbNodes-1]);
11785 MESSAGE(" --- nodes " << vtkNodeId[0]+1 << " " << vtkNodeId[nbNodes-1]+1);
11791 std::list<int> order;
11793 if (nodesEdges.size() > 0)
11795 order.push_back(nodesEdges[0]); MESSAGE(" --- back " << order.back()+1); // SMDS id = VTK id + 1;
11796 nodesEdges[0] = -1;
11797 order.push_back(nodesEdges[1]); MESSAGE(" --- back " << order.back()+1);
11798 nodesEdges[1] = -1; // do not reuse this edge
11802 int nodeTofind = order.back(); // try first to push back
11804 for (i = 0; i<nodesEdges.size(); i++)
11805 if (nodesEdges[i] == nodeTofind)
11807 if (i == nodesEdges.size())
11808 found = false; // no follower found on back
11811 if (i%2) // odd ==> use the previous one
11812 if (nodesEdges[i-1] < 0)
11816 order.push_back(nodesEdges[i-1]); MESSAGE(" --- back " << order.back()+1);
11817 nodesEdges[i-1] = -1;
11819 else // even ==> use the next one
11820 if (nodesEdges[i+1] < 0)
11824 order.push_back(nodesEdges[i+1]); MESSAGE(" --- back " << order.back()+1);
11825 nodesEdges[i+1] = -1;
11830 // try to push front
11832 nodeTofind = order.front(); // try to push front
11833 for (i = 0; i<nodesEdges.size(); i++)
11834 if (nodesEdges[i] == nodeTofind)
11836 if (i == nodesEdges.size())
11838 found = false; // no predecessor found on front
11841 if (i%2) // odd ==> use the previous one
11842 if (nodesEdges[i-1] < 0)
11846 order.push_front(nodesEdges[i-1]); MESSAGE(" --- front " << order.front()+1);
11847 nodesEdges[i-1] = -1;
11849 else // even ==> use the next one
11850 if (nodesEdges[i+1] < 0)
11854 order.push_front(nodesEdges[i+1]); MESSAGE(" --- front " << order.front()+1);
11855 nodesEdges[i+1] = -1;
11861 std::vector<int> nodes;
11862 nodes.push_back(shapeId);
11863 std::list<int>::iterator itl = order.begin();
11864 for (; itl != order.end(); itl++)
11866 nodes.push_back((*itl) + 1); // SMDS id = VTK id + 1;
11867 MESSAGE(" ordered node " << nodes[nodes.size()-1]);
11869 listOfListOfNodes.push_back(nodes);
11872 // partition geom faces with blocFissure
11873 // mesh blocFissure and geom faces of the skin (external wires given, triangle algo to choose)
11874 // mesh volume around blocFissure (skin triangles and quadrangle given, tetra algo to choose)
11880 //================================================================================
11882 * \brief Generates skin mesh (containing 2D cells) from 3D mesh
11883 * The created 2D mesh elements based on nodes of free faces of boundary volumes
11884 * \return TRUE if operation has been completed successfully, FALSE otherwise
11886 //================================================================================
11888 bool SMESH_MeshEditor::Make2DMeshFrom3D()
11890 // iterates on volume elements and detect all free faces on them
11891 SMESHDS_Mesh* aMesh = GetMeshDS();
11894 //bool res = false;
11895 int nbFree = 0, nbExisted = 0, nbCreated = 0;
11896 SMDS_VolumeIteratorPtr vIt = aMesh->volumesIterator();
11899 const SMDS_MeshVolume* volume = vIt->next();
11900 SMDS_VolumeTool vTool( volume, /*ignoreCentralNodes=*/false );
11901 vTool.SetExternalNormal();
11902 //const bool isPoly = volume->IsPoly();
11903 const int iQuad = volume->IsQuadratic();
11904 for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
11906 if (!vTool.IsFreeFace(iface))
11909 vector<const SMDS_MeshNode *> nodes;
11910 int nbFaceNodes = vTool.NbFaceNodes(iface);
11911 const SMDS_MeshNode** faceNodes = vTool.GetFaceNodes(iface);
11913 for ( ; inode < nbFaceNodes; inode += iQuad+1)
11914 nodes.push_back(faceNodes[inode]);
11915 if (iQuad) { // add medium nodes
11916 for ( inode = 1; inode < nbFaceNodes; inode += 2)
11917 nodes.push_back(faceNodes[inode]);
11918 if ( nbFaceNodes == 9 ) // bi-quadratic quad
11919 nodes.push_back(faceNodes[8]);
11921 // add new face based on volume nodes
11922 if (aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false) ) {
11924 continue; // face already exsist
11926 AddElement(nodes, SMDSAbs_Face, ( !iQuad && nbFaceNodes/(iQuad+1) > 4 ));
11930 return ( nbFree==(nbExisted+nbCreated) );
11935 inline const SMDS_MeshNode* getNodeWithSameID(SMESHDS_Mesh* mesh, const SMDS_MeshNode* node)
11937 if ( const SMDS_MeshNode* n = mesh->FindNode( node->GetID() ))
11939 return mesh->AddNodeWithID( node->X(),node->Y(),node->Z(), node->GetID() );
11942 //================================================================================
11944 * \brief Creates missing boundary elements
11945 * \param elements - elements whose boundary is to be checked
11946 * \param dimension - defines type of boundary elements to create
11947 * \param group - a group to store created boundary elements in
11948 * \param targetMesh - a mesh to store created boundary elements in
11949 * \param toCopyElements - if true, the checked elements will be copied into the targetMesh
11950 * \param toCopyExistingBoundary - if true, not only new but also pre-existing
11951 * boundary elements will be copied into the targetMesh
11952 * \param toAddExistingBondary - if true, not only new but also pre-existing
11953 * boundary elements will be added into the new group
11954 * \param aroundElements - if true, elements will be created on boundary of given
11955 * elements else, on boundary of the whole mesh.
11956 * \return nb of added boundary elements
11958 //================================================================================
11960 int SMESH_MeshEditor::MakeBoundaryMesh(const TIDSortedElemSet& elements,
11961 Bnd_Dimension dimension,
11962 SMESH_Group* group/*=0*/,
11963 SMESH_Mesh* targetMesh/*=0*/,
11964 bool toCopyElements/*=false*/,
11965 bool toCopyExistingBoundary/*=false*/,
11966 bool toAddExistingBondary/*= false*/,
11967 bool aroundElements/*= false*/)
11969 SMDSAbs_ElementType missType = (dimension == BND_2DFROM3D) ? SMDSAbs_Face : SMDSAbs_Edge;
11970 SMDSAbs_ElementType elemType = (dimension == BND_1DFROM2D) ? SMDSAbs_Face : SMDSAbs_Volume;
11971 // hope that all elements are of the same type, do not check them all
11972 if ( !elements.empty() && (*elements.begin())->GetType() != elemType )
11973 throw SALOME_Exception(LOCALIZED("wrong element type"));
11976 toCopyElements = toCopyExistingBoundary = false;
11978 SMESH_MeshEditor tgtEditor( targetMesh ? targetMesh : myMesh );
11979 SMESHDS_Mesh* aMesh = GetMeshDS(), *tgtMeshDS = tgtEditor.GetMeshDS();
11980 int nbAddedBnd = 0;
11982 // editor adding present bnd elements and optionally holding elements to add to the group
11983 SMESH_MeshEditor* presentEditor;
11984 SMESH_MeshEditor tgtEditor2( tgtEditor.GetMesh() );
11985 presentEditor = toAddExistingBondary ? &tgtEditor : &tgtEditor2;
11987 SMESH_MesherHelper helper( *myMesh );
11988 const TopAbs_ShapeEnum missShapeType = ( missType==SMDSAbs_Face ? TopAbs_FACE : TopAbs_EDGE );
11989 SMDS_VolumeTool vTool;
11990 TIDSortedElemSet avoidSet;
11991 const TIDSortedElemSet emptySet, *elemSet = aroundElements ? &elements : &emptySet;
11994 typedef vector<const SMDS_MeshNode*> TConnectivity;
11996 SMDS_ElemIteratorPtr eIt;
11997 if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
11998 else eIt = elemSetIterator( elements );
12000 while (eIt->more())
12002 const SMDS_MeshElement* elem = eIt->next();
12003 const int iQuad = elem->IsQuadratic();
12005 // ------------------------------------------------------------------------------------
12006 // 1. For an elem, get present bnd elements and connectivities of missing bnd elements
12007 // ------------------------------------------------------------------------------------
12008 vector<const SMDS_MeshElement*> presentBndElems;
12009 vector<TConnectivity> missingBndElems;
12010 TConnectivity nodes, elemNodes;
12011 if ( vTool.Set(elem, /*ignoreCentralNodes=*/true) ) // elem is a volume --------------
12013 vTool.SetExternalNormal();
12014 const SMDS_MeshElement* otherVol = 0;
12015 for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12017 if ( !vTool.IsFreeFace(iface, &otherVol) &&
12018 ( !aroundElements || elements.count( otherVol )))
12020 const SMDS_MeshNode** nn = vTool.GetFaceNodes(iface);
12021 const int nbFaceNodes = vTool.NbFaceNodes (iface);
12022 if ( missType == SMDSAbs_Edge ) // boundary edges
12024 nodes.resize( 2+iQuad );
12025 for ( int i = 0; i < nbFaceNodes; i += 1+iQuad)
12027 for ( int j = 0; j < nodes.size(); ++j )
12029 if ( const SMDS_MeshElement* edge =
12030 aMesh->FindElement(nodes,SMDSAbs_Edge,/*noMedium=*/false))
12031 presentBndElems.push_back( edge );
12033 missingBndElems.push_back( nodes );
12036 else // boundary face
12039 for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12040 nodes.push_back( nn[inode] ); // add corner nodes
12042 for ( inode = 1; inode < nbFaceNodes; inode += 2)
12043 nodes.push_back( nn[inode] ); // add medium nodes
12044 int iCenter = vTool.GetCenterNodeIndex(iface); // for HEX27
12046 nodes.push_back( vTool.GetNodes()[ iCenter ] );
12048 if (const SMDS_MeshElement * f = aMesh->FindElement( nodes,
12049 SMDSAbs_Face, /*noMedium=*/false ))
12050 presentBndElems.push_back( f );
12052 missingBndElems.push_back( nodes );
12054 if ( targetMesh != myMesh )
12056 // add 1D elements on face boundary to be added to a new mesh
12057 const SMDS_MeshElement* edge;
12058 for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12061 edge = aMesh->FindEdge( nn[inode], nn[inode+1], nn[inode+2]);
12063 edge = aMesh->FindEdge( nn[inode], nn[inode+1]);
12064 if ( edge && avoidSet.insert( edge ).second )
12065 presentBndElems.push_back( edge );
12071 else if ( elem->GetType() == SMDSAbs_Face ) // elem is a face ------------------------
12073 avoidSet.clear(), avoidSet.insert( elem );
12074 elemNodes.assign( SMDS_MeshElement::iterator( elem->interlacedNodesElemIterator() ),
12075 SMDS_MeshElement::iterator() );
12076 elemNodes.push_back( elemNodes[0] );
12077 nodes.resize( 2 + iQuad );
12078 const int nbLinks = elem->NbCornerNodes();
12079 for ( int i = 0, iN = 0; i < nbLinks; i++, iN += 1+iQuad )
12081 nodes[0] = elemNodes[iN];
12082 nodes[1] = elemNodes[iN+1+iQuad];
12083 if ( SMESH_MeshAlgos::FindFaceInSet( nodes[0], nodes[1], *elemSet, avoidSet))
12084 continue; // not free link
12086 if ( iQuad ) nodes[2] = elemNodes[iN+1];
12087 if ( const SMDS_MeshElement* edge =
12088 aMesh->FindElement(nodes,SMDSAbs_Edge,/*noMedium=*/false))
12089 presentBndElems.push_back( edge );
12091 missingBndElems.push_back( nodes );
12095 // ---------------------------------
12096 // 2. Add missing boundary elements
12097 // ---------------------------------
12098 if ( targetMesh != myMesh )
12099 // instead of making a map of nodes in this mesh and targetMesh,
12100 // we create nodes with same IDs.
12101 for ( int i = 0; i < missingBndElems.size(); ++i )
12103 TConnectivity& srcNodes = missingBndElems[i];
12104 TConnectivity nodes( srcNodes.size() );
12105 for ( inode = 0; inode < nodes.size(); ++inode )
12106 nodes[inode] = getNodeWithSameID( tgtMeshDS, srcNodes[inode] );
12107 if ( aroundElements && tgtEditor.GetMeshDS()->FindElement( nodes,
12109 /*noMedium=*/false))
12111 tgtEditor.AddElement(nodes, missType, !iQuad && nodes.size()/(iQuad+1)>4);
12115 for ( int i = 0; i < missingBndElems.size(); ++i )
12117 TConnectivity& nodes = missingBndElems[i];
12118 if ( aroundElements && tgtEditor.GetMeshDS()->FindElement( nodes,
12120 /*noMedium=*/false))
12122 SMDS_MeshElement* elem =
12123 tgtEditor.AddElement(nodes, missType, !iQuad && nodes.size()/(iQuad+1)>4);
12126 // try to set a new element to a shape
12127 if ( myMesh->HasShapeToMesh() )
12130 set< pair<TopAbs_ShapeEnum, int > > mediumShapes;
12131 const int nbN = nodes.size() / (iQuad+1 );
12132 for ( inode = 0; inode < nbN && ok; ++inode )
12134 pair<int, TopAbs_ShapeEnum> i_stype =
12135 helper.GetMediumPos( nodes[inode], nodes[(inode+1)%nbN]);
12136 if (( ok = ( i_stype.first > 0 && i_stype.second >= TopAbs_FACE )))
12137 mediumShapes.insert( make_pair ( i_stype.second, i_stype.first ));
12139 if ( ok && mediumShapes.size() > 1 )
12141 set< pair<TopAbs_ShapeEnum, int > >::iterator stype_i = mediumShapes.begin();
12142 pair<TopAbs_ShapeEnum, int> stype_i_0 = *stype_i;
12143 for ( ++stype_i; stype_i != mediumShapes.end() && ok; ++stype_i )
12145 if (( ok = ( stype_i->first != stype_i_0.first )))
12146 ok = helper.IsSubShape( aMesh->IndexToShape( stype_i->second ),
12147 aMesh->IndexToShape( stype_i_0.second ));
12150 if ( ok && mediumShapes.begin()->first == missShapeType )
12151 aMesh->SetMeshElementOnShape( elem, mediumShapes.begin()->second );
12155 // ----------------------------------
12156 // 3. Copy present boundary elements
12157 // ----------------------------------
12158 if ( toCopyExistingBoundary )
12159 for ( int i = 0 ; i < presentBndElems.size(); ++i )
12161 const SMDS_MeshElement* e = presentBndElems[i];
12162 TConnectivity nodes( e->NbNodes() );
12163 for ( inode = 0; inode < nodes.size(); ++inode )
12164 nodes[inode] = getNodeWithSameID( tgtMeshDS, e->GetNode(inode) );
12165 presentEditor->AddElement(nodes, e->GetType(), e->IsPoly());
12167 else // store present elements to add them to a group
12168 for ( int i = 0 ; i < presentBndElems.size(); ++i )
12170 presentEditor->myLastCreatedElems.Append(presentBndElems[i]);
12173 } // loop on given elements
12175 // ---------------------------------------------
12176 // 4. Fill group with boundary elements
12177 // ---------------------------------------------
12180 if ( SMESHDS_Group* g = dynamic_cast<SMESHDS_Group*>( group->GetGroupDS() ))
12181 for ( int i = 0; i < tgtEditor.myLastCreatedElems.Size(); ++i )
12182 g->SMDSGroup().Add( tgtEditor.myLastCreatedElems( i+1 ));
12184 tgtEditor.myLastCreatedElems.Clear();
12185 tgtEditor2.myLastCreatedElems.Clear();
12187 // -----------------------
12188 // 5. Copy given elements
12189 // -----------------------
12190 if ( toCopyElements && targetMesh != myMesh )
12192 if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
12193 else eIt = elemSetIterator( elements );
12194 while (eIt->more())
12196 const SMDS_MeshElement* elem = eIt->next();
12197 TConnectivity nodes( elem->NbNodes() );
12198 for ( inode = 0; inode < nodes.size(); ++inode )
12199 nodes[inode] = getNodeWithSameID( tgtMeshDS, elem->GetNode(inode) );
12200 tgtEditor.AddElement(nodes, elemType, elem->IsPoly());
12202 tgtEditor.myLastCreatedElems.Clear();