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, false );
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
6442 * \param targetMesh - mesh to create groups in
6443 * \param topPresent - is there "top" elements that are created by sweeping
6445 //=======================================================================
6447 SMESH_MeshEditor::PGroupIDs
6448 SMESH_MeshEditor::generateGroups(const SMESH_SequenceOfElemPtr& nodeGens,
6449 const SMESH_SequenceOfElemPtr& elemGens,
6450 const std::string& postfix,
6451 SMESH_Mesh* targetMesh,
6452 const bool topPresent)
6454 PGroupIDs newGroupIDs( new list<int> );
6455 SMESH_Mesh* mesh = targetMesh ? targetMesh : GetMesh();
6457 // Sort existing groups by types and collect their names
6459 // containers to store an old group and generated new ones;
6460 // 1st new group is for result elems of different type than a source one;
6461 // 2nd new group is for same type result elems ("top" group at extrusion)
6463 using boost::make_tuple;
6464 typedef tuple< SMESHDS_GroupBase*, SMESHDS_Group*, SMESHDS_Group* > TOldNewGroup;
6465 vector< list< TOldNewGroup > > groupsByType( SMDSAbs_NbElementTypes );
6466 vector< TOldNewGroup* > orderedOldNewGroups; // in order of old groups
6468 set< string > groupNames;
6470 SMESH_Mesh::GroupIteratorPtr groupIt = GetMesh()->GetGroups();
6471 if ( !groupIt->more() ) return newGroupIDs;
6473 int newGroupID = mesh->GetGroupIds().back()+1;
6474 while ( groupIt->more() )
6476 SMESH_Group * group = groupIt->next();
6477 if ( !group ) continue;
6478 SMESHDS_GroupBase* groupDS = group->GetGroupDS();
6479 if ( !groupDS || groupDS->IsEmpty() ) continue;
6480 groupNames.insert ( group->GetName() );
6481 groupDS->SetStoreName( group->GetName() );
6482 const SMDSAbs_ElementType type = groupDS->GetType();
6483 SMESHDS_Group* newGroup = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
6484 SMESHDS_Group* newTopGroup = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
6485 groupsByType[ type ].push_back( make_tuple( groupDS, newGroup, newTopGroup ));
6486 orderedOldNewGroups.push_back( & groupsByType[ type ].back() );
6489 // Loop on nodes and elements to add them in new groups
6491 vector< const SMDS_MeshElement* > resultElems;
6492 for ( int isNodes = 0; isNodes < 2; ++isNodes )
6494 const SMESH_SequenceOfElemPtr& gens = isNodes ? nodeGens : elemGens;
6495 const SMESH_SequenceOfElemPtr& elems = isNodes ? myLastCreatedNodes : myLastCreatedElems;
6496 if ( gens.Length() != elems.Length() )
6497 throw SALOME_Exception("SMESH_MeshEditor::generateGroups(): invalid args");
6499 // loop on created elements
6500 for (int iElem = 1; iElem <= elems.Length(); ++iElem )
6502 const SMDS_MeshElement* sourceElem = gens( iElem );
6503 if ( !sourceElem ) {
6504 MESSAGE("generateGroups(): NULL source element");
6507 list< TOldNewGroup > & groupsOldNew = groupsByType[ sourceElem->GetType() ];
6508 if ( groupsOldNew.empty() ) { // no groups of this type at all
6509 while ( iElem < gens.Length() && gens( iElem+1 ) == sourceElem )
6510 ++iElem; // skip all elements made by sourceElem
6513 // collect all elements made by the iElem-th sourceElem
6514 resultElems.clear();
6515 if ( const SMDS_MeshElement* resElem = elems( iElem ))
6516 if ( resElem != sourceElem )
6517 resultElems.push_back( resElem );
6518 while ( iElem < gens.Length() && gens( iElem+1 ) == sourceElem )
6519 if ( const SMDS_MeshElement* resElem = elems( ++iElem ))
6520 if ( resElem != sourceElem )
6521 resultElems.push_back( resElem );
6523 const SMDS_MeshElement* topElem = 0;
6524 if ( isNodes ) // there must be a top element
6526 topElem = resultElems.back();
6527 resultElems.pop_back();
6531 vector< const SMDS_MeshElement* >::reverse_iterator resElemIt = resultElems.rbegin();
6532 for ( ; resElemIt != resultElems.rend() ; ++resElemIt )
6533 if ( (*resElemIt)->GetType() == sourceElem->GetType() )
6535 topElem = *resElemIt;
6536 *resElemIt = 0; // erase *resElemIt
6540 // add resultElems to groups originted from ones the sourceElem belongs to
6541 list< TOldNewGroup >::iterator gOldNew, gLast = groupsOldNew.end();
6542 for ( gOldNew = groupsOldNew.begin(); gOldNew != gLast; ++gOldNew )
6544 SMESHDS_GroupBase* oldGroup = gOldNew->get<0>();
6545 if ( oldGroup->Contains( sourceElem )) // sourceElem is in oldGroup
6547 // fill in a new group
6548 SMDS_MeshGroup & newGroup = gOldNew->get<1>()->SMDSGroup();
6549 vector< const SMDS_MeshElement* >::iterator resLast = resultElems.end(), resElemIt;
6550 for ( resElemIt = resultElems.begin(); resElemIt != resLast; ++resElemIt )
6552 newGroup.Add( *resElemIt );
6554 // fill a "top" group
6557 SMDS_MeshGroup & newTopGroup = gOldNew->get<2>()->SMDSGroup();
6558 newTopGroup.Add( topElem );
6562 } // loop on created elements
6563 }// loop on nodes and elements
6565 // Create new SMESH_Groups from SMESHDS_Groups and remove empty SMESHDS_Groups
6567 list<int> topGrouIds;
6568 for ( size_t i = 0; i < orderedOldNewGroups.size(); ++i )
6570 SMESHDS_GroupBase* oldGroupDS = orderedOldNewGroups[i]->get<0>();
6571 SMESHDS_Group* newGroups[2] = { orderedOldNewGroups[i]->get<1>(),
6572 orderedOldNewGroups[i]->get<2>() };
6573 for ( int is2nd = 0; is2nd < 2; ++is2nd )
6575 SMESHDS_Group* newGroupDS = newGroups[ is2nd ];
6576 if ( newGroupDS->IsEmpty() )
6578 mesh->GetMeshDS()->RemoveGroup( newGroupDS );
6583 newGroupDS->SetType( newGroupDS->GetElements()->next()->GetType() );
6586 const bool isTop = ( topPresent &&
6587 newGroupDS->GetType() == oldGroupDS->GetType() &&
6590 string name = oldGroupDS->GetStoreName();
6591 { // remove trailing whitespaces (issue 22599)
6592 size_t size = name.size();
6593 while ( size > 1 && isspace( name[ size-1 ]))
6595 if ( size != name.size() )
6597 name.resize( size );
6598 oldGroupDS->SetStoreName( name.c_str() );
6601 if ( !targetMesh ) {
6602 string suffix = ( isTop ? "top": postfix.c_str() );
6606 while ( !groupNames.insert( name ).second ) // name exists
6607 name = SMESH_Comment( oldGroupDS->GetStoreName() ) << "_" << suffix << "_" << nb++;
6612 newGroupDS->SetStoreName( name.c_str() );
6614 // make a SMESH_Groups
6615 mesh->AddGroup( newGroupDS );
6617 topGrouIds.push_back( newGroupDS->GetID() );
6619 newGroupIDs->push_back( newGroupDS->GetID() );
6623 newGroupIDs->splice( newGroupIDs->end(), topGrouIds );
6628 //================================================================================
6630 * \brief Return list of group of nodes close to each other within theTolerance
6631 * Search among theNodes or in the whole mesh if theNodes is empty using
6632 * an Octree algorithm
6634 //================================================================================
6636 void SMESH_MeshEditor::FindCoincidentNodes (TIDSortedNodeSet & theNodes,
6637 const double theTolerance,
6638 TListOfListOfNodes & theGroupsOfNodes)
6640 myLastCreatedElems.Clear();
6641 myLastCreatedNodes.Clear();
6643 if ( theNodes.empty() )
6644 { // get all nodes in the mesh
6645 SMDS_NodeIteratorPtr nIt = GetMeshDS()->nodesIterator(/*idInceasingOrder=*/true);
6646 while ( nIt->more() )
6647 theNodes.insert( theNodes.end(),nIt->next());
6650 SMESH_OctreeNode::FindCoincidentNodes ( theNodes, &theGroupsOfNodes, theTolerance);
6653 //=======================================================================
6654 //function : SimplifyFace
6656 //=======================================================================
6658 int SMESH_MeshEditor::SimplifyFace (const vector<const SMDS_MeshNode *>& faceNodes,
6659 vector<const SMDS_MeshNode *>& poly_nodes,
6660 vector<int>& quantities) const
6662 int nbNodes = faceNodes.size();
6667 set<const SMDS_MeshNode*> nodeSet;
6669 // get simple seq of nodes
6670 //const SMDS_MeshNode* simpleNodes[ nbNodes ];
6671 vector<const SMDS_MeshNode*> simpleNodes( nbNodes );
6672 int iSimple = 0, nbUnique = 0;
6674 simpleNodes[iSimple++] = faceNodes[0];
6676 for (int iCur = 1; iCur < nbNodes; iCur++) {
6677 if (faceNodes[iCur] != simpleNodes[iSimple - 1]) {
6678 simpleNodes[iSimple++] = faceNodes[iCur];
6679 if (nodeSet.insert( faceNodes[iCur] ).second)
6683 int nbSimple = iSimple;
6684 if (simpleNodes[nbSimple - 1] == simpleNodes[0]) {
6694 bool foundLoop = (nbSimple > nbUnique);
6697 set<const SMDS_MeshNode*> loopSet;
6698 for (iSimple = 0; iSimple < nbSimple && !foundLoop; iSimple++) {
6699 const SMDS_MeshNode* n = simpleNodes[iSimple];
6700 if (!loopSet.insert( n ).second) {
6704 int iC = 0, curLast = iSimple;
6705 for (; iC < curLast; iC++) {
6706 if (simpleNodes[iC] == n) break;
6708 int loopLen = curLast - iC;
6710 // create sub-element
6712 quantities.push_back(loopLen);
6713 for (; iC < curLast; iC++) {
6714 poly_nodes.push_back(simpleNodes[iC]);
6717 // shift the rest nodes (place from the first loop position)
6718 for (iC = curLast + 1; iC < nbSimple; iC++) {
6719 simpleNodes[iC - loopLen] = simpleNodes[iC];
6721 nbSimple -= loopLen;
6724 } // for (iSimple = 0; iSimple < nbSimple; iSimple++)
6725 } // while (foundLoop)
6729 quantities.push_back(iSimple);
6730 for (int i = 0; i < iSimple; i++)
6731 poly_nodes.push_back(simpleNodes[i]);
6737 //=======================================================================
6738 //function : MergeNodes
6739 //purpose : In each group, the cdr of nodes are substituted by the first one
6741 //=======================================================================
6743 void SMESH_MeshEditor::MergeNodes (TListOfListOfNodes & theGroupsOfNodes)
6745 MESSAGE("MergeNodes");
6746 myLastCreatedElems.Clear();
6747 myLastCreatedNodes.Clear();
6749 SMESHDS_Mesh* aMesh = GetMeshDS();
6751 TNodeNodeMap nodeNodeMap; // node to replace - new node
6752 set<const SMDS_MeshElement*> elems; // all elements with changed nodes
6753 list< int > rmElemIds, rmNodeIds;
6755 // Fill nodeNodeMap and elems
6757 TListOfListOfNodes::iterator grIt = theGroupsOfNodes.begin();
6758 for ( ; grIt != theGroupsOfNodes.end(); grIt++ ) {
6759 list<const SMDS_MeshNode*>& nodes = *grIt;
6760 list<const SMDS_MeshNode*>::iterator nIt = nodes.begin();
6761 const SMDS_MeshNode* nToKeep = *nIt;
6762 //MESSAGE("node to keep " << nToKeep->GetID());
6763 for ( ++nIt; nIt != nodes.end(); nIt++ ) {
6764 const SMDS_MeshNode* nToRemove = *nIt;
6765 nodeNodeMap.insert( TNodeNodeMap::value_type( nToRemove, nToKeep ));
6766 if ( nToRemove != nToKeep ) {
6767 //MESSAGE(" node to remove " << nToRemove->GetID());
6768 rmNodeIds.push_back( nToRemove->GetID() );
6769 AddToSameGroups( nToKeep, nToRemove, aMesh );
6770 // set _alwaysComputed to a sub-mesh of VERTEX to enable mesh computing
6771 // after MergeNodes() w/o creating node in place of merged ones.
6772 const SMDS_PositionPtr& pos = nToRemove->GetPosition();
6773 if ( pos && pos->GetTypeOfPosition() == SMDS_TOP_VERTEX )
6774 if ( SMESH_subMesh* sm = myMesh->GetSubMeshContaining( nToRemove->getshapeId() ))
6775 sm->SetIsAlwaysComputed( true );
6778 SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
6779 while ( invElemIt->more() ) {
6780 const SMDS_MeshElement* elem = invElemIt->next();
6785 // Change element nodes or remove an element
6787 set<const SMDS_MeshElement*>::iterator eIt = elems.begin();
6788 for ( ; eIt != elems.end(); eIt++ ) {
6789 const SMDS_MeshElement* elem = *eIt;
6790 //MESSAGE(" ---- inverse elem on node to remove " << elem->GetID());
6791 int nbNodes = elem->NbNodes();
6792 int aShapeId = FindShape( elem );
6794 set<const SMDS_MeshNode*> nodeSet;
6795 vector< const SMDS_MeshNode*> curNodes( nbNodes ), uniqueNodes( nbNodes );
6796 int iUnique = 0, iCur = 0, nbRepl = 0;
6797 vector<int> iRepl( nbNodes );
6799 // get new seq of nodes
6800 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6801 while ( itN->more() ) {
6802 const SMDS_MeshNode* n =
6803 static_cast<const SMDS_MeshNode*>( itN->next() );
6805 TNodeNodeMap::iterator nnIt = nodeNodeMap.find( n );
6806 if ( nnIt != nodeNodeMap.end() ) { // n sticks
6808 // BUG 0020185: begin
6810 bool stopRecur = false;
6811 set<const SMDS_MeshNode*> nodesRecur;
6812 nodesRecur.insert(n);
6813 while (!stopRecur) {
6814 TNodeNodeMap::iterator nnIt_i = nodeNodeMap.find( n );
6815 if ( nnIt_i != nodeNodeMap.end() ) { // n sticks
6816 n = (*nnIt_i).second;
6817 if (!nodesRecur.insert(n).second) {
6818 // error: recursive dependancy
6828 curNodes[ iCur ] = n;
6829 bool isUnique = nodeSet.insert( n ).second;
6831 uniqueNodes[ iUnique++ ] = n;
6833 iRepl[ nbRepl++ ] = iCur;
6837 // Analyse element topology after replacement
6840 int nbUniqueNodes = nodeSet.size();
6841 //MESSAGE("nbNodes nbUniqueNodes " << nbNodes << " " << nbUniqueNodes);
6842 if ( nbNodes != nbUniqueNodes ) { // some nodes stick
6843 // Polygons and Polyhedral volumes
6844 if (elem->IsPoly()) {
6846 if (elem->GetType() == SMDSAbs_Face) {
6848 vector<const SMDS_MeshNode *> face_nodes (nbNodes);
6850 for (; inode < nbNodes; inode++) {
6851 face_nodes[inode] = curNodes[inode];
6854 vector<const SMDS_MeshNode *> polygons_nodes;
6855 vector<int> quantities;
6856 int nbNew = SimplifyFace(face_nodes, polygons_nodes, quantities);
6859 for (int iface = 0; iface < nbNew; iface++) {
6860 int nbNodes = quantities[iface];
6861 vector<const SMDS_MeshNode *> poly_nodes (nbNodes);
6862 for (int ii = 0; ii < nbNodes; ii++, inode++) {
6863 poly_nodes[ii] = polygons_nodes[inode];
6865 SMDS_MeshElement* newElem = aMesh->AddPolygonalFace(poly_nodes);
6866 myLastCreatedElems.Append(newElem);
6868 aMesh->SetMeshElementOnShape(newElem, aShapeId);
6871 MESSAGE("ChangeElementNodes MergeNodes Polygon");
6872 //aMesh->ChangeElementNodes(elem, &polygons_nodes[inode], quantities[nbNew - 1]);
6873 vector<const SMDS_MeshNode *> polynodes(polygons_nodes.begin()+inode,polygons_nodes.end());
6875 if (nbNew > 0) quid = nbNew - 1;
6876 vector<int> newquant(quantities.begin()+quid, quantities.end());
6877 const SMDS_MeshElement* newElem = 0;
6878 newElem = aMesh->AddPolyhedralVolume(polynodes, newquant);
6879 myLastCreatedElems.Append(newElem);
6880 if ( aShapeId && newElem )
6881 aMesh->SetMeshElementOnShape( newElem, aShapeId );
6882 rmElemIds.push_back(elem->GetID());
6885 rmElemIds.push_back(elem->GetID());
6889 else if (elem->GetType() == SMDSAbs_Volume) {
6890 // Polyhedral volume
6891 if (nbUniqueNodes < 4) {
6892 rmElemIds.push_back(elem->GetID());
6895 // each face has to be analyzed in order to check volume validity
6896 const SMDS_VtkVolume* aPolyedre =
6897 dynamic_cast<const SMDS_VtkVolume*>( elem );
6899 int nbFaces = aPolyedre->NbFaces();
6901 vector<const SMDS_MeshNode *> poly_nodes;
6902 vector<int> quantities;
6904 for (int iface = 1; iface <= nbFaces; iface++) {
6905 int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
6906 vector<const SMDS_MeshNode *> faceNodes (nbFaceNodes);
6908 for (int inode = 1; inode <= nbFaceNodes; inode++) {
6909 const SMDS_MeshNode * faceNode = aPolyedre->GetFaceNode(iface, inode);
6910 TNodeNodeMap::iterator nnIt = nodeNodeMap.find(faceNode);
6911 if (nnIt != nodeNodeMap.end()) { // faceNode sticks
6912 faceNode = (*nnIt).second;
6914 faceNodes[inode - 1] = faceNode;
6917 SimplifyFace(faceNodes, poly_nodes, quantities);
6920 if (quantities.size() > 3) {
6921 // to be done: remove coincident faces
6924 if (quantities.size() > 3)
6926 MESSAGE("ChangeElementNodes MergeNodes Polyhedron");
6927 //aMesh->ChangePolyhedronNodes(elem, poly_nodes, quantities);
6928 const SMDS_MeshElement* newElem = 0;
6929 newElem = aMesh->AddPolyhedralVolume(poly_nodes, quantities);
6930 myLastCreatedElems.Append(newElem);
6931 if ( aShapeId && newElem )
6932 aMesh->SetMeshElementOnShape( newElem, aShapeId );
6933 rmElemIds.push_back(elem->GetID());
6937 rmElemIds.push_back(elem->GetID());
6948 // TODO not all the possible cases are solved. Find something more generic?
6949 switch ( nbNodes ) {
6950 case 2: ///////////////////////////////////// EDGE
6951 isOk = false; break;
6952 case 3: ///////////////////////////////////// TRIANGLE
6953 isOk = false; break;
6955 if ( elem->GetType() == SMDSAbs_Volume ) // TETRAHEDRON
6957 else { //////////////////////////////////// QUADRANGLE
6958 if ( nbUniqueNodes < 3 )
6960 else if ( nbRepl == 2 && iRepl[ 1 ] - iRepl[ 0 ] == 2 )
6961 isOk = false; // opposite nodes stick
6962 //MESSAGE("isOk " << isOk);
6965 case 6: ///////////////////////////////////// PENTAHEDRON
6966 if ( nbUniqueNodes == 4 ) {
6967 // ---------------------------------> tetrahedron
6969 iRepl[ 0 ] > 2 && iRepl[ 1 ] > 2 && iRepl[ 2 ] > 2 ) {
6970 // all top nodes stick: reverse a bottom
6971 uniqueNodes[ 0 ] = curNodes [ 1 ];
6972 uniqueNodes[ 1 ] = curNodes [ 0 ];
6974 else if (nbRepl == 3 &&
6975 iRepl[ 0 ] < 3 && iRepl[ 1 ] < 3 && iRepl[ 2 ] < 3 ) {
6976 // all bottom nodes stick: set a top before
6977 uniqueNodes[ 3 ] = uniqueNodes [ 0 ];
6978 uniqueNodes[ 0 ] = curNodes [ 3 ];
6979 uniqueNodes[ 1 ] = curNodes [ 4 ];
6980 uniqueNodes[ 2 ] = curNodes [ 5 ];
6982 else if (nbRepl == 4 &&
6983 iRepl[ 2 ] - iRepl [ 0 ] == 3 && iRepl[ 3 ] - iRepl [ 1 ] == 3 ) {
6984 // a lateral face turns into a line: reverse a bottom
6985 uniqueNodes[ 0 ] = curNodes [ 1 ];
6986 uniqueNodes[ 1 ] = curNodes [ 0 ];
6991 else if ( nbUniqueNodes == 5 ) {
6992 // PENTAHEDRON --------------------> 2 tetrahedrons
6993 if ( nbRepl == 2 && iRepl[ 1 ] - iRepl [ 0 ] == 3 ) {
6994 // a bottom node sticks with a linked top one
6996 SMDS_MeshElement* newElem =
6997 aMesh->AddVolume(curNodes[ 3 ],
7000 curNodes[ iRepl[ 0 ] == 2 ? 1 : 2 ]);
7001 myLastCreatedElems.Append(newElem);
7003 aMesh->SetMeshElementOnShape( newElem, aShapeId );
7004 // 2. : reverse a bottom
7005 uniqueNodes[ 0 ] = curNodes [ 1 ];
7006 uniqueNodes[ 1 ] = curNodes [ 0 ];
7016 if(elem->IsQuadratic()) { // Quadratic quadrangle
7028 MESSAGE("nbRepl=2: " << iRepl[0] << " " << iRepl[1]);
7031 MESSAGE("nbRepl=3: " << iRepl[0] << " " << iRepl[1] << " " << iRepl[2]);
7033 if( iRepl[0]==0 && iRepl[1]==1 && iRepl[2]==4 ) {
7034 uniqueNodes[0] = curNodes[0];
7035 uniqueNodes[1] = curNodes[2];
7036 uniqueNodes[2] = curNodes[3];
7037 uniqueNodes[3] = curNodes[5];
7038 uniqueNodes[4] = curNodes[6];
7039 uniqueNodes[5] = curNodes[7];
7042 if( iRepl[0]==0 && iRepl[1]==3 && iRepl[2]==7 ) {
7043 uniqueNodes[0] = curNodes[0];
7044 uniqueNodes[1] = curNodes[1];
7045 uniqueNodes[2] = curNodes[2];
7046 uniqueNodes[3] = curNodes[4];
7047 uniqueNodes[4] = curNodes[5];
7048 uniqueNodes[5] = curNodes[6];
7051 if( iRepl[0]==0 && iRepl[1]==4 && iRepl[2]==7 ) {
7052 uniqueNodes[0] = curNodes[1];
7053 uniqueNodes[1] = curNodes[2];
7054 uniqueNodes[2] = curNodes[3];
7055 uniqueNodes[3] = curNodes[5];
7056 uniqueNodes[4] = curNodes[6];
7057 uniqueNodes[5] = curNodes[0];
7060 if( iRepl[0]==1 && iRepl[1]==2 && iRepl[2]==5 ) {
7061 uniqueNodes[0] = curNodes[0];
7062 uniqueNodes[1] = curNodes[1];
7063 uniqueNodes[2] = curNodes[3];
7064 uniqueNodes[3] = curNodes[4];
7065 uniqueNodes[4] = curNodes[6];
7066 uniqueNodes[5] = curNodes[7];
7069 if( iRepl[0]==1 && iRepl[1]==4 && iRepl[2]==5 ) {
7070 uniqueNodes[0] = curNodes[0];
7071 uniqueNodes[1] = curNodes[2];
7072 uniqueNodes[2] = curNodes[3];
7073 uniqueNodes[3] = curNodes[1];
7074 uniqueNodes[4] = curNodes[6];
7075 uniqueNodes[5] = curNodes[7];
7078 if( iRepl[0]==2 && iRepl[1]==3 && iRepl[2]==6 ) {
7079 uniqueNodes[0] = curNodes[0];
7080 uniqueNodes[1] = curNodes[1];
7081 uniqueNodes[2] = curNodes[2];
7082 uniqueNodes[3] = curNodes[4];
7083 uniqueNodes[4] = curNodes[5];
7084 uniqueNodes[5] = curNodes[7];
7087 if( iRepl[0]==2 && iRepl[1]==5 && iRepl[2]==6 ) {
7088 uniqueNodes[0] = curNodes[0];
7089 uniqueNodes[1] = curNodes[1];
7090 uniqueNodes[2] = curNodes[3];
7091 uniqueNodes[3] = curNodes[4];
7092 uniqueNodes[4] = curNodes[2];
7093 uniqueNodes[5] = curNodes[7];
7096 if( iRepl[0]==3 && iRepl[1]==6 && iRepl[2]==7 ) {
7097 uniqueNodes[0] = curNodes[0];
7098 uniqueNodes[1] = curNodes[1];
7099 uniqueNodes[2] = curNodes[2];
7100 uniqueNodes[3] = curNodes[4];
7101 uniqueNodes[4] = curNodes[5];
7102 uniqueNodes[5] = curNodes[3];
7107 MESSAGE("nbRepl=4: " << iRepl[0] << " " << iRepl[1] << " " << iRepl[2] << " " << iRepl[3]);
7110 MESSAGE("nbRepl=5: " << iRepl[0] << " " << iRepl[1] << " " << iRepl[2] << " " << iRepl[3] << " " << iRepl[4]);
7114 //////////////////////////////////// HEXAHEDRON
7116 SMDS_VolumeTool hexa (elem);
7117 hexa.SetExternalNormal();
7118 if ( nbUniqueNodes == 4 && nbRepl == 4 ) {
7119 //////////////////////// HEX ---> 1 tetrahedron
7120 for ( int iFace = 0; iFace < 6; iFace++ ) {
7121 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7122 if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7123 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7124 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7125 // one face turns into a point ...
7126 int iOppFace = hexa.GetOppFaceIndex( iFace );
7127 ind = hexa.GetFaceNodesIndices( iOppFace );
7129 for ( iCur = 0; iCur < 4 && nbStick < 2; iCur++ ) {
7130 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7133 if ( nbStick == 1 ) {
7134 // ... and the opposite one - into a triangle.
7136 ind = hexa.GetFaceNodesIndices( iFace );
7137 uniqueNodes[ 3 ] = curNodes[ind[ 0 ]];
7144 else if ( nbUniqueNodes == 6 && nbRepl == 2 ) {
7145 //////////////////////// HEX ---> 1 prism
7146 int nbTria = 0, iTria[3];
7147 const int *ind; // indices of face nodes
7148 // look for triangular faces
7149 for ( int iFace = 0; iFace < 6 && nbTria < 3; iFace++ ) {
7150 ind = hexa.GetFaceNodesIndices( iFace );
7151 TIDSortedNodeSet faceNodes;
7152 for ( iCur = 0; iCur < 4; iCur++ )
7153 faceNodes.insert( curNodes[ind[iCur]] );
7154 if ( faceNodes.size() == 3 )
7155 iTria[ nbTria++ ] = iFace;
7157 // check if triangles are opposite
7158 if ( nbTria == 2 && iTria[0] == hexa.GetOppFaceIndex( iTria[1] ))
7161 // set nodes of the bottom triangle
7162 ind = hexa.GetFaceNodesIndices( iTria[ 0 ]);
7164 for ( iCur = 0; iCur < 4; iCur++ )
7165 if ( ind[iCur] != iRepl[0] && ind[iCur] != iRepl[1])
7166 indB.push_back( ind[iCur] );
7167 if ( !hexa.IsForward() )
7168 std::swap( indB[0], indB[2] );
7169 for ( iCur = 0; iCur < 3; iCur++ )
7170 uniqueNodes[ iCur ] = curNodes[indB[iCur]];
7171 // set nodes of the top triangle
7172 const int *indT = hexa.GetFaceNodesIndices( iTria[ 1 ]);
7173 for ( iCur = 0; iCur < 3; ++iCur )
7174 for ( int j = 0; j < 4; ++j )
7175 if ( hexa.IsLinked( indB[ iCur ], indT[ j ] ))
7177 uniqueNodes[ iCur + 3 ] = curNodes[ indT[ j ]];
7183 else if (nbUniqueNodes == 5 && nbRepl == 4 ) {
7184 //////////////////// HEXAHEDRON ---> 2 tetrahedrons
7185 for ( int iFace = 0; iFace < 6; iFace++ ) {
7186 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7187 if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7188 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7189 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7190 // one face turns into a point ...
7191 int iOppFace = hexa.GetOppFaceIndex( iFace );
7192 ind = hexa.GetFaceNodesIndices( iOppFace );
7194 iUnique = 2; // reverse a tetrahedron 1 bottom
7195 for ( iCur = 0; iCur < 4 && nbStick == 0; iCur++ ) {
7196 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7198 else if ( iUnique >= 0 )
7199 uniqueNodes[ iUnique-- ] = curNodes[ind[ iCur ]];
7201 if ( nbStick == 0 ) {
7202 // ... and the opposite one is a quadrangle
7204 const int* indTop = hexa.GetFaceNodesIndices( iFace );
7205 uniqueNodes[ 3 ] = curNodes[indTop[ 0 ]];
7208 SMDS_MeshElement* newElem =
7209 aMesh->AddVolume(curNodes[ind[ 0 ]],
7212 curNodes[indTop[ 0 ]]);
7213 myLastCreatedElems.Append(newElem);
7215 aMesh->SetMeshElementOnShape( newElem, aShapeId );
7222 else if ( nbUniqueNodes == 6 && nbRepl == 4 ) {
7223 ////////////////// HEXAHEDRON ---> 2 tetrahedrons or 1 prism
7224 // find indices of quad and tri faces
7225 int iQuadFace[ 6 ], iTriFace[ 6 ], nbQuad = 0, nbTri = 0, iFace;
7226 for ( iFace = 0; iFace < 6; iFace++ ) {
7227 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7229 for ( iCur = 0; iCur < 4; iCur++ )
7230 nodeSet.insert( curNodes[ind[ iCur ]] );
7231 nbUniqueNodes = nodeSet.size();
7232 if ( nbUniqueNodes == 3 )
7233 iTriFace[ nbTri++ ] = iFace;
7234 else if ( nbUniqueNodes == 4 )
7235 iQuadFace[ nbQuad++ ] = iFace;
7237 if (nbQuad == 2 && nbTri == 4 &&
7238 hexa.GetOppFaceIndex( iQuadFace[ 0 ] ) == iQuadFace[ 1 ]) {
7239 // 2 opposite quadrangles stuck with a diagonal;
7240 // sample groups of merged indices: (0-4)(2-6)
7241 // --------------------------------------------> 2 tetrahedrons
7242 const int *ind1 = hexa.GetFaceNodesIndices( iQuadFace[ 0 ]); // indices of quad1 nodes
7243 const int *ind2 = hexa.GetFaceNodesIndices( iQuadFace[ 1 ]);
7244 int i0, i1d, i2, i3d, i0t, i2t; // d-daigonal, t-top
7245 if (curNodes[ind1[ 0 ]] == curNodes[ind2[ 0 ]] &&
7246 curNodes[ind1[ 2 ]] == curNodes[ind2[ 2 ]]) {
7247 // stuck with 0-2 diagonal
7255 else if (curNodes[ind1[ 1 ]] == curNodes[ind2[ 3 ]] &&
7256 curNodes[ind1[ 3 ]] == curNodes[ind2[ 1 ]]) {
7257 // stuck with 1-3 diagonal
7269 uniqueNodes[ 0 ] = curNodes [ i0 ];
7270 uniqueNodes[ 1 ] = curNodes [ i1d ];
7271 uniqueNodes[ 2 ] = curNodes [ i3d ];
7272 uniqueNodes[ 3 ] = curNodes [ i0t ];
7275 SMDS_MeshElement* newElem = aMesh->AddVolume(curNodes[ i1d ],
7279 myLastCreatedElems.Append(newElem);
7281 aMesh->SetMeshElementOnShape( newElem, aShapeId );
7284 else if (( nbTri == 2 && nbQuad == 3 ) || // merged (0-4)(1-5)
7285 ( nbTri == 4 && nbQuad == 2 )) { // merged (7-4)(1-5)
7286 // --------------------------------------------> prism
7287 // find 2 opposite triangles
7289 for ( iFace = 0; iFace + 1 < nbTri; iFace++ ) {
7290 if ( hexa.GetOppFaceIndex( iTriFace[ iFace ] ) == iTriFace[ iFace + 1 ]) {
7291 // find indices of kept and replaced nodes
7292 // and fill unique nodes of 2 opposite triangles
7293 const int *ind1 = hexa.GetFaceNodesIndices( iTriFace[ iFace ]);
7294 const int *ind2 = hexa.GetFaceNodesIndices( iTriFace[ iFace + 1 ]);
7295 const SMDS_MeshNode** hexanodes = hexa.GetNodes();
7296 // fill unique nodes
7299 for ( iCur = 0; iCur < 4 && isOk; iCur++ ) {
7300 const SMDS_MeshNode* n = curNodes[ind1[ iCur ]];
7301 const SMDS_MeshNode* nInit = hexanodes[ind1[ iCur ]];
7303 // iCur of a linked node of the opposite face (make normals co-directed):
7304 int iCurOpp = ( iCur == 1 || iCur == 3 ) ? 4 - iCur : iCur;
7305 // check that correspondent corners of triangles are linked
7306 if ( !hexa.IsLinked( ind1[ iCur ], ind2[ iCurOpp ] ))
7309 uniqueNodes[ iUnique ] = n;
7310 uniqueNodes[ iUnique + 3 ] = curNodes[ind2[ iCurOpp ]];
7319 } // if ( nbUniqueNodes == 6 && nbRepl == 4 )
7322 MESSAGE("MergeNodes() removes hexahedron "<< elem);
7329 } // switch ( nbNodes )
7331 } // if ( nbNodes != nbUniqueNodes ) // some nodes stick
7333 if ( isOk ) { // the elem remains valid after sticking nodes
7334 if (elem->IsPoly() && elem->GetType() == SMDSAbs_Volume)
7336 // Change nodes of polyedre
7337 const SMDS_VtkVolume* aPolyedre =
7338 dynamic_cast<const SMDS_VtkVolume*>( elem );
7340 int nbFaces = aPolyedre->NbFaces();
7342 vector<const SMDS_MeshNode *> poly_nodes;
7343 vector<int> quantities (nbFaces);
7345 for (int iface = 1; iface <= nbFaces; iface++) {
7346 int inode, nbFaceNodes = aPolyedre->NbFaceNodes(iface);
7347 quantities[iface - 1] = nbFaceNodes;
7349 for (inode = 1; inode <= nbFaceNodes; inode++) {
7350 const SMDS_MeshNode* curNode = aPolyedre->GetFaceNode(iface, inode);
7352 TNodeNodeMap::iterator nnIt = nodeNodeMap.find( curNode );
7353 if (nnIt != nodeNodeMap.end()) { // curNode sticks
7354 curNode = (*nnIt).second;
7356 poly_nodes.push_back(curNode);
7359 aMesh->ChangePolyhedronNodes( elem, poly_nodes, quantities );
7362 else // replace non-polyhedron elements
7364 const SMDSAbs_ElementType etyp = elem->GetType();
7365 const int elemId = elem->GetID();
7366 const bool isPoly = (elem->GetEntityType() == SMDSEntity_Polygon);
7367 uniqueNodes.resize(nbUniqueNodes);
7369 SMESHDS_SubMesh * sm = aShapeId > 0 ? aMesh->MeshElements(aShapeId) : 0;
7371 aMesh->RemoveFreeElement(elem, sm, /*fromGroups=*/false);
7372 SMDS_MeshElement* newElem = this->AddElement(uniqueNodes, etyp, isPoly, elemId);
7373 if ( sm && newElem )
7374 sm->AddElement( newElem );
7375 if ( elem != newElem )
7376 ReplaceElemInGroups( elem, newElem, aMesh );
7380 // Remove invalid regular element or invalid polygon
7381 rmElemIds.push_back( elem->GetID() );
7384 } // loop on elements
7386 // Remove bad elements, then equal nodes (order important)
7388 Remove( rmElemIds, false );
7389 Remove( rmNodeIds, true );
7394 // ========================================================
7395 // class : SortableElement
7396 // purpose : allow sorting elements basing on their nodes
7397 // ========================================================
7398 class SortableElement : public set <const SMDS_MeshElement*>
7402 SortableElement( const SMDS_MeshElement* theElem )
7405 SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator();
7406 while ( nodeIt->more() )
7407 this->insert( nodeIt->next() );
7410 const SMDS_MeshElement* Get() const
7413 void Set(const SMDS_MeshElement* e) const
7418 mutable const SMDS_MeshElement* myElem;
7421 //=======================================================================
7422 //function : FindEqualElements
7423 //purpose : Return list of group of elements built on the same nodes.
7424 // Search among theElements or in the whole mesh if theElements is empty
7425 //=======================================================================
7427 void SMESH_MeshEditor::FindEqualElements(TIDSortedElemSet & theElements,
7428 TListOfListOfElementsID & theGroupsOfElementsID)
7430 myLastCreatedElems.Clear();
7431 myLastCreatedNodes.Clear();
7433 typedef map< SortableElement, int > TMapOfNodeSet;
7434 typedef list<int> TGroupOfElems;
7436 if ( theElements.empty() )
7437 { // get all elements in the mesh
7438 SMDS_ElemIteratorPtr eIt = GetMeshDS()->elementsIterator();
7439 while ( eIt->more() )
7440 theElements.insert( theElements.end(), eIt->next());
7443 vector< TGroupOfElems > arrayOfGroups;
7444 TGroupOfElems groupOfElems;
7445 TMapOfNodeSet mapOfNodeSet;
7447 TIDSortedElemSet::iterator elemIt = theElements.begin();
7448 for ( int i = 0, j=0; elemIt != theElements.end(); ++elemIt, ++j ) {
7449 const SMDS_MeshElement* curElem = *elemIt;
7450 SortableElement SE(curElem);
7453 pair< TMapOfNodeSet::iterator, bool> pp = mapOfNodeSet.insert(make_pair(SE, i));
7454 if( !(pp.second) ) {
7455 TMapOfNodeSet::iterator& itSE = pp.first;
7456 ind = (*itSE).second;
7457 arrayOfGroups[ind].push_back(curElem->GetID());
7460 groupOfElems.clear();
7461 groupOfElems.push_back(curElem->GetID());
7462 arrayOfGroups.push_back(groupOfElems);
7467 vector< TGroupOfElems >::iterator groupIt = arrayOfGroups.begin();
7468 for ( ; groupIt != arrayOfGroups.end(); ++groupIt ) {
7469 groupOfElems = *groupIt;
7470 if ( groupOfElems.size() > 1 ) {
7471 groupOfElems.sort();
7472 theGroupsOfElementsID.push_back(groupOfElems);
7477 //=======================================================================
7478 //function : MergeElements
7479 //purpose : In each given group, substitute all elements by the first one.
7480 //=======================================================================
7482 void SMESH_MeshEditor::MergeElements(TListOfListOfElementsID & theGroupsOfElementsID)
7484 myLastCreatedElems.Clear();
7485 myLastCreatedNodes.Clear();
7487 typedef list<int> TListOfIDs;
7488 TListOfIDs rmElemIds; // IDs of elems to remove
7490 SMESHDS_Mesh* aMesh = GetMeshDS();
7492 TListOfListOfElementsID::iterator groupsIt = theGroupsOfElementsID.begin();
7493 while ( groupsIt != theGroupsOfElementsID.end() ) {
7494 TListOfIDs& aGroupOfElemID = *groupsIt;
7495 aGroupOfElemID.sort();
7496 int elemIDToKeep = aGroupOfElemID.front();
7497 const SMDS_MeshElement* elemToKeep = aMesh->FindElement(elemIDToKeep);
7498 aGroupOfElemID.pop_front();
7499 TListOfIDs::iterator idIt = aGroupOfElemID.begin();
7500 while ( idIt != aGroupOfElemID.end() ) {
7501 int elemIDToRemove = *idIt;
7502 const SMDS_MeshElement* elemToRemove = aMesh->FindElement(elemIDToRemove);
7503 // add the kept element in groups of removed one (PAL15188)
7504 AddToSameGroups( elemToKeep, elemToRemove, aMesh );
7505 rmElemIds.push_back( elemIDToRemove );
7511 Remove( rmElemIds, false );
7514 //=======================================================================
7515 //function : MergeEqualElements
7516 //purpose : Remove all but one of elements built on the same nodes.
7517 //=======================================================================
7519 void SMESH_MeshEditor::MergeEqualElements()
7521 TIDSortedElemSet aMeshElements; /* empty input ==
7522 to merge equal elements in the whole mesh */
7523 TListOfListOfElementsID aGroupsOfElementsID;
7524 FindEqualElements(aMeshElements, aGroupsOfElementsID);
7525 MergeElements(aGroupsOfElementsID);
7528 //=======================================================================
7529 //function : findAdjacentFace
7531 //=======================================================================
7533 static const SMDS_MeshElement* findAdjacentFace(const SMDS_MeshNode* n1,
7534 const SMDS_MeshNode* n2,
7535 const SMDS_MeshElement* elem)
7537 TIDSortedElemSet elemSet, avoidSet;
7539 avoidSet.insert ( elem );
7540 return SMESH_MeshAlgos::FindFaceInSet( n1, n2, elemSet, avoidSet );
7543 //=======================================================================
7544 //function : FindFreeBorder
7546 //=======================================================================
7548 #define ControlFreeBorder SMESH::Controls::FreeEdges::IsFreeEdge
7550 bool SMESH_MeshEditor::FindFreeBorder (const SMDS_MeshNode* theFirstNode,
7551 const SMDS_MeshNode* theSecondNode,
7552 const SMDS_MeshNode* theLastNode,
7553 list< const SMDS_MeshNode* > & theNodes,
7554 list< const SMDS_MeshElement* >& theFaces)
7556 if ( !theFirstNode || !theSecondNode )
7558 // find border face between theFirstNode and theSecondNode
7559 const SMDS_MeshElement* curElem = findAdjacentFace( theFirstNode, theSecondNode, 0 );
7563 theFaces.push_back( curElem );
7564 theNodes.push_back( theFirstNode );
7565 theNodes.push_back( theSecondNode );
7567 //vector<const SMDS_MeshNode*> nodes;
7568 const SMDS_MeshNode *nIgnore = theFirstNode, *nStart = theSecondNode;
7569 TIDSortedElemSet foundElems;
7570 bool needTheLast = ( theLastNode != 0 );
7572 while ( nStart != theLastNode ) {
7573 if ( nStart == theFirstNode )
7574 return !needTheLast;
7576 // find all free border faces sharing form nStart
7578 list< const SMDS_MeshElement* > curElemList;
7579 list< const SMDS_MeshNode* > nStartList;
7580 SMDS_ElemIteratorPtr invElemIt = nStart->GetInverseElementIterator(SMDSAbs_Face);
7581 while ( invElemIt->more() ) {
7582 const SMDS_MeshElement* e = invElemIt->next();
7583 if ( e == curElem || foundElems.insert( e ).second ) {
7585 int iNode = 0, nbNodes = e->NbNodes();
7586 //const SMDS_MeshNode* nodes[nbNodes+1];
7587 vector<const SMDS_MeshNode*> nodes(nbNodes+1);
7589 if(e->IsQuadratic()) {
7590 const SMDS_VtkFace* F =
7591 dynamic_cast<const SMDS_VtkFace*>(e);
7592 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
7593 // use special nodes iterator
7594 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
7595 while( anIter->more() ) {
7596 nodes[ iNode++ ] = cast2Node(anIter->next());
7600 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
7601 while ( nIt->more() )
7602 nodes[ iNode++ ] = static_cast<const SMDS_MeshNode*>( nIt->next() );
7604 nodes[ iNode ] = nodes[ 0 ];
7606 for ( iNode = 0; iNode < nbNodes; iNode++ )
7607 if (((nodes[ iNode ] == nStart && nodes[ iNode + 1] != nIgnore ) ||
7608 (nodes[ iNode + 1] == nStart && nodes[ iNode ] != nIgnore )) &&
7609 ControlFreeBorder( &nodes[ iNode ], e->GetID() ))
7611 nStartList.push_back( nodes[ iNode + ( nodes[ iNode ] == nStart ? 1 : 0 )]);
7612 curElemList.push_back( e );
7616 // analyse the found
7618 int nbNewBorders = curElemList.size();
7619 if ( nbNewBorders == 0 ) {
7620 // no free border furthermore
7621 return !needTheLast;
7623 else if ( nbNewBorders == 1 ) {
7624 // one more element found
7626 nStart = nStartList.front();
7627 curElem = curElemList.front();
7628 theFaces.push_back( curElem );
7629 theNodes.push_back( nStart );
7632 // several continuations found
7633 list< const SMDS_MeshElement* >::iterator curElemIt;
7634 list< const SMDS_MeshNode* >::iterator nStartIt;
7635 // check if one of them reached the last node
7636 if ( needTheLast ) {
7637 for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
7638 curElemIt!= curElemList.end();
7639 curElemIt++, nStartIt++ )
7640 if ( *nStartIt == theLastNode ) {
7641 theFaces.push_back( *curElemIt );
7642 theNodes.push_back( *nStartIt );
7646 // find the best free border by the continuations
7647 list<const SMDS_MeshNode*> contNodes[ 2 ], *cNL;
7648 list<const SMDS_MeshElement*> contFaces[ 2 ], *cFL;
7649 for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
7650 curElemIt!= curElemList.end();
7651 curElemIt++, nStartIt++ )
7653 cNL = & contNodes[ contNodes[0].empty() ? 0 : 1 ];
7654 cFL = & contFaces[ contFaces[0].empty() ? 0 : 1 ];
7655 // find one more free border
7656 if ( ! SMESH_MeshEditor::FindFreeBorder( nStart, *nStartIt, theLastNode, *cNL, *cFL )) {
7660 else if ( !contNodes[0].empty() && !contNodes[1].empty() ) {
7661 // choice: clear a worse one
7662 int iLongest = ( contNodes[0].size() < contNodes[1].size() ? 1 : 0 );
7663 int iWorse = ( needTheLast ? 1 - iLongest : iLongest );
7664 contNodes[ iWorse ].clear();
7665 contFaces[ iWorse ].clear();
7668 if ( contNodes[0].empty() && contNodes[1].empty() )
7671 // append the best free border
7672 cNL = & contNodes[ contNodes[0].empty() ? 1 : 0 ];
7673 cFL = & contFaces[ contFaces[0].empty() ? 1 : 0 ];
7674 theNodes.pop_back(); // remove nIgnore
7675 theNodes.pop_back(); // remove nStart
7676 theFaces.pop_back(); // remove curElem
7677 list< const SMDS_MeshNode* >::iterator nIt = cNL->begin();
7678 list< const SMDS_MeshElement* >::iterator fIt = cFL->begin();
7679 for ( ; nIt != cNL->end(); nIt++ ) theNodes.push_back( *nIt );
7680 for ( ; fIt != cFL->end(); fIt++ ) theFaces.push_back( *fIt );
7683 } // several continuations found
7684 } // while ( nStart != theLastNode )
7689 //=======================================================================
7690 //function : CheckFreeBorderNodes
7691 //purpose : Return true if the tree nodes are on a free border
7692 //=======================================================================
7694 bool SMESH_MeshEditor::CheckFreeBorderNodes(const SMDS_MeshNode* theNode1,
7695 const SMDS_MeshNode* theNode2,
7696 const SMDS_MeshNode* theNode3)
7698 list< const SMDS_MeshNode* > nodes;
7699 list< const SMDS_MeshElement* > faces;
7700 return FindFreeBorder( theNode1, theNode2, theNode3, nodes, faces);
7703 //=======================================================================
7704 //function : SewFreeBorder
7706 //=======================================================================
7708 SMESH_MeshEditor::Sew_Error
7709 SMESH_MeshEditor::SewFreeBorder (const SMDS_MeshNode* theBordFirstNode,
7710 const SMDS_MeshNode* theBordSecondNode,
7711 const SMDS_MeshNode* theBordLastNode,
7712 const SMDS_MeshNode* theSideFirstNode,
7713 const SMDS_MeshNode* theSideSecondNode,
7714 const SMDS_MeshNode* theSideThirdNode,
7715 const bool theSideIsFreeBorder,
7716 const bool toCreatePolygons,
7717 const bool toCreatePolyedrs)
7719 myLastCreatedElems.Clear();
7720 myLastCreatedNodes.Clear();
7722 MESSAGE("::SewFreeBorder()");
7723 Sew_Error aResult = SEW_OK;
7725 // ====================================
7726 // find side nodes and elements
7727 // ====================================
7729 list< const SMDS_MeshNode* > nSide[ 2 ];
7730 list< const SMDS_MeshElement* > eSide[ 2 ];
7731 list< const SMDS_MeshNode* >::iterator nIt[ 2 ];
7732 list< const SMDS_MeshElement* >::iterator eIt[ 2 ];
7736 if (!FindFreeBorder(theBordFirstNode,theBordSecondNode,theBordLastNode,
7737 nSide[0], eSide[0])) {
7738 MESSAGE(" Free Border 1 not found " );
7739 aResult = SEW_BORDER1_NOT_FOUND;
7741 if (theSideIsFreeBorder) {
7744 if (!FindFreeBorder(theSideFirstNode, theSideSecondNode, theSideThirdNode,
7745 nSide[1], eSide[1])) {
7746 MESSAGE(" Free Border 2 not found " );
7747 aResult = ( aResult != SEW_OK ? SEW_BOTH_BORDERS_NOT_FOUND : SEW_BORDER2_NOT_FOUND );
7750 if ( aResult != SEW_OK )
7753 if (!theSideIsFreeBorder) {
7757 // -------------------------------------------------------------------------
7759 // 1. If nodes to merge are not coincident, move nodes of the free border
7760 // from the coord sys defined by the direction from the first to last
7761 // nodes of the border to the correspondent sys of the side 2
7762 // 2. On the side 2, find the links most co-directed with the correspondent
7763 // links of the free border
7764 // -------------------------------------------------------------------------
7766 // 1. Since sewing may break if there are volumes to split on the side 2,
7767 // we wont move nodes but just compute new coordinates for them
7768 typedef map<const SMDS_MeshNode*, gp_XYZ> TNodeXYZMap;
7769 TNodeXYZMap nBordXYZ;
7770 list< const SMDS_MeshNode* >& bordNodes = nSide[ 0 ];
7771 list< const SMDS_MeshNode* >::iterator nBordIt;
7773 gp_XYZ Pb1( theBordFirstNode->X(), theBordFirstNode->Y(), theBordFirstNode->Z() );
7774 gp_XYZ Pb2( theBordLastNode->X(), theBordLastNode->Y(), theBordLastNode->Z() );
7775 gp_XYZ Ps1( theSideFirstNode->X(), theSideFirstNode->Y(), theSideFirstNode->Z() );
7776 gp_XYZ Ps2( theSideSecondNode->X(), theSideSecondNode->Y(), theSideSecondNode->Z() );
7777 double tol2 = 1.e-8;
7778 gp_Vec Vbs1( Pb1 - Ps1 ),Vbs2( Pb2 - Ps2 );
7779 if ( Vbs1.SquareMagnitude() > tol2 || Vbs2.SquareMagnitude() > tol2 ) {
7780 // Need node movement.
7782 // find X and Z axes to create trsf
7783 gp_Vec Zb( Pb1 - Pb2 ), Zs( Ps1 - Ps2 );
7785 if ( X.SquareMagnitude() <= gp::Resolution() * gp::Resolution() )
7787 X = gp_Ax2( gp::Origin(), Zb ).XDirection();
7790 gp_Ax3 toBordAx( Pb1, Zb, X );
7791 gp_Ax3 fromSideAx( Ps1, Zs, X );
7792 gp_Ax3 toGlobalAx( gp::Origin(), gp::DZ(), gp::DX() );
7794 gp_Trsf toBordSys, fromSide2Sys;
7795 toBordSys.SetTransformation( toBordAx );
7796 fromSide2Sys.SetTransformation( fromSideAx, toGlobalAx );
7797 fromSide2Sys.SetScaleFactor( Zs.Magnitude() / Zb.Magnitude() );
7800 for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
7801 const SMDS_MeshNode* n = *nBordIt;
7802 gp_XYZ xyz( n->X(),n->Y(),n->Z() );
7803 toBordSys.Transforms( xyz );
7804 fromSide2Sys.Transforms( xyz );
7805 nBordXYZ.insert( TNodeXYZMap::value_type( n, xyz ));
7809 // just insert nodes XYZ in the nBordXYZ map
7810 for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
7811 const SMDS_MeshNode* n = *nBordIt;
7812 nBordXYZ.insert( TNodeXYZMap::value_type( n, gp_XYZ( n->X(),n->Y(),n->Z() )));
7816 // 2. On the side 2, find the links most co-directed with the correspondent
7817 // links of the free border
7819 list< const SMDS_MeshElement* >& sideElems = eSide[ 1 ];
7820 list< const SMDS_MeshNode* >& sideNodes = nSide[ 1 ];
7821 sideNodes.push_back( theSideFirstNode );
7823 bool hasVolumes = false;
7824 LinkID_Gen aLinkID_Gen( GetMeshDS() );
7825 set<long> foundSideLinkIDs, checkedLinkIDs;
7826 SMDS_VolumeTool volume;
7827 //const SMDS_MeshNode* faceNodes[ 4 ];
7829 const SMDS_MeshNode* sideNode;
7830 const SMDS_MeshElement* sideElem;
7831 const SMDS_MeshNode* prevSideNode = theSideFirstNode;
7832 const SMDS_MeshNode* prevBordNode = theBordFirstNode;
7833 nBordIt = bordNodes.begin();
7835 // border node position and border link direction to compare with
7836 gp_XYZ bordPos = nBordXYZ[ *nBordIt ];
7837 gp_XYZ bordDir = bordPos - nBordXYZ[ prevBordNode ];
7838 // choose next side node by link direction or by closeness to
7839 // the current border node:
7840 bool searchByDir = ( *nBordIt != theBordLastNode );
7842 // find the next node on the Side 2
7844 double maxDot = -DBL_MAX, minDist = DBL_MAX;
7846 checkedLinkIDs.clear();
7847 gp_XYZ prevXYZ( prevSideNode->X(), prevSideNode->Y(), prevSideNode->Z() );
7849 // loop on inverse elements of current node (prevSideNode) on the Side 2
7850 SMDS_ElemIteratorPtr invElemIt = prevSideNode->GetInverseElementIterator();
7851 while ( invElemIt->more() )
7853 const SMDS_MeshElement* elem = invElemIt->next();
7854 // prepare data for a loop on links coming to prevSideNode, of a face or a volume
7855 int iPrevNode, iNode = 0, nbNodes = elem->NbNodes();
7856 vector< const SMDS_MeshNode* > faceNodes( nbNodes, (const SMDS_MeshNode*)0 );
7857 bool isVolume = volume.Set( elem );
7858 const SMDS_MeshNode** nodes = isVolume ? volume.GetNodes() : & faceNodes[0];
7859 if ( isVolume ) // --volume
7861 else if ( elem->GetType()==SMDSAbs_Face ) { // --face
7862 // retrieve all face nodes and find iPrevNode - an index of the prevSideNode
7863 if(elem->IsQuadratic()) {
7864 const SMDS_VtkFace* F =
7865 dynamic_cast<const SMDS_VtkFace*>(elem);
7866 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
7867 // use special nodes iterator
7868 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
7869 while( anIter->more() ) {
7870 nodes[ iNode ] = cast2Node(anIter->next());
7871 if ( nodes[ iNode++ ] == prevSideNode )
7872 iPrevNode = iNode - 1;
7876 SMDS_ElemIteratorPtr nIt = elem->nodesIterator();
7877 while ( nIt->more() ) {
7878 nodes[ iNode ] = cast2Node( nIt->next() );
7879 if ( nodes[ iNode++ ] == prevSideNode )
7880 iPrevNode = iNode - 1;
7883 // there are 2 links to check
7888 // loop on links, to be precise, on the second node of links
7889 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
7890 const SMDS_MeshNode* n = nodes[ iNode ];
7892 if ( !volume.IsLinked( n, prevSideNode ))
7896 if ( iNode ) // a node before prevSideNode
7897 n = nodes[ iPrevNode == 0 ? elem->NbNodes() - 1 : iPrevNode - 1 ];
7898 else // a node after prevSideNode
7899 n = nodes[ iPrevNode + 1 == elem->NbNodes() ? 0 : iPrevNode + 1 ];
7901 // check if this link was already used
7902 long iLink = aLinkID_Gen.GetLinkID( prevSideNode, n );
7903 bool isJustChecked = !checkedLinkIDs.insert( iLink ).second;
7904 if (!isJustChecked &&
7905 foundSideLinkIDs.find( iLink ) == foundSideLinkIDs.end() )
7907 // test a link geometrically
7908 gp_XYZ nextXYZ ( n->X(), n->Y(), n->Z() );
7909 bool linkIsBetter = false;
7910 double dot = 0.0, dist = 0.0;
7911 if ( searchByDir ) { // choose most co-directed link
7912 dot = bordDir * ( nextXYZ - prevXYZ ).Normalized();
7913 linkIsBetter = ( dot > maxDot );
7915 else { // choose link with the node closest to bordPos
7916 dist = ( nextXYZ - bordPos ).SquareModulus();
7917 linkIsBetter = ( dist < minDist );
7919 if ( linkIsBetter ) {
7928 } // loop on inverse elements of prevSideNode
7931 MESSAGE(" Cant find path by links of the Side 2 ");
7932 return SEW_BAD_SIDE_NODES;
7934 sideNodes.push_back( sideNode );
7935 sideElems.push_back( sideElem );
7936 foundSideLinkIDs.insert ( linkID );
7937 prevSideNode = sideNode;
7939 if ( *nBordIt == theBordLastNode )
7940 searchByDir = false;
7942 // find the next border link to compare with
7943 gp_XYZ sidePos( sideNode->X(), sideNode->Y(), sideNode->Z() );
7944 searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
7945 // move to next border node if sideNode is before forward border node (bordPos)
7946 while ( *nBordIt != theBordLastNode && !searchByDir ) {
7947 prevBordNode = *nBordIt;
7949 bordPos = nBordXYZ[ *nBordIt ];
7950 bordDir = bordPos - nBordXYZ[ prevBordNode ];
7951 searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
7955 while ( sideNode != theSideSecondNode );
7957 if ( hasVolumes && sideNodes.size () != bordNodes.size() && !toCreatePolyedrs) {
7958 MESSAGE("VOLUME SPLITTING IS FORBIDDEN");
7959 return SEW_VOLUMES_TO_SPLIT; // volume splitting is forbidden
7961 } // end nodes search on the side 2
7963 // ============================
7964 // sew the border to the side 2
7965 // ============================
7967 int nbNodes[] = { nSide[0].size(), nSide[1].size() };
7968 int maxNbNodes = Max( nbNodes[0], nbNodes[1] );
7970 TListOfListOfNodes nodeGroupsToMerge;
7971 if ( nbNodes[0] == nbNodes[1] ||
7972 ( theSideIsFreeBorder && !theSideThirdNode)) {
7974 // all nodes are to be merged
7976 for (nIt[0] = nSide[0].begin(), nIt[1] = nSide[1].begin();
7977 nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end();
7978 nIt[0]++, nIt[1]++ )
7980 nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
7981 nodeGroupsToMerge.back().push_back( *nIt[1] ); // to keep
7982 nodeGroupsToMerge.back().push_back( *nIt[0] ); // to remove
7987 // insert new nodes into the border and the side to get equal nb of segments
7989 // get normalized parameters of nodes on the borders
7990 //double param[ 2 ][ maxNbNodes ];
7992 param[0] = new double [ maxNbNodes ];
7993 param[1] = new double [ maxNbNodes ];
7995 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
7996 list< const SMDS_MeshNode* >& nodes = nSide[ iBord ];
7997 list< const SMDS_MeshNode* >::iterator nIt = nodes.begin();
7998 const SMDS_MeshNode* nPrev = *nIt;
7999 double bordLength = 0;
8000 for ( iNode = 0; nIt != nodes.end(); nIt++, iNode++ ) { // loop on border nodes
8001 const SMDS_MeshNode* nCur = *nIt;
8002 gp_XYZ segment (nCur->X() - nPrev->X(),
8003 nCur->Y() - nPrev->Y(),
8004 nCur->Z() - nPrev->Z());
8005 double segmentLen = segment.Modulus();
8006 bordLength += segmentLen;
8007 param[ iBord ][ iNode ] = bordLength;
8010 // normalize within [0,1]
8011 for ( iNode = 0; iNode < nbNodes[ iBord ]; iNode++ ) {
8012 param[ iBord ][ iNode ] /= bordLength;
8016 // loop on border segments
8017 const SMDS_MeshNode *nPrev[ 2 ] = { 0, 0 };
8018 int i[ 2 ] = { 0, 0 };
8019 nIt[0] = nSide[0].begin(); eIt[0] = eSide[0].begin();
8020 nIt[1] = nSide[1].begin(); eIt[1] = eSide[1].begin();
8022 TElemOfNodeListMap insertMap;
8023 TElemOfNodeListMap::iterator insertMapIt;
8025 // key: elem to insert nodes into
8026 // value: 2 nodes to insert between + nodes to be inserted
8028 bool next[ 2 ] = { false, false };
8030 // find min adjacent segment length after sewing
8031 double nextParam = 10., prevParam = 0;
8032 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8033 if ( i[ iBord ] + 1 < nbNodes[ iBord ])
8034 nextParam = Min( nextParam, param[iBord][ i[iBord] + 1 ]);
8035 if ( i[ iBord ] > 0 )
8036 prevParam = Max( prevParam, param[iBord][ i[iBord] - 1 ]);
8038 double minParam = Min( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8039 double maxParam = Max( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8040 double minSegLen = Min( nextParam - minParam, maxParam - prevParam );
8042 // choose to insert or to merge nodes
8043 double du = param[ 1 ][ i[1] ] - param[ 0 ][ i[0] ];
8044 if ( Abs( du ) <= minSegLen * 0.2 ) {
8047 nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8048 const SMDS_MeshNode* n0 = *nIt[0];
8049 const SMDS_MeshNode* n1 = *nIt[1];
8050 nodeGroupsToMerge.back().push_back( n1 );
8051 nodeGroupsToMerge.back().push_back( n0 );
8052 // position of node of the border changes due to merge
8053 param[ 0 ][ i[0] ] += du;
8054 // move n1 for the sake of elem shape evaluation during insertion.
8055 // n1 will be removed by MergeNodes() anyway
8056 const_cast<SMDS_MeshNode*>( n0 )->setXYZ( n1->X(), n1->Y(), n1->Z() );
8057 next[0] = next[1] = true;
8062 int intoBord = ( du < 0 ) ? 0 : 1;
8063 const SMDS_MeshElement* elem = *eIt[ intoBord ];
8064 const SMDS_MeshNode* n1 = nPrev[ intoBord ];
8065 const SMDS_MeshNode* n2 = *nIt[ intoBord ];
8066 const SMDS_MeshNode* nIns = *nIt[ 1 - intoBord ];
8067 if ( intoBord == 1 ) {
8068 // move node of the border to be on a link of elem of the side
8069 gp_XYZ p1 (n1->X(), n1->Y(), n1->Z());
8070 gp_XYZ p2 (n2->X(), n2->Y(), n2->Z());
8071 double ratio = du / ( param[ 1 ][ i[1] ] - param[ 1 ][ i[1]-1 ]);
8072 gp_XYZ p = p2 * ( 1 - ratio ) + p1 * ratio;
8073 GetMeshDS()->MoveNode( nIns, p.X(), p.Y(), p.Z() );
8075 insertMapIt = insertMap.find( elem );
8076 bool notFound = ( insertMapIt == insertMap.end() );
8077 bool otherLink = ( !notFound && (*insertMapIt).second.front() != n1 );
8079 // insert into another link of the same element:
8080 // 1. perform insertion into the other link of the elem
8081 list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8082 const SMDS_MeshNode* n12 = nodeList.front(); nodeList.pop_front();
8083 const SMDS_MeshNode* n22 = nodeList.front(); nodeList.pop_front();
8084 InsertNodesIntoLink( elem, n12, n22, nodeList, toCreatePolygons );
8085 // 2. perform insertion into the link of adjacent faces
8087 const SMDS_MeshElement* adjElem = findAdjacentFace( n12, n22, elem );
8089 InsertNodesIntoLink( adjElem, n12, n22, nodeList, toCreatePolygons );
8093 if (toCreatePolyedrs) {
8094 // perform insertion into the links of adjacent volumes
8095 UpdateVolumes(n12, n22, nodeList);
8097 // 3. find an element appeared on n1 and n2 after the insertion
8098 insertMap.erase( elem );
8099 elem = findAdjacentFace( n1, n2, 0 );
8101 if ( notFound || otherLink ) {
8102 // add element and nodes of the side into the insertMap
8103 insertMapIt = insertMap.insert
8104 ( TElemOfNodeListMap::value_type( elem, list<const SMDS_MeshNode*>() )).first;
8105 (*insertMapIt).second.push_back( n1 );
8106 (*insertMapIt).second.push_back( n2 );
8108 // add node to be inserted into elem
8109 (*insertMapIt).second.push_back( nIns );
8110 next[ 1 - intoBord ] = true;
8113 // go to the next segment
8114 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8115 if ( next[ iBord ] ) {
8116 if ( i[ iBord ] != 0 && eIt[ iBord ] != eSide[ iBord ].end())
8118 nPrev[ iBord ] = *nIt[ iBord ];
8119 nIt[ iBord ]++; i[ iBord ]++;
8123 while ( nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end());
8125 // perform insertion of nodes into elements
8127 for (insertMapIt = insertMap.begin();
8128 insertMapIt != insertMap.end();
8131 const SMDS_MeshElement* elem = (*insertMapIt).first;
8132 list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8133 const SMDS_MeshNode* n1 = nodeList.front(); nodeList.pop_front();
8134 const SMDS_MeshNode* n2 = nodeList.front(); nodeList.pop_front();
8136 InsertNodesIntoLink( elem, n1, n2, nodeList, toCreatePolygons );
8138 if ( !theSideIsFreeBorder ) {
8139 // look for and insert nodes into the faces adjacent to elem
8141 const SMDS_MeshElement* adjElem = findAdjacentFace( n1, n2, elem );
8143 InsertNodesIntoLink( adjElem, n1, n2, nodeList, toCreatePolygons );
8148 if (toCreatePolyedrs) {
8149 // perform insertion into the links of adjacent volumes
8150 UpdateVolumes(n1, n2, nodeList);
8156 } // end: insert new nodes
8158 MergeNodes ( nodeGroupsToMerge );
8163 //=======================================================================
8164 //function : InsertNodesIntoLink
8165 //purpose : insert theNodesToInsert into theFace between theBetweenNode1
8166 // and theBetweenNode2 and split theElement
8167 //=======================================================================
8169 void SMESH_MeshEditor::InsertNodesIntoLink(const SMDS_MeshElement* theFace,
8170 const SMDS_MeshNode* theBetweenNode1,
8171 const SMDS_MeshNode* theBetweenNode2,
8172 list<const SMDS_MeshNode*>& theNodesToInsert,
8173 const bool toCreatePoly)
8175 if ( theFace->GetType() != SMDSAbs_Face ) return;
8177 // find indices of 2 link nodes and of the rest nodes
8178 int iNode = 0, il1, il2, i3, i4;
8179 il1 = il2 = i3 = i4 = -1;
8180 //const SMDS_MeshNode* nodes[ theFace->NbNodes() ];
8181 vector<const SMDS_MeshNode*> nodes( theFace->NbNodes() );
8183 if(theFace->IsQuadratic()) {
8184 const SMDS_VtkFace* F =
8185 dynamic_cast<const SMDS_VtkFace*>(theFace);
8186 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8187 // use special nodes iterator
8188 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8189 while( anIter->more() ) {
8190 const SMDS_MeshNode* n = cast2Node(anIter->next());
8191 if ( n == theBetweenNode1 )
8193 else if ( n == theBetweenNode2 )
8199 nodes[ iNode++ ] = n;
8203 SMDS_ElemIteratorPtr nodeIt = theFace->nodesIterator();
8204 while ( nodeIt->more() ) {
8205 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8206 if ( n == theBetweenNode1 )
8208 else if ( n == theBetweenNode2 )
8214 nodes[ iNode++ ] = n;
8217 if ( il1 < 0 || il2 < 0 || i3 < 0 )
8220 // arrange link nodes to go one after another regarding the face orientation
8221 bool reverse = ( Abs( il2 - il1 ) == 1 ? il2 < il1 : il1 < il2 );
8222 list<const SMDS_MeshNode *> aNodesToInsert = theNodesToInsert;
8227 aNodesToInsert.reverse();
8229 // check that not link nodes of a quadrangles are in good order
8230 int nbFaceNodes = theFace->NbNodes();
8231 if ( nbFaceNodes == 4 && i4 - i3 != 1 ) {
8237 if (toCreatePoly || theFace->IsPoly()) {
8240 vector<const SMDS_MeshNode *> poly_nodes (nbFaceNodes + aNodesToInsert.size());
8242 // add nodes of face up to first node of link
8245 if(theFace->IsQuadratic()) {
8246 const SMDS_VtkFace* F =
8247 dynamic_cast<const SMDS_VtkFace*>(theFace);
8248 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8249 // use special nodes iterator
8250 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8251 while( anIter->more() && !isFLN ) {
8252 const SMDS_MeshNode* n = cast2Node(anIter->next());
8253 poly_nodes[iNode++] = n;
8254 if (n == nodes[il1]) {
8258 // add nodes to insert
8259 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8260 for (; nIt != aNodesToInsert.end(); nIt++) {
8261 poly_nodes[iNode++] = *nIt;
8263 // add nodes of face starting from last node of link
8264 while ( anIter->more() ) {
8265 poly_nodes[iNode++] = cast2Node(anIter->next());
8269 SMDS_ElemIteratorPtr nodeIt = theFace->nodesIterator();
8270 while ( nodeIt->more() && !isFLN ) {
8271 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8272 poly_nodes[iNode++] = n;
8273 if (n == nodes[il1]) {
8277 // add nodes to insert
8278 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8279 for (; nIt != aNodesToInsert.end(); nIt++) {
8280 poly_nodes[iNode++] = *nIt;
8282 // add nodes of face starting from last node of link
8283 while ( nodeIt->more() ) {
8284 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8285 poly_nodes[iNode++] = n;
8289 // edit or replace the face
8290 SMESHDS_Mesh *aMesh = GetMeshDS();
8292 if (theFace->IsPoly()) {
8293 aMesh->ChangePolygonNodes(theFace, poly_nodes);
8296 int aShapeId = FindShape( theFace );
8298 SMDS_MeshElement* newElem = aMesh->AddPolygonalFace(poly_nodes);
8299 myLastCreatedElems.Append(newElem);
8300 if ( aShapeId && newElem )
8301 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8303 aMesh->RemoveElement(theFace);
8308 SMESHDS_Mesh *aMesh = GetMeshDS();
8309 if( !theFace->IsQuadratic() ) {
8311 // put aNodesToInsert between theBetweenNode1 and theBetweenNode2
8312 int nbLinkNodes = 2 + aNodesToInsert.size();
8313 //const SMDS_MeshNode* linkNodes[ nbLinkNodes ];
8314 vector<const SMDS_MeshNode*> linkNodes( nbLinkNodes );
8315 linkNodes[ 0 ] = nodes[ il1 ];
8316 linkNodes[ nbLinkNodes - 1 ] = nodes[ il2 ];
8317 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8318 for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
8319 linkNodes[ iNode++ ] = *nIt;
8321 // decide how to split a quadrangle: compare possible variants
8322 // and choose which of splits to be a quadrangle
8323 int i1, i2, iSplit, nbSplits = nbLinkNodes - 1, iBestQuad;
8324 if ( nbFaceNodes == 3 ) {
8325 iBestQuad = nbSplits;
8328 else if ( nbFaceNodes == 4 ) {
8329 SMESH::Controls::NumericalFunctorPtr aCrit( new SMESH::Controls::AspectRatio);
8330 double aBestRate = DBL_MAX;
8331 for ( int iQuad = 0; iQuad < nbSplits; iQuad++ ) {
8333 double aBadRate = 0;
8334 // evaluate elements quality
8335 for ( iSplit = 0; iSplit < nbSplits; iSplit++ ) {
8336 if ( iSplit == iQuad ) {
8337 SMDS_FaceOfNodes quad (linkNodes[ i1++ ],
8341 aBadRate += getBadRate( &quad, aCrit );
8344 SMDS_FaceOfNodes tria (linkNodes[ i1++ ],
8346 nodes[ iSplit < iQuad ? i4 : i3 ]);
8347 aBadRate += getBadRate( &tria, aCrit );
8351 if ( aBadRate < aBestRate ) {
8353 aBestRate = aBadRate;
8358 // create new elements
8359 int aShapeId = FindShape( theFace );
8362 for ( iSplit = 0; iSplit < nbSplits - 1; iSplit++ ) {
8363 SMDS_MeshElement* newElem = 0;
8364 if ( iSplit == iBestQuad )
8365 newElem = aMesh->AddFace (linkNodes[ i1++ ],
8370 newElem = aMesh->AddFace (linkNodes[ i1++ ],
8372 nodes[ iSplit < iBestQuad ? i4 : i3 ]);
8373 myLastCreatedElems.Append(newElem);
8374 if ( aShapeId && newElem )
8375 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8378 // change nodes of theFace
8379 const SMDS_MeshNode* newNodes[ 4 ];
8380 newNodes[ 0 ] = linkNodes[ i1 ];
8381 newNodes[ 1 ] = linkNodes[ i2 ];
8382 newNodes[ 2 ] = nodes[ iSplit >= iBestQuad ? i3 : i4 ];
8383 newNodes[ 3 ] = nodes[ i4 ];
8384 //aMesh->ChangeElementNodes( theFace, newNodes, iSplit == iBestQuad ? 4 : 3 );
8385 const SMDS_MeshElement* newElem = 0;
8386 if (iSplit == iBestQuad)
8387 newElem = aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2], newNodes[3] );
8389 newElem = aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2] );
8390 myLastCreatedElems.Append(newElem);
8391 if ( aShapeId && newElem )
8392 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8393 } // end if(!theFace->IsQuadratic())
8394 else { // theFace is quadratic
8395 // we have to split theFace on simple triangles and one simple quadrangle
8397 int nbshift = tmp*2;
8398 // shift nodes in nodes[] by nbshift
8400 for(i=0; i<nbshift; i++) {
8401 const SMDS_MeshNode* n = nodes[0];
8402 for(j=0; j<nbFaceNodes-1; j++) {
8403 nodes[j] = nodes[j+1];
8405 nodes[nbFaceNodes-1] = n;
8407 il1 = il1 - nbshift;
8408 // now have to insert nodes between n0 and n1 or n1 and n2 (see below)
8409 // n0 n1 n2 n0 n1 n2
8410 // +-----+-----+ +-----+-----+
8419 // create new elements
8420 int aShapeId = FindShape( theFace );
8423 if(nbFaceNodes==6) { // quadratic triangle
8424 SMDS_MeshElement* newElem =
8425 aMesh->AddFace(nodes[3],nodes[4],nodes[5]);
8426 myLastCreatedElems.Append(newElem);
8427 if ( aShapeId && newElem )
8428 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8429 if(theFace->IsMediumNode(nodes[il1])) {
8430 // create quadrangle
8431 newElem = aMesh->AddFace(nodes[0],nodes[1],nodes[3],nodes[5]);
8432 myLastCreatedElems.Append(newElem);
8433 if ( aShapeId && newElem )
8434 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8440 // create quadrangle
8441 newElem = aMesh->AddFace(nodes[1],nodes[2],nodes[3],nodes[5]);
8442 myLastCreatedElems.Append(newElem);
8443 if ( aShapeId && newElem )
8444 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8450 else { // nbFaceNodes==8 - quadratic quadrangle
8451 SMDS_MeshElement* newElem =
8452 aMesh->AddFace(nodes[3],nodes[4],nodes[5]);
8453 myLastCreatedElems.Append(newElem);
8454 if ( aShapeId && newElem )
8455 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8456 newElem = aMesh->AddFace(nodes[5],nodes[6],nodes[7]);
8457 myLastCreatedElems.Append(newElem);
8458 if ( aShapeId && newElem )
8459 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8460 newElem = aMesh->AddFace(nodes[5],nodes[7],nodes[3]);
8461 myLastCreatedElems.Append(newElem);
8462 if ( aShapeId && newElem )
8463 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8464 if(theFace->IsMediumNode(nodes[il1])) {
8465 // create quadrangle
8466 newElem = aMesh->AddFace(nodes[0],nodes[1],nodes[3],nodes[7]);
8467 myLastCreatedElems.Append(newElem);
8468 if ( aShapeId && newElem )
8469 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8475 // create quadrangle
8476 newElem = aMesh->AddFace(nodes[1],nodes[2],nodes[3],nodes[7]);
8477 myLastCreatedElems.Append(newElem);
8478 if ( aShapeId && newElem )
8479 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8485 // create needed triangles using n1,n2,n3 and inserted nodes
8486 int nbn = 2 + aNodesToInsert.size();
8487 //const SMDS_MeshNode* aNodes[nbn];
8488 vector<const SMDS_MeshNode*> aNodes(nbn);
8489 aNodes[0] = nodes[n1];
8490 aNodes[nbn-1] = nodes[n2];
8491 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8492 for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
8493 aNodes[iNode++] = *nIt;
8495 for(i=1; i<nbn; i++) {
8496 SMDS_MeshElement* newElem =
8497 aMesh->AddFace(aNodes[i-1],aNodes[i],nodes[n3]);
8498 myLastCreatedElems.Append(newElem);
8499 if ( aShapeId && newElem )
8500 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8504 aMesh->RemoveElement(theFace);
8507 //=======================================================================
8508 //function : UpdateVolumes
8510 //=======================================================================
8511 void SMESH_MeshEditor::UpdateVolumes (const SMDS_MeshNode* theBetweenNode1,
8512 const SMDS_MeshNode* theBetweenNode2,
8513 list<const SMDS_MeshNode*>& theNodesToInsert)
8515 myLastCreatedElems.Clear();
8516 myLastCreatedNodes.Clear();
8518 SMDS_ElemIteratorPtr invElemIt = theBetweenNode1->GetInverseElementIterator(SMDSAbs_Volume);
8519 while (invElemIt->more()) { // loop on inverse elements of theBetweenNode1
8520 const SMDS_MeshElement* elem = invElemIt->next();
8522 // check, if current volume has link theBetweenNode1 - theBetweenNode2
8523 SMDS_VolumeTool aVolume (elem);
8524 if (!aVolume.IsLinked(theBetweenNode1, theBetweenNode2))
8527 // insert new nodes in all faces of the volume, sharing link theBetweenNode1 - theBetweenNode2
8528 int iface, nbFaces = aVolume.NbFaces();
8529 vector<const SMDS_MeshNode *> poly_nodes;
8530 vector<int> quantities (nbFaces);
8532 for (iface = 0; iface < nbFaces; iface++) {
8533 int nbFaceNodes = aVolume.NbFaceNodes(iface), nbInserted = 0;
8534 // faceNodes will contain (nbFaceNodes + 1) nodes, last = first
8535 const SMDS_MeshNode** faceNodes = aVolume.GetFaceNodes(iface);
8537 for (int inode = 0; inode < nbFaceNodes; inode++) {
8538 poly_nodes.push_back(faceNodes[inode]);
8540 if (nbInserted == 0) {
8541 if (faceNodes[inode] == theBetweenNode1) {
8542 if (faceNodes[inode + 1] == theBetweenNode2) {
8543 nbInserted = theNodesToInsert.size();
8545 // add nodes to insert
8546 list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.begin();
8547 for (; nIt != theNodesToInsert.end(); nIt++) {
8548 poly_nodes.push_back(*nIt);
8552 else if (faceNodes[inode] == theBetweenNode2) {
8553 if (faceNodes[inode + 1] == theBetweenNode1) {
8554 nbInserted = theNodesToInsert.size();
8556 // add nodes to insert in reversed order
8557 list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.end();
8559 for (; nIt != theNodesToInsert.begin(); nIt--) {
8560 poly_nodes.push_back(*nIt);
8562 poly_nodes.push_back(*nIt);
8569 quantities[iface] = nbFaceNodes + nbInserted;
8572 // Replace or update the volume
8573 SMESHDS_Mesh *aMesh = GetMeshDS();
8575 if (elem->IsPoly()) {
8576 aMesh->ChangePolyhedronNodes(elem, poly_nodes, quantities);
8580 int aShapeId = FindShape( elem );
8582 SMDS_MeshElement* newElem =
8583 aMesh->AddPolyhedralVolume(poly_nodes, quantities);
8584 myLastCreatedElems.Append(newElem);
8585 if (aShapeId && newElem)
8586 aMesh->SetMeshElementOnShape(newElem, aShapeId);
8588 aMesh->RemoveElement(elem);
8595 //================================================================================
8597 * \brief Transform any volume into data of SMDSEntity_Polyhedra
8599 //================================================================================
8601 void volumeToPolyhedron( const SMDS_MeshElement* elem,
8602 vector<const SMDS_MeshNode *> & nodes,
8603 vector<int> & nbNodeInFaces )
8606 nbNodeInFaces.clear();
8607 SMDS_VolumeTool vTool ( elem );
8608 for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
8610 const SMDS_MeshNode** fNodes = vTool.GetFaceNodes( iF );
8611 nodes.insert( nodes.end(), fNodes, fNodes + vTool.NbFaceNodes( iF ));
8612 nbNodeInFaces.push_back( vTool.NbFaceNodes( iF ));
8617 //=======================================================================
8619 * \brief Convert elements contained in a submesh to quadratic
8620 * \return int - nb of checked elements
8622 //=======================================================================
8624 int SMESH_MeshEditor::convertElemToQuadratic(SMESHDS_SubMesh * theSm,
8625 SMESH_MesherHelper& theHelper,
8626 const bool theForce3d)
8629 if( !theSm ) return nbElem;
8631 vector<int> nbNodeInFaces;
8632 vector<const SMDS_MeshNode *> nodes;
8633 SMDS_ElemIteratorPtr ElemItr = theSm->GetElements();
8634 while(ElemItr->more())
8637 const SMDS_MeshElement* elem = ElemItr->next();
8638 if( !elem ) continue;
8640 // analyse a necessity of conversion
8641 const SMDSAbs_ElementType aType = elem->GetType();
8642 if ( aType < SMDSAbs_Edge || aType > SMDSAbs_Volume )
8644 const SMDSAbs_EntityType aGeomType = elem->GetEntityType();
8645 bool hasCentralNodes = false;
8646 if ( elem->IsQuadratic() )
8649 switch ( aGeomType ) {
8650 case SMDSEntity_Quad_Triangle:
8651 case SMDSEntity_Quad_Quadrangle:
8652 case SMDSEntity_Quad_Hexa:
8653 alreadyOK = !theHelper.GetIsBiQuadratic(); break;
8655 case SMDSEntity_BiQuad_Triangle:
8656 case SMDSEntity_BiQuad_Quadrangle:
8657 case SMDSEntity_TriQuad_Hexa:
8658 alreadyOK = theHelper.GetIsBiQuadratic();
8659 hasCentralNodes = true;
8664 // take into account already present modium nodes
8666 case SMDSAbs_Volume:
8667 theHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( elem )); break;
8669 theHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( elem )); break;
8671 theHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( elem )); break;
8677 // get elem data needed to re-create it
8679 const int id = elem->GetID();
8680 const int nbNodes = elem->NbCornerNodes();
8681 nodes.assign(elem->begin_nodes(), elem->end_nodes());
8682 if ( aGeomType == SMDSEntity_Polyhedra )
8683 nbNodeInFaces = static_cast<const SMDS_VtkVolume* >( elem )->GetQuantities();
8684 else if ( aGeomType == SMDSEntity_Hexagonal_Prism )
8685 volumeToPolyhedron( elem, nodes, nbNodeInFaces );
8687 // remove a linear element
8688 GetMeshDS()->RemoveFreeElement(elem, theSm, /*fromGroups=*/false);
8690 // remove central nodes of biquadratic elements (biquad->quad convertion)
8691 if ( hasCentralNodes )
8692 for ( size_t i = nbNodes * 2; i < nodes.size(); ++i )
8693 if ( nodes[i]->NbInverseElements() == 0 )
8694 GetMeshDS()->RemoveFreeNode( nodes[i], theSm, /*fromGroups=*/true );
8696 const SMDS_MeshElement* NewElem = 0;
8702 NewElem = theHelper.AddEdge(nodes[0], nodes[1], id, theForce3d);
8710 NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
8713 NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
8716 NewElem = theHelper.AddPolygonalFace(nodes, id, theForce3d);
8720 case SMDSAbs_Volume :
8724 case SMDSEntity_Tetra:
8725 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
8727 case SMDSEntity_Pyramid:
8728 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], id, theForce3d);
8730 case SMDSEntity_Penta:
8731 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], nodes[5], id, theForce3d);
8733 case SMDSEntity_Hexa:
8734 case SMDSEntity_Quad_Hexa:
8735 case SMDSEntity_TriQuad_Hexa:
8736 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
8737 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
8739 case SMDSEntity_Hexagonal_Prism:
8741 NewElem = theHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
8748 ReplaceElemInGroups( elem, NewElem, GetMeshDS());
8749 if( NewElem && NewElem->getshapeId() < 1 )
8750 theSm->AddElement( NewElem );
8754 //=======================================================================
8755 //function : ConvertToQuadratic
8757 //=======================================================================
8759 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d, const bool theToBiQuad)
8761 SMESHDS_Mesh* meshDS = GetMeshDS();
8763 SMESH_MesherHelper aHelper(*myMesh);
8765 aHelper.SetIsQuadratic( true );
8766 aHelper.SetIsBiQuadratic( theToBiQuad );
8767 aHelper.SetElementsOnShape(true);
8768 aHelper.ToFixNodeParameters( true );
8770 // convert elements assigned to sub-meshes
8771 int nbCheckedElems = 0;
8772 if ( myMesh->HasShapeToMesh() )
8774 if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
8776 SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
8777 while ( smIt->more() ) {
8778 SMESH_subMesh* sm = smIt->next();
8779 if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() ) {
8780 aHelper.SetSubShape( sm->GetSubShape() );
8781 nbCheckedElems += convertElemToQuadratic(smDS, aHelper, theForce3d);
8787 // convert elements NOT assigned to sub-meshes
8788 int totalNbElems = meshDS->NbEdges() + meshDS->NbFaces() + meshDS->NbVolumes();
8789 if ( nbCheckedElems < totalNbElems ) // not all elements are in sub-meshes
8791 aHelper.SetElementsOnShape(false);
8792 SMESHDS_SubMesh *smDS = 0;
8795 SMDS_EdgeIteratorPtr aEdgeItr = meshDS->edgesIterator();
8796 while( aEdgeItr->more() )
8798 const SMDS_MeshEdge* edge = aEdgeItr->next();
8799 if ( !edge->IsQuadratic() )
8801 int id = edge->GetID();
8802 const SMDS_MeshNode* n1 = edge->GetNode(0);
8803 const SMDS_MeshNode* n2 = edge->GetNode(1);
8805 meshDS->RemoveFreeElement(edge, smDS, /*fromGroups=*/false);
8807 const SMDS_MeshEdge* NewEdge = aHelper.AddEdge(n1, n2, id, theForce3d);
8808 ReplaceElemInGroups( edge, NewEdge, GetMeshDS());
8812 aHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( edge ));
8817 SMDS_FaceIteratorPtr aFaceItr = meshDS->facesIterator();
8818 while( aFaceItr->more() )
8820 const SMDS_MeshFace* face = aFaceItr->next();
8821 if ( !face ) continue;
8823 const SMDSAbs_EntityType type = face->GetEntityType();
8827 case SMDSEntity_Quad_Triangle:
8828 case SMDSEntity_Quad_Quadrangle:
8829 alreadyOK = !theToBiQuad;
8830 aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
8832 case SMDSEntity_BiQuad_Triangle:
8833 case SMDSEntity_BiQuad_Quadrangle:
8834 alreadyOK = theToBiQuad;
8835 aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
8837 default: alreadyOK = false;
8842 const int id = face->GetID();
8843 vector<const SMDS_MeshNode *> nodes ( face->begin_nodes(), face->end_nodes());
8845 meshDS->RemoveFreeElement(face, smDS, /*fromGroups=*/false);
8847 SMDS_MeshFace * NewFace = 0;
8850 case SMDSEntity_Triangle:
8851 case SMDSEntity_Quad_Triangle:
8852 case SMDSEntity_BiQuad_Triangle:
8853 NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
8854 if ( nodes.size() == 7 && nodes[6]->NbInverseElements() == 0 ) // rm a central node
8855 GetMeshDS()->RemoveFreeNode( nodes[6], /*sm=*/0, /*fromGroups=*/true );
8858 case SMDSEntity_Quadrangle:
8859 case SMDSEntity_Quad_Quadrangle:
8860 case SMDSEntity_BiQuad_Quadrangle:
8861 NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
8862 if ( nodes.size() == 9 && nodes[8]->NbInverseElements() == 0 ) // rm a central node
8863 GetMeshDS()->RemoveFreeNode( nodes[8], /*sm=*/0, /*fromGroups=*/true );
8867 NewFace = aHelper.AddPolygonalFace(nodes, id, theForce3d);
8869 ReplaceElemInGroups( face, NewFace, GetMeshDS());
8873 vector<int> nbNodeInFaces;
8874 SMDS_VolumeIteratorPtr aVolumeItr = meshDS->volumesIterator();
8875 while(aVolumeItr->more())
8877 const SMDS_MeshVolume* volume = aVolumeItr->next();
8878 if ( !volume ) continue;
8880 const SMDSAbs_EntityType type = volume->GetEntityType();
8881 if ( volume->IsQuadratic() )
8886 case SMDSEntity_Quad_Hexa: alreadyOK = !theToBiQuad; break;
8887 case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; break;
8888 default: alreadyOK = true;
8892 aHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( volume ));
8896 const int id = volume->GetID();
8897 vector<const SMDS_MeshNode *> nodes (volume->begin_nodes(), volume->end_nodes());
8898 if ( type == SMDSEntity_Polyhedra )
8899 nbNodeInFaces = static_cast<const SMDS_VtkVolume* >(volume)->GetQuantities();
8900 else if ( type == SMDSEntity_Hexagonal_Prism )
8901 volumeToPolyhedron( volume, nodes, nbNodeInFaces );
8903 meshDS->RemoveFreeElement(volume, smDS, /*fromGroups=*/false);
8905 SMDS_MeshVolume * NewVolume = 0;
8908 case SMDSEntity_Tetra:
8909 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d );
8911 case SMDSEntity_Hexa:
8912 case SMDSEntity_Quad_Hexa:
8913 case SMDSEntity_TriQuad_Hexa:
8914 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
8915 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
8916 for ( size_t i = 20; i < nodes.size(); ++i ) // rm central nodes
8917 if ( nodes[i]->NbInverseElements() == 0 )
8918 GetMeshDS()->RemoveFreeNode( nodes[i], /*sm=*/0, /*fromGroups=*/true );
8920 case SMDSEntity_Pyramid:
8921 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
8922 nodes[3], nodes[4], id, theForce3d);
8924 case SMDSEntity_Penta:
8925 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
8926 nodes[3], nodes[4], nodes[5], id, theForce3d);
8928 case SMDSEntity_Hexagonal_Prism:
8930 NewVolume = aHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
8932 ReplaceElemInGroups(volume, NewVolume, meshDS);
8937 { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
8938 // aHelper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
8939 // aHelper.FixQuadraticElements(myError);
8940 SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
8944 //================================================================================
8946 * \brief Makes given elements quadratic
8947 * \param theForce3d - if true, the medium nodes will be placed in the middle of link
8948 * \param theElements - elements to make quadratic
8950 //================================================================================
8952 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d,
8953 TIDSortedElemSet& theElements,
8954 const bool theToBiQuad)
8956 if ( theElements.empty() ) return;
8958 // we believe that all theElements are of the same type
8959 const SMDSAbs_ElementType elemType = (*theElements.begin())->GetType();
8961 // get all nodes shared by theElements
8962 TIDSortedNodeSet allNodes;
8963 TIDSortedElemSet::iterator eIt = theElements.begin();
8964 for ( ; eIt != theElements.end(); ++eIt )
8965 allNodes.insert( (*eIt)->begin_nodes(), (*eIt)->end_nodes() );
8967 // complete theElements with elements of lower dim whose all nodes are in allNodes
8969 TIDSortedElemSet quadAdjacentElems [ SMDSAbs_NbElementTypes ]; // quadratic adjacent elements
8970 TIDSortedElemSet checkedAdjacentElems [ SMDSAbs_NbElementTypes ];
8971 TIDSortedNodeSet::iterator nIt = allNodes.begin();
8972 for ( ; nIt != allNodes.end(); ++nIt )
8974 const SMDS_MeshNode* n = *nIt;
8975 SMDS_ElemIteratorPtr invIt = n->GetInverseElementIterator();
8976 while ( invIt->more() )
8978 const SMDS_MeshElement* e = invIt->next();
8979 const SMDSAbs_ElementType type = e->GetType();
8980 if ( e->IsQuadratic() )
8982 quadAdjacentElems[ type ].insert( e );
8985 switch ( e->GetEntityType() ) {
8986 case SMDSEntity_Quad_Triangle:
8987 case SMDSEntity_Quad_Quadrangle:
8988 case SMDSEntity_Quad_Hexa: alreadyOK = !theToBiQuad; break;
8989 case SMDSEntity_BiQuad_Triangle:
8990 case SMDSEntity_BiQuad_Quadrangle:
8991 case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; break;
8992 default: alreadyOK = true;
8997 if ( type >= elemType )
8998 continue; // same type or more complex linear element
9000 if ( !checkedAdjacentElems[ type ].insert( e ).second )
9001 continue; // e is already checked
9005 SMDS_NodeIteratorPtr nodeIt = e->nodeIterator();
9006 while ( nodeIt->more() && allIn )
9007 allIn = allNodes.count( nodeIt->next() );
9009 theElements.insert(e );
9013 SMESH_MesherHelper helper(*myMesh);
9014 helper.SetIsQuadratic( true );
9015 helper.SetIsBiQuadratic( theToBiQuad );
9017 // add links of quadratic adjacent elements to the helper
9019 if ( !quadAdjacentElems[SMDSAbs_Edge].empty() )
9020 for ( eIt = quadAdjacentElems[SMDSAbs_Edge].begin();
9021 eIt != quadAdjacentElems[SMDSAbs_Edge].end(); ++eIt )
9023 helper.AddTLinks( static_cast< const SMDS_MeshEdge*> (*eIt) );
9025 if ( !quadAdjacentElems[SMDSAbs_Face].empty() )
9026 for ( eIt = quadAdjacentElems[SMDSAbs_Face].begin();
9027 eIt != quadAdjacentElems[SMDSAbs_Face].end(); ++eIt )
9029 helper.AddTLinks( static_cast< const SMDS_MeshFace*> (*eIt) );
9031 if ( !quadAdjacentElems[SMDSAbs_Volume].empty() )
9032 for ( eIt = quadAdjacentElems[SMDSAbs_Volume].begin();
9033 eIt != quadAdjacentElems[SMDSAbs_Volume].end(); ++eIt )
9035 helper.AddTLinks( static_cast< const SMDS_MeshVolume*> (*eIt) );
9038 // make quadratic (or bi-tri-quadratic) elements instead of linear ones
9040 SMESHDS_Mesh* meshDS = GetMeshDS();
9041 SMESHDS_SubMesh* smDS = 0;
9042 for ( eIt = theElements.begin(); eIt != theElements.end(); ++eIt )
9044 const SMDS_MeshElement* elem = *eIt;
9047 int nbCentralNodes = 0;
9048 switch ( elem->GetEntityType() ) {
9049 // linear convertible
9050 case SMDSEntity_Edge:
9051 case SMDSEntity_Triangle:
9052 case SMDSEntity_Quadrangle:
9053 case SMDSEntity_Tetra:
9054 case SMDSEntity_Pyramid:
9055 case SMDSEntity_Hexa:
9056 case SMDSEntity_Penta: alreadyOK = false; nbCentralNodes = 0; break;
9057 // quadratic that can become bi-quadratic
9058 case SMDSEntity_Quad_Triangle:
9059 case SMDSEntity_Quad_Quadrangle:
9060 case SMDSEntity_Quad_Hexa: alreadyOK =!theToBiQuad; nbCentralNodes = 0; break;
9062 case SMDSEntity_BiQuad_Triangle:
9063 case SMDSEntity_BiQuad_Quadrangle: alreadyOK = theToBiQuad; nbCentralNodes = 1; break;
9064 case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; nbCentralNodes = 7; break;
9066 default: alreadyOK = true;
9068 if ( alreadyOK ) continue;
9070 const SMDSAbs_ElementType type = elem->GetType();
9071 const int id = elem->GetID();
9072 const int nbNodes = elem->NbCornerNodes();
9073 vector<const SMDS_MeshNode *> nodes ( elem->begin_nodes(), elem->end_nodes());
9075 helper.SetSubShape( elem->getshapeId() );
9077 if ( !smDS || !smDS->Contains( elem ))
9078 smDS = meshDS->MeshElements( elem->getshapeId() );
9079 meshDS->RemoveFreeElement(elem, smDS, /*fromGroups=*/false);
9081 SMDS_MeshElement * newElem = 0;
9084 case 4: // cases for most frequently used element types go first (for optimization)
9085 if ( type == SMDSAbs_Volume )
9086 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9088 newElem = helper.AddFace (nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9091 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9092 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9095 newElem = helper.AddFace (nodes[0], nodes[1], nodes[2], id, theForce3d);
9098 newElem = helper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9101 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9102 nodes[4], id, theForce3d);
9105 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9106 nodes[4], nodes[5], id, theForce3d);
9110 ReplaceElemInGroups( elem, newElem, meshDS);
9111 if( newElem && smDS )
9112 smDS->AddElement( newElem );
9114 // remove central nodes
9115 for ( size_t i = nodes.size() - nbCentralNodes; i < nodes.size(); ++i )
9116 if ( nodes[i]->NbInverseElements() == 0 )
9117 meshDS->RemoveFreeNode( nodes[i], smDS, /*fromGroups=*/true );
9119 } // loop on theElements
9122 { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9123 // helper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9124 // helper.FixQuadraticElements( myError );
9125 SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
9129 //=======================================================================
9131 * \brief Convert quadratic elements to linear ones and remove quadratic nodes
9132 * \return int - nb of checked elements
9134 //=======================================================================
9136 int SMESH_MeshEditor::removeQuadElem(SMESHDS_SubMesh * theSm,
9137 SMDS_ElemIteratorPtr theItr,
9138 const int theShapeID)
9141 SMESHDS_Mesh* meshDS = GetMeshDS();
9143 while( theItr->more() )
9145 const SMDS_MeshElement* elem = theItr->next();
9147 if( elem && elem->IsQuadratic())
9149 int id = elem->GetID();
9150 int nbCornerNodes = elem->NbCornerNodes();
9151 SMDSAbs_ElementType aType = elem->GetType();
9153 vector<const SMDS_MeshNode *> nodes( elem->begin_nodes(), elem->end_nodes() );
9155 //remove a quadratic element
9156 if ( !theSm || !theSm->Contains( elem ))
9157 theSm = meshDS->MeshElements( elem->getshapeId() );
9158 meshDS->RemoveFreeElement( elem, theSm, /*fromGroups=*/false );
9160 // remove medium nodes
9161 for ( unsigned i = nbCornerNodes; i < nodes.size(); ++i )
9162 if ( nodes[i]->NbInverseElements() == 0 )
9163 meshDS->RemoveFreeNode( nodes[i], theSm );
9165 // add a linear element
9166 nodes.resize( nbCornerNodes );
9167 SMDS_MeshElement * newElem = AddElement( nodes, aType, false, id );
9168 ReplaceElemInGroups(elem, newElem, meshDS);
9169 if( theSm && newElem )
9170 theSm->AddElement( newElem );
9176 //=======================================================================
9177 //function : ConvertFromQuadratic
9179 //=======================================================================
9181 bool SMESH_MeshEditor::ConvertFromQuadratic()
9183 int nbCheckedElems = 0;
9184 if ( myMesh->HasShapeToMesh() )
9186 if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9188 SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9189 while ( smIt->more() ) {
9190 SMESH_subMesh* sm = smIt->next();
9191 if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() )
9192 nbCheckedElems += removeQuadElem( smDS, smDS->GetElements(), sm->GetId() );
9198 GetMeshDS()->NbEdges() + GetMeshDS()->NbFaces() + GetMeshDS()->NbVolumes();
9199 if ( nbCheckedElems < totalNbElems ) // not all elements are in submeshes
9201 SMESHDS_SubMesh *aSM = 0;
9202 removeQuadElem( aSM, GetMeshDS()->elementsIterator(), 0 );
9210 //================================================================================
9212 * \brief Return true if all medium nodes of the element are in the node set
9214 //================================================================================
9216 bool allMediumNodesIn(const SMDS_MeshElement* elem, TIDSortedNodeSet& nodeSet )
9218 for ( int i = elem->NbCornerNodes(); i < elem->NbNodes(); ++i )
9219 if ( !nodeSet.count( elem->GetNode(i) ))
9225 //================================================================================
9227 * \brief Makes given elements linear
9229 //================================================================================
9231 void SMESH_MeshEditor::ConvertFromQuadratic(TIDSortedElemSet& theElements)
9233 if ( theElements.empty() ) return;
9235 // collect IDs of medium nodes of theElements; some of these nodes will be removed
9236 set<int> mediumNodeIDs;
9237 TIDSortedElemSet::iterator eIt = theElements.begin();
9238 for ( ; eIt != theElements.end(); ++eIt )
9240 const SMDS_MeshElement* e = *eIt;
9241 for ( int i = e->NbCornerNodes(); i < e->NbNodes(); ++i )
9242 mediumNodeIDs.insert( e->GetNode(i)->GetID() );
9245 // replace given elements by linear ones
9246 SMDS_ElemIteratorPtr elemIt = elemSetIterator( theElements );
9247 removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9249 // we need to convert remaining elements whose all medium nodes are in mediumNodeIDs
9250 // except those elements sharing medium nodes of quadratic element whose medium nodes
9251 // are not all in mediumNodeIDs
9253 // get remaining medium nodes
9254 TIDSortedNodeSet mediumNodes;
9255 set<int>::iterator nIdsIt = mediumNodeIDs.begin();
9256 for ( ; nIdsIt != mediumNodeIDs.end(); ++nIdsIt )
9257 if ( const SMDS_MeshNode* n = GetMeshDS()->FindNode( *nIdsIt ))
9258 mediumNodes.insert( mediumNodes.end(), n );
9260 // find more quadratic elements to convert
9261 TIDSortedElemSet moreElemsToConvert;
9262 TIDSortedNodeSet::iterator nIt = mediumNodes.begin();
9263 for ( ; nIt != mediumNodes.end(); ++nIt )
9265 SMDS_ElemIteratorPtr invIt = (*nIt)->GetInverseElementIterator();
9266 while ( invIt->more() )
9268 const SMDS_MeshElement* e = invIt->next();
9269 if ( e->IsQuadratic() && allMediumNodesIn( e, mediumNodes ))
9271 // find a more complex element including e and
9272 // whose medium nodes are not in mediumNodes
9273 bool complexFound = false;
9274 for ( int type = e->GetType() + 1; type < SMDSAbs_0DElement; ++type )
9276 SMDS_ElemIteratorPtr invIt2 =
9277 (*nIt)->GetInverseElementIterator( SMDSAbs_ElementType( type ));
9278 while ( invIt2->more() )
9280 const SMDS_MeshElement* eComplex = invIt2->next();
9281 if ( eComplex->IsQuadratic() && !allMediumNodesIn( eComplex, mediumNodes))
9283 int nbCommonNodes = SMESH_MeshAlgos::GetCommonNodes( e, eComplex ).size();
9284 if ( nbCommonNodes == e->NbNodes())
9286 complexFound = true;
9287 type = SMDSAbs_NbElementTypes; // to quit from the outer loop
9293 if ( !complexFound )
9294 moreElemsToConvert.insert( e );
9298 elemIt = elemSetIterator( moreElemsToConvert );
9299 removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9302 //=======================================================================
9303 //function : SewSideElements
9305 //=======================================================================
9307 SMESH_MeshEditor::Sew_Error
9308 SMESH_MeshEditor::SewSideElements (TIDSortedElemSet& theSide1,
9309 TIDSortedElemSet& theSide2,
9310 const SMDS_MeshNode* theFirstNode1,
9311 const SMDS_MeshNode* theFirstNode2,
9312 const SMDS_MeshNode* theSecondNode1,
9313 const SMDS_MeshNode* theSecondNode2)
9315 myLastCreatedElems.Clear();
9316 myLastCreatedNodes.Clear();
9318 MESSAGE ("::::SewSideElements()");
9319 if ( theSide1.size() != theSide2.size() )
9320 return SEW_DIFF_NB_OF_ELEMENTS;
9322 Sew_Error aResult = SEW_OK;
9324 // 1. Build set of faces representing each side
9325 // 2. Find which nodes of the side 1 to merge with ones on the side 2
9326 // 3. Replace nodes in elements of the side 1 and remove replaced nodes
9328 // =======================================================================
9329 // 1. Build set of faces representing each side:
9330 // =======================================================================
9331 // a. build set of nodes belonging to faces
9332 // b. complete set of faces: find missing faces whose nodes are in set of nodes
9333 // c. create temporary faces representing side of volumes if correspondent
9334 // face does not exist
9336 SMESHDS_Mesh* aMesh = GetMeshDS();
9337 // TODO algoritm not OK with vtkUnstructuredGrid: 2 meshes can't share nodes
9338 //SMDS_Mesh aTmpFacesMesh; // try to use the same mesh
9339 TIDSortedElemSet faceSet1, faceSet2;
9340 set<const SMDS_MeshElement*> volSet1, volSet2;
9341 set<const SMDS_MeshNode*> nodeSet1, nodeSet2;
9342 TIDSortedElemSet * faceSetPtr[] = { &faceSet1, &faceSet2 };
9343 set<const SMDS_MeshElement*> * volSetPtr[] = { &volSet1, &volSet2 };
9344 set<const SMDS_MeshNode*> * nodeSetPtr[] = { &nodeSet1, &nodeSet2 };
9345 TIDSortedElemSet * elemSetPtr[] = { &theSide1, &theSide2 };
9346 int iSide, iFace, iNode;
9348 list<const SMDS_MeshElement* > tempFaceList;
9349 for ( iSide = 0; iSide < 2; iSide++ ) {
9350 set<const SMDS_MeshNode*> * nodeSet = nodeSetPtr[ iSide ];
9351 TIDSortedElemSet * elemSet = elemSetPtr[ iSide ];
9352 TIDSortedElemSet * faceSet = faceSetPtr[ iSide ];
9353 set<const SMDS_MeshElement*> * volSet = volSetPtr [ iSide ];
9354 set<const SMDS_MeshElement*>::iterator vIt;
9355 TIDSortedElemSet::iterator eIt;
9356 set<const SMDS_MeshNode*>::iterator nIt;
9358 // check that given nodes belong to given elements
9359 const SMDS_MeshNode* n1 = ( iSide == 0 ) ? theFirstNode1 : theFirstNode2;
9360 const SMDS_MeshNode* n2 = ( iSide == 0 ) ? theSecondNode1 : theSecondNode2;
9361 int firstIndex = -1, secondIndex = -1;
9362 for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9363 const SMDS_MeshElement* elem = *eIt;
9364 if ( firstIndex < 0 ) firstIndex = elem->GetNodeIndex( n1 );
9365 if ( secondIndex < 0 ) secondIndex = elem->GetNodeIndex( n2 );
9366 if ( firstIndex > -1 && secondIndex > -1 ) break;
9368 if ( firstIndex < 0 || secondIndex < 0 ) {
9369 // we can simply return until temporary faces created
9370 return (iSide == 0 ) ? SEW_BAD_SIDE1_NODES : SEW_BAD_SIDE2_NODES;
9373 // -----------------------------------------------------------
9374 // 1a. Collect nodes of existing faces
9375 // and build set of face nodes in order to detect missing
9376 // faces corresponding to sides of volumes
9377 // -----------------------------------------------------------
9379 set< set <const SMDS_MeshNode*> > setOfFaceNodeSet;
9381 // loop on the given element of a side
9382 for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9383 //const SMDS_MeshElement* elem = *eIt;
9384 const SMDS_MeshElement* elem = *eIt;
9385 if ( elem->GetType() == SMDSAbs_Face ) {
9386 faceSet->insert( elem );
9387 set <const SMDS_MeshNode*> faceNodeSet;
9388 SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
9389 while ( nodeIt->more() ) {
9390 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9391 nodeSet->insert( n );
9392 faceNodeSet.insert( n );
9394 setOfFaceNodeSet.insert( faceNodeSet );
9396 else if ( elem->GetType() == SMDSAbs_Volume )
9397 volSet->insert( elem );
9399 // ------------------------------------------------------------------------------
9400 // 1b. Complete set of faces: find missing faces whose nodes are in set of nodes
9401 // ------------------------------------------------------------------------------
9403 for ( nIt = nodeSet->begin(); nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
9404 SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
9405 while ( fIt->more() ) { // loop on faces sharing a node
9406 const SMDS_MeshElement* f = fIt->next();
9407 if ( faceSet->find( f ) == faceSet->end() ) {
9408 // check if all nodes are in nodeSet and
9409 // complete setOfFaceNodeSet if they are
9410 set <const SMDS_MeshNode*> faceNodeSet;
9411 SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
9412 bool allInSet = true;
9413 while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
9414 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9415 if ( nodeSet->find( n ) == nodeSet->end() )
9418 faceNodeSet.insert( n );
9421 faceSet->insert( f );
9422 setOfFaceNodeSet.insert( faceNodeSet );
9428 // -------------------------------------------------------------------------
9429 // 1c. Create temporary faces representing sides of volumes if correspondent
9430 // face does not exist
9431 // -------------------------------------------------------------------------
9433 if ( !volSet->empty() ) {
9434 //int nodeSetSize = nodeSet->size();
9436 // loop on given volumes
9437 for ( vIt = volSet->begin(); vIt != volSet->end(); vIt++ ) {
9438 SMDS_VolumeTool vol (*vIt);
9439 // loop on volume faces: find free faces
9440 // --------------------------------------
9441 list<const SMDS_MeshElement* > freeFaceList;
9442 for ( iFace = 0; iFace < vol.NbFaces(); iFace++ ) {
9443 if ( !vol.IsFreeFace( iFace ))
9445 // check if there is already a face with same nodes in a face set
9446 const SMDS_MeshElement* aFreeFace = 0;
9447 const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iFace );
9448 int nbNodes = vol.NbFaceNodes( iFace );
9449 set <const SMDS_MeshNode*> faceNodeSet;
9450 vol.GetFaceNodes( iFace, faceNodeSet );
9451 bool isNewFace = setOfFaceNodeSet.insert( faceNodeSet ).second;
9453 // no such a face is given but it still can exist, check it
9454 vector<const SMDS_MeshNode *> nodes ( fNodes, fNodes + nbNodes);
9455 aFreeFace = aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false );
9458 // create a temporary face
9459 if ( nbNodes == 3 ) {
9460 //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2] );
9461 aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2] );
9463 else if ( nbNodes == 4 ) {
9464 //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
9465 aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
9468 vector<const SMDS_MeshNode *> poly_nodes ( fNodes, & fNodes[nbNodes]);
9469 //aFreeFace = aTmpFacesMesh.AddPolygonalFace(poly_nodes);
9470 aFreeFace = aMesh->AddPolygonalFace(poly_nodes);
9473 tempFaceList.push_back( aFreeFace );
9477 freeFaceList.push_back( aFreeFace );
9479 } // loop on faces of a volume
9481 // choose one of several free faces of a volume
9482 // --------------------------------------------
9483 if ( freeFaceList.size() > 1 ) {
9484 // choose a face having max nb of nodes shared by other elems of a side
9485 int maxNbNodes = -1;
9486 list<const SMDS_MeshElement* >::iterator fIt = freeFaceList.begin();
9487 while ( fIt != freeFaceList.end() ) { // loop on free faces
9488 int nbSharedNodes = 0;
9489 SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
9490 while ( nodeIt->more() ) { // loop on free face nodes
9491 const SMDS_MeshNode* n =
9492 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9493 SMDS_ElemIteratorPtr invElemIt = n->GetInverseElementIterator();
9494 while ( invElemIt->more() ) {
9495 const SMDS_MeshElement* e = invElemIt->next();
9496 nbSharedNodes += faceSet->count( e );
9497 nbSharedNodes += elemSet->count( e );
9500 if ( nbSharedNodes > maxNbNodes ) {
9501 maxNbNodes = nbSharedNodes;
9502 freeFaceList.erase( freeFaceList.begin(), fIt++ );
9504 else if ( nbSharedNodes == maxNbNodes ) {
9508 freeFaceList.erase( fIt++ ); // here fIt++ occurs before erase
9511 if ( freeFaceList.size() > 1 )
9513 // could not choose one face, use another way
9514 // choose a face most close to the bary center of the opposite side
9515 gp_XYZ aBC( 0., 0., 0. );
9516 set <const SMDS_MeshNode*> addedNodes;
9517 TIDSortedElemSet * elemSet2 = elemSetPtr[ 1 - iSide ];
9518 eIt = elemSet2->begin();
9519 for ( eIt = elemSet2->begin(); eIt != elemSet2->end(); eIt++ ) {
9520 SMDS_ElemIteratorPtr nodeIt = (*eIt)->nodesIterator();
9521 while ( nodeIt->more() ) { // loop on free face nodes
9522 const SMDS_MeshNode* n =
9523 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9524 if ( addedNodes.insert( n ).second )
9525 aBC += gp_XYZ( n->X(),n->Y(),n->Z() );
9528 aBC /= addedNodes.size();
9529 double minDist = DBL_MAX;
9530 fIt = freeFaceList.begin();
9531 while ( fIt != freeFaceList.end() ) { // loop on free faces
9533 SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
9534 while ( nodeIt->more() ) { // loop on free face nodes
9535 const SMDS_MeshNode* n =
9536 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9537 gp_XYZ p( n->X(),n->Y(),n->Z() );
9538 dist += ( aBC - p ).SquareModulus();
9540 if ( dist < minDist ) {
9542 freeFaceList.erase( freeFaceList.begin(), fIt++ );
9545 fIt = freeFaceList.erase( fIt++ );
9548 } // choose one of several free faces of a volume
9550 if ( freeFaceList.size() == 1 ) {
9551 const SMDS_MeshElement* aFreeFace = freeFaceList.front();
9552 faceSet->insert( aFreeFace );
9553 // complete a node set with nodes of a found free face
9554 // for ( iNode = 0; iNode < ; iNode++ )
9555 // nodeSet->insert( fNodes[ iNode ] );
9558 } // loop on volumes of a side
9560 // // complete a set of faces if new nodes in a nodeSet appeared
9561 // // ----------------------------------------------------------
9562 // if ( nodeSetSize != nodeSet->size() ) {
9563 // for ( ; nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
9564 // SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
9565 // while ( fIt->more() ) { // loop on faces sharing a node
9566 // const SMDS_MeshElement* f = fIt->next();
9567 // if ( faceSet->find( f ) == faceSet->end() ) {
9568 // // check if all nodes are in nodeSet and
9569 // // complete setOfFaceNodeSet if they are
9570 // set <const SMDS_MeshNode*> faceNodeSet;
9571 // SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
9572 // bool allInSet = true;
9573 // while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
9574 // const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9575 // if ( nodeSet->find( n ) == nodeSet->end() )
9576 // allInSet = false;
9578 // faceNodeSet.insert( n );
9580 // if ( allInSet ) {
9581 // faceSet->insert( f );
9582 // setOfFaceNodeSet.insert( faceNodeSet );
9588 } // Create temporary faces, if there are volumes given
9591 if ( faceSet1.size() != faceSet2.size() ) {
9592 // delete temporary faces: they are in reverseElements of actual nodes
9593 // SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
9594 // while ( tmpFaceIt->more() )
9595 // aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
9596 // list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
9597 // for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
9598 // aMesh->RemoveElement(*tmpFaceIt);
9599 MESSAGE("Diff nb of faces");
9600 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9603 // ============================================================
9604 // 2. Find nodes to merge:
9605 // bind a node to remove to a node to put instead
9606 // ============================================================
9608 TNodeNodeMap nReplaceMap; // bind a node to remove to a node to put instead
9609 if ( theFirstNode1 != theFirstNode2 )
9610 nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
9611 if ( theSecondNode1 != theSecondNode2 )
9612 nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
9614 LinkID_Gen aLinkID_Gen( GetMeshDS() );
9615 set< long > linkIdSet; // links to process
9616 linkIdSet.insert( aLinkID_Gen.GetLinkID( theFirstNode1, theSecondNode1 ));
9618 typedef pair< const SMDS_MeshNode*, const SMDS_MeshNode* > NLink;
9619 list< NLink > linkList[2];
9620 linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
9621 linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
9622 // loop on links in linkList; find faces by links and append links
9623 // of the found faces to linkList
9624 list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
9625 for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ )
9627 NLink link[] = { *linkIt[0], *linkIt[1] };
9628 long linkID = aLinkID_Gen.GetLinkID( link[0].first, link[0].second );
9629 if ( !linkIdSet.count( linkID ) )
9632 // by links, find faces in the face sets,
9633 // and find indices of link nodes in the found faces;
9634 // in a face set, there is only one or no face sharing a link
9635 // ---------------------------------------------------------------
9637 const SMDS_MeshElement* face[] = { 0, 0 };
9638 vector<const SMDS_MeshNode*> fnodes[2];
9639 int iLinkNode[2][2];
9640 TIDSortedElemSet avoidSet;
9641 for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
9642 const SMDS_MeshNode* n1 = link[iSide].first;
9643 const SMDS_MeshNode* n2 = link[iSide].second;
9644 //cout << "Side " << iSide << " ";
9645 //cout << "L( " << n1->GetID() << ", " << n2->GetID() << " ) " << endl;
9646 // find a face by two link nodes
9647 face[ iSide ] = SMESH_MeshAlgos::FindFaceInSet( n1, n2,
9648 *faceSetPtr[ iSide ], avoidSet,
9649 &iLinkNode[iSide][0],
9650 &iLinkNode[iSide][1] );
9653 //cout << " F " << face[ iSide]->GetID() <<endl;
9654 faceSetPtr[ iSide ]->erase( face[ iSide ]);
9655 // put face nodes to fnodes
9656 if ( face[ iSide ]->IsQuadratic() )
9658 // use interlaced nodes iterator
9659 const SMDS_VtkFace* F = dynamic_cast<const SMDS_VtkFace*>( face[ iSide ]);
9660 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
9661 SMDS_ElemIteratorPtr nIter = F->interlacedNodesElemIterator();
9662 while ( nIter->more() )
9663 fnodes[ iSide ].push_back( cast2Node( nIter->next() ));
9667 fnodes[ iSide ].assign( face[ iSide ]->begin_nodes(),
9668 face[ iSide ]->end_nodes() );
9670 fnodes[ iSide ].push_back( fnodes[ iSide ].front());
9674 // check similarity of elements of the sides
9675 if (aResult == SEW_OK && (( face[0] && !face[1] ) || ( !face[0] && face[1] ))) {
9676 MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
9677 if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
9678 aResult = ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
9681 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9683 break; // do not return because it's necessary to remove tmp faces
9686 // set nodes to merge
9687 // -------------------
9689 if ( face[0] && face[1] ) {
9690 const int nbNodes = face[0]->NbNodes();
9691 if ( nbNodes != face[1]->NbNodes() ) {
9692 MESSAGE("Diff nb of face nodes");
9693 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9694 break; // do not return because it s necessary to remove tmp faces
9696 bool reverse[] = { false, false }; // order of nodes in the link
9697 for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
9698 // analyse link orientation in faces
9699 int i1 = iLinkNode[ iSide ][ 0 ];
9700 int i2 = iLinkNode[ iSide ][ 1 ];
9701 reverse[ iSide ] = Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1;
9703 int di1 = reverse[0] ? -1 : +1, i1 = iLinkNode[0][1] + di1;
9704 int di2 = reverse[1] ? -1 : +1, i2 = iLinkNode[1][1] + di2;
9705 for ( int i = nbNodes - 2; i > 0; --i, i1 += di1, i2 += di2 )
9707 nReplaceMap.insert ( make_pair ( fnodes[0][ ( i1 + nbNodes ) % nbNodes ],
9708 fnodes[1][ ( i2 + nbNodes ) % nbNodes ]));
9711 // add other links of the faces to linkList
9712 // -----------------------------------------
9714 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
9715 linkID = aLinkID_Gen.GetLinkID( fnodes[0][iNode], fnodes[0][iNode+1] );
9716 pair< set<long>::iterator, bool > iter_isnew = linkIdSet.insert( linkID );
9717 if ( !iter_isnew.second ) { // already in a set: no need to process
9718 linkIdSet.erase( iter_isnew.first );
9720 else // new in set == encountered for the first time: add
9722 const SMDS_MeshNode* n1 = fnodes[0][ iNode ];
9723 const SMDS_MeshNode* n2 = fnodes[0][ iNode + 1];
9724 linkList[0].push_back ( NLink( n1, n2 ));
9725 linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
9730 if ( faceSetPtr[0]->empty() || faceSetPtr[1]->empty() )
9733 } // loop on link lists
9735 if ( aResult == SEW_OK &&
9736 ( //linkIt[0] != linkList[0].end() ||
9737 !faceSetPtr[0]->empty() || !faceSetPtr[1]->empty() )) {
9738 MESSAGE( (linkIt[0] != linkList[0].end()) <<" "<< (faceSetPtr[0]->empty()) <<
9739 " " << (faceSetPtr[1]->empty()));
9740 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9743 // ====================================================================
9744 // 3. Replace nodes in elements of the side 1 and remove replaced nodes
9745 // ====================================================================
9747 // delete temporary faces
9748 // SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
9749 // while ( tmpFaceIt->more() )
9750 // aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
9751 list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
9752 for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
9753 aMesh->RemoveElement(*tmpFaceIt);
9755 if ( aResult != SEW_OK)
9758 list< int > nodeIDsToRemove/*, elemIDsToRemove*/;
9759 // loop on nodes replacement map
9760 TNodeNodeMap::iterator nReplaceMapIt = nReplaceMap.begin(), nnIt;
9761 for ( ; nReplaceMapIt != nReplaceMap.end(); nReplaceMapIt++ )
9762 if ( (*nReplaceMapIt).first != (*nReplaceMapIt).second ) {
9763 const SMDS_MeshNode* nToRemove = (*nReplaceMapIt).first;
9764 nodeIDsToRemove.push_back( nToRemove->GetID() );
9765 // loop on elements sharing nToRemove
9766 SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
9767 while ( invElemIt->more() ) {
9768 const SMDS_MeshElement* e = invElemIt->next();
9769 // get a new suite of nodes: make replacement
9770 int nbReplaced = 0, i = 0, nbNodes = e->NbNodes();
9771 vector< const SMDS_MeshNode*> nodes( nbNodes );
9772 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
9773 while ( nIt->more() ) {
9774 const SMDS_MeshNode* n =
9775 static_cast<const SMDS_MeshNode*>( nIt->next() );
9776 nnIt = nReplaceMap.find( n );
9777 if ( nnIt != nReplaceMap.end() ) {
9783 // if ( nbReplaced == nbNodes && e->GetType() == SMDSAbs_Face )
9784 // elemIDsToRemove.push_back( e->GetID() );
9788 SMDSAbs_ElementType etyp = e->GetType();
9789 SMDS_MeshElement* newElem = this->AddElement(nodes, etyp, false);
9792 myLastCreatedElems.Append(newElem);
9793 AddToSameGroups(newElem, e, aMesh);
9794 int aShapeId = e->getshapeId();
9797 aMesh->SetMeshElementOnShape( newElem, aShapeId );
9800 aMesh->RemoveElement(e);
9805 Remove( nodeIDsToRemove, true );
9810 //================================================================================
9812 * \brief Find corresponding nodes in two sets of faces
9813 * \param theSide1 - first face set
9814 * \param theSide2 - second first face
9815 * \param theFirstNode1 - a boundary node of set 1
9816 * \param theFirstNode2 - a node of set 2 corresponding to theFirstNode1
9817 * \param theSecondNode1 - a boundary node of set 1 linked with theFirstNode1
9818 * \param theSecondNode2 - a node of set 2 corresponding to theSecondNode1
9819 * \param nReplaceMap - output map of corresponding nodes
9820 * \return bool - is a success or not
9822 //================================================================================
9825 //#define DEBUG_MATCHING_NODES
9828 SMESH_MeshEditor::Sew_Error
9829 SMESH_MeshEditor::FindMatchingNodes(set<const SMDS_MeshElement*>& theSide1,
9830 set<const SMDS_MeshElement*>& theSide2,
9831 const SMDS_MeshNode* theFirstNode1,
9832 const SMDS_MeshNode* theFirstNode2,
9833 const SMDS_MeshNode* theSecondNode1,
9834 const SMDS_MeshNode* theSecondNode2,
9835 TNodeNodeMap & nReplaceMap)
9837 set<const SMDS_MeshElement*> * faceSetPtr[] = { &theSide1, &theSide2 };
9839 nReplaceMap.clear();
9840 if ( theFirstNode1 != theFirstNode2 )
9841 nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
9842 if ( theSecondNode1 != theSecondNode2 )
9843 nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
9845 set< SMESH_TLink > linkSet; // set of nodes where order of nodes is ignored
9846 linkSet.insert( SMESH_TLink( theFirstNode1, theSecondNode1 ));
9848 list< NLink > linkList[2];
9849 linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
9850 linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
9852 // loop on links in linkList; find faces by links and append links
9853 // of the found faces to linkList
9854 list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
9855 for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ ) {
9856 NLink link[] = { *linkIt[0], *linkIt[1] };
9857 if ( linkSet.find( link[0] ) == linkSet.end() )
9860 // by links, find faces in the face sets,
9861 // and find indices of link nodes in the found faces;
9862 // in a face set, there is only one or no face sharing a link
9863 // ---------------------------------------------------------------
9865 const SMDS_MeshElement* face[] = { 0, 0 };
9866 list<const SMDS_MeshNode*> notLinkNodes[2];
9867 //bool reverse[] = { false, false }; // order of notLinkNodes
9869 for ( int iSide = 0; iSide < 2; iSide++ ) // loop on 2 sides
9871 const SMDS_MeshNode* n1 = link[iSide].first;
9872 const SMDS_MeshNode* n2 = link[iSide].second;
9873 set<const SMDS_MeshElement*> * faceSet = faceSetPtr[ iSide ];
9874 set< const SMDS_MeshElement* > facesOfNode1;
9875 for ( int iNode = 0; iNode < 2; iNode++ ) // loop on 2 nodes of a link
9877 // during a loop of the first node, we find all faces around n1,
9878 // during a loop of the second node, we find one face sharing both n1 and n2
9879 const SMDS_MeshNode* n = iNode ? n1 : n2; // a node of a link
9880 SMDS_ElemIteratorPtr fIt = n->GetInverseElementIterator(SMDSAbs_Face);
9881 while ( fIt->more() ) { // loop on faces sharing a node
9882 const SMDS_MeshElement* f = fIt->next();
9883 if (faceSet->find( f ) != faceSet->end() && // f is in face set
9884 ! facesOfNode1.insert( f ).second ) // f encounters twice
9886 if ( face[ iSide ] ) {
9887 MESSAGE( "2 faces per link " );
9888 return ( iSide ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
9891 faceSet->erase( f );
9893 // get not link nodes
9894 int nbN = f->NbNodes();
9895 if ( f->IsQuadratic() )
9897 nbNodes[ iSide ] = nbN;
9898 list< const SMDS_MeshNode* > & nodes = notLinkNodes[ iSide ];
9899 int i1 = f->GetNodeIndex( n1 );
9900 int i2 = f->GetNodeIndex( n2 );
9901 int iEnd = nbN, iBeg = -1, iDelta = 1;
9902 bool reverse = ( Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1 );
9904 std::swap( iEnd, iBeg ); iDelta = -1;
9909 if ( i == iEnd ) i = iBeg + iDelta;
9910 if ( i == i1 ) break;
9911 nodes.push_back ( f->GetNode( i ) );
9917 // check similarity of elements of the sides
9918 if (( face[0] && !face[1] ) || ( !face[0] && face[1] )) {
9919 MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
9920 if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
9921 return ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
9924 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9928 // set nodes to merge
9929 // -------------------
9931 if ( face[0] && face[1] ) {
9932 if ( nbNodes[0] != nbNodes[1] ) {
9933 MESSAGE("Diff nb of face nodes");
9934 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9936 #ifdef DEBUG_MATCHING_NODES
9937 MESSAGE ( " Link 1: " << link[0].first->GetID() <<" "<< link[0].second->GetID()
9938 << " F 1: " << face[0] << "| Link 2: " << link[1].first->GetID() <<" "
9939 << link[1].second->GetID() << " F 2: " << face[1] << " | Bind: " ) ;
9941 int nbN = nbNodes[0];
9943 list<const SMDS_MeshNode*>::iterator n1 = notLinkNodes[0].begin();
9944 list<const SMDS_MeshNode*>::iterator n2 = notLinkNodes[1].begin();
9945 for ( int i = 0 ; i < nbN - 2; ++i ) {
9946 #ifdef DEBUG_MATCHING_NODES
9947 MESSAGE ( (*n1)->GetID() << " to " << (*n2)->GetID() );
9949 nReplaceMap.insert( make_pair( *(n1++), *(n2++) ));
9953 // add other links of the face 1 to linkList
9954 // -----------------------------------------
9956 const SMDS_MeshElement* f0 = face[0];
9957 const SMDS_MeshNode* n1 = f0->GetNode( nbN - 1 );
9958 for ( int i = 0; i < nbN; i++ )
9960 const SMDS_MeshNode* n2 = f0->GetNode( i );
9961 pair< set< SMESH_TLink >::iterator, bool > iter_isnew =
9962 linkSet.insert( SMESH_TLink( n1, n2 ));
9963 if ( !iter_isnew.second ) { // already in a set: no need to process
9964 linkSet.erase( iter_isnew.first );
9966 else // new in set == encountered for the first time: add
9968 #ifdef DEBUG_MATCHING_NODES
9969 MESSAGE ( "Add link 1: " << n1->GetID() << " " << n2->GetID() << " "
9970 << " | link 2: " << nReplaceMap[n1]->GetID() << " " << nReplaceMap[n2]->GetID() << " " );
9972 linkList[0].push_back ( NLink( n1, n2 ));
9973 linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
9978 } // loop on link lists
9983 //================================================================================
9985 * \brief Create elements equal (on same nodes) to given ones
9986 * \param [in] theElements - a set of elems to duplicate. If it is empty, all
9987 * elements of the uppest dimension are duplicated.
9989 //================================================================================
9991 void SMESH_MeshEditor::DoubleElements( const TIDSortedElemSet& theElements )
9994 SMESHDS_Mesh* mesh = GetMeshDS();
9996 // get an element type and an iterator over elements
9998 SMDSAbs_ElementType type;
9999 SMDS_ElemIteratorPtr elemIt;
10000 vector< const SMDS_MeshElement* > allElems;
10001 if ( theElements.empty() )
10003 if ( mesh->NbNodes() == 0 )
10005 // get most complex type
10006 SMDSAbs_ElementType types[SMDSAbs_NbElementTypes] = {
10007 SMDSAbs_Volume, SMDSAbs_Face, SMDSAbs_Edge,
10008 SMDSAbs_0DElement, SMDSAbs_Ball, SMDSAbs_Node
10010 for ( int i = 0; i < SMDSAbs_NbElementTypes; ++i )
10011 if ( mesh->GetMeshInfo().NbElements( types[i] ))
10016 // put all elements in the vector <allElems>
10017 allElems.reserve( mesh->GetMeshInfo().NbElements( type ));
10018 elemIt = mesh->elementsIterator( type );
10019 while ( elemIt->more() )
10020 allElems.push_back( elemIt->next());
10021 elemIt = elemSetIterator( allElems );
10025 type = (*theElements.begin())->GetType();
10026 elemIt = elemSetIterator( theElements );
10029 // duplicate elements
10031 if ( type == SMDSAbs_Ball )
10033 SMDS_UnstructuredGrid* vtkGrid = mesh->getGrid();
10034 while ( elemIt->more() )
10036 const SMDS_MeshElement* elem = elemIt->next();
10037 if ( elem->GetType() != SMDSAbs_Ball )
10039 if (( elem = mesh->AddBall( elem->GetNode(0),
10040 vtkGrid->GetBallDiameter( elem->getVtkId() ))))
10041 myLastCreatedElems.Append( elem );
10046 vector< const SMDS_MeshNode* > nodes;
10047 while ( elemIt->more() )
10049 const SMDS_MeshElement* elem = elemIt->next();
10050 if ( elem->GetType() != type )
10053 nodes.assign( elem->begin_nodes(), elem->end_nodes() );
10055 if ( type == SMDSAbs_Volume && elem->GetVtkType() == VTK_POLYHEDRON )
10057 std::vector<int> quantities =
10058 static_cast< const SMDS_VtkVolume* >( elem )->GetQuantities();
10059 elem = mesh->AddPolyhedralVolume( nodes, quantities );
10063 AddElement( nodes, type, elem->IsPoly() );
10064 elem = 0; // myLastCreatedElems is already filled
10067 myLastCreatedElems.Append( elem );
10072 //================================================================================
10074 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10075 \param theElems - the list of elements (edges or faces) to be replicated
10076 The nodes for duplication could be found from these elements
10077 \param theNodesNot - list of nodes to NOT replicate
10078 \param theAffectedElems - the list of elements (cells and edges) to which the
10079 replicated nodes should be associated to.
10080 \return TRUE if operation has been completed successfully, FALSE otherwise
10082 //================================================================================
10084 bool SMESH_MeshEditor::DoubleNodes( const TIDSortedElemSet& theElems,
10085 const TIDSortedElemSet& theNodesNot,
10086 const TIDSortedElemSet& theAffectedElems )
10088 myLastCreatedElems.Clear();
10089 myLastCreatedNodes.Clear();
10091 if ( theElems.size() == 0 )
10094 SMESHDS_Mesh* aMeshDS = GetMeshDS();
10099 std::map< const SMDS_MeshNode*, const SMDS_MeshNode* > anOldNodeToNewNode;
10100 // duplicate elements and nodes
10101 res = doubleNodes( aMeshDS, theElems, theNodesNot, anOldNodeToNewNode, true );
10102 // replce nodes by duplications
10103 res = doubleNodes( aMeshDS, theAffectedElems, theNodesNot, anOldNodeToNewNode, false );
10107 //================================================================================
10109 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10110 \param theMeshDS - mesh instance
10111 \param theElems - the elements replicated or modified (nodes should be changed)
10112 \param theNodesNot - nodes to NOT replicate
10113 \param theNodeNodeMap - relation of old node to new created node
10114 \param theIsDoubleElem - flag os to replicate element or modify
10115 \return TRUE if operation has been completed successfully, FALSE otherwise
10117 //================================================================================
10119 bool SMESH_MeshEditor::doubleNodes( SMESHDS_Mesh* theMeshDS,
10120 const TIDSortedElemSet& theElems,
10121 const TIDSortedElemSet& theNodesNot,
10122 std::map< const SMDS_MeshNode*,
10123 const SMDS_MeshNode* >& theNodeNodeMap,
10124 const bool theIsDoubleElem )
10126 MESSAGE("doubleNodes");
10127 // iterate on through element and duplicate them (by nodes duplication)
10129 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10130 for ( ; elemItr != theElems.end(); ++elemItr )
10132 const SMDS_MeshElement* anElem = *elemItr;
10136 bool isDuplicate = false;
10137 // duplicate nodes to duplicate element
10138 std::vector<const SMDS_MeshNode*> newNodes( anElem->NbNodes() );
10139 SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
10141 while ( anIter->more() )
10144 SMDS_MeshNode* aCurrNode = (SMDS_MeshNode*)anIter->next();
10145 SMDS_MeshNode* aNewNode = aCurrNode;
10146 if ( theNodeNodeMap.find( aCurrNode ) != theNodeNodeMap.end() )
10147 aNewNode = (SMDS_MeshNode*)theNodeNodeMap[ aCurrNode ];
10148 else if ( theIsDoubleElem && theNodesNot.find( aCurrNode ) == theNodesNot.end() )
10151 aNewNode = theMeshDS->AddNode( aCurrNode->X(), aCurrNode->Y(), aCurrNode->Z() );
10152 theNodeNodeMap[ aCurrNode ] = aNewNode;
10153 myLastCreatedNodes.Append( aNewNode );
10155 isDuplicate |= (aCurrNode != aNewNode);
10156 newNodes[ ind++ ] = aNewNode;
10158 if ( !isDuplicate )
10161 if ( theIsDoubleElem )
10162 AddElement(newNodes, anElem->GetType(), anElem->IsPoly());
10165 MESSAGE("ChangeElementNodes");
10166 theMeshDS->ChangeElementNodes( anElem, &newNodes[ 0 ], anElem->NbNodes() );
10173 //================================================================================
10175 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10176 \param theNodes - identifiers of nodes to be doubled
10177 \param theModifiedElems - identifiers of elements to be updated by the new (doubled)
10178 nodes. If list of element identifiers is empty then nodes are doubled but
10179 they not assigned to elements
10180 \return TRUE if operation has been completed successfully, FALSE otherwise
10182 //================================================================================
10184 bool SMESH_MeshEditor::DoubleNodes( const std::list< int >& theListOfNodes,
10185 const std::list< int >& theListOfModifiedElems )
10187 MESSAGE("DoubleNodes");
10188 myLastCreatedElems.Clear();
10189 myLastCreatedNodes.Clear();
10191 if ( theListOfNodes.size() == 0 )
10194 SMESHDS_Mesh* aMeshDS = GetMeshDS();
10198 // iterate through nodes and duplicate them
10200 std::map< const SMDS_MeshNode*, const SMDS_MeshNode* > anOldNodeToNewNode;
10202 std::list< int >::const_iterator aNodeIter;
10203 for ( aNodeIter = theListOfNodes.begin(); aNodeIter != theListOfNodes.end(); ++aNodeIter )
10205 int aCurr = *aNodeIter;
10206 SMDS_MeshNode* aNode = (SMDS_MeshNode*)aMeshDS->FindNode( aCurr );
10212 const SMDS_MeshNode* aNewNode = aMeshDS->AddNode( aNode->X(), aNode->Y(), aNode->Z() );
10215 anOldNodeToNewNode[ aNode ] = aNewNode;
10216 myLastCreatedNodes.Append( aNewNode );
10220 // Create map of new nodes for modified elements
10222 std::map< SMDS_MeshElement*, vector<const SMDS_MeshNode*> > anElemToNodes;
10224 std::list< int >::const_iterator anElemIter;
10225 for ( anElemIter = theListOfModifiedElems.begin();
10226 anElemIter != theListOfModifiedElems.end(); ++anElemIter )
10228 int aCurr = *anElemIter;
10229 SMDS_MeshElement* anElem = (SMDS_MeshElement*)aMeshDS->FindElement( aCurr );
10233 vector<const SMDS_MeshNode*> aNodeArr( anElem->NbNodes() );
10235 SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
10237 while ( anIter->more() )
10239 SMDS_MeshNode* aCurrNode = (SMDS_MeshNode*)anIter->next();
10240 if ( aCurr && anOldNodeToNewNode.find( aCurrNode ) != anOldNodeToNewNode.end() )
10242 const SMDS_MeshNode* aNewNode = anOldNodeToNewNode[ aCurrNode ];
10243 aNodeArr[ ind++ ] = aNewNode;
10246 aNodeArr[ ind++ ] = aCurrNode;
10248 anElemToNodes[ anElem ] = aNodeArr;
10251 // Change nodes of elements
10253 std::map< SMDS_MeshElement*, vector<const SMDS_MeshNode*> >::iterator
10254 anElemToNodesIter = anElemToNodes.begin();
10255 for ( ; anElemToNodesIter != anElemToNodes.end(); ++anElemToNodesIter )
10257 const SMDS_MeshElement* anElem = anElemToNodesIter->first;
10258 vector<const SMDS_MeshNode*> aNodeArr = anElemToNodesIter->second;
10261 MESSAGE("ChangeElementNodes");
10262 aMeshDS->ChangeElementNodes( anElem, &aNodeArr[ 0 ], anElem->NbNodes() );
10271 //================================================================================
10273 \brief Check if element located inside shape
10274 \return TRUE if IN or ON shape, FALSE otherwise
10276 //================================================================================
10278 template<class Classifier>
10279 bool isInside(const SMDS_MeshElement* theElem,
10280 Classifier& theClassifier,
10281 const double theTol)
10283 gp_XYZ centerXYZ (0, 0, 0);
10284 SMDS_ElemIteratorPtr aNodeItr = theElem->nodesIterator();
10285 while (aNodeItr->more())
10286 centerXYZ += SMESH_TNodeXYZ(cast2Node( aNodeItr->next()));
10288 gp_Pnt aPnt = centerXYZ / theElem->NbNodes();
10289 theClassifier.Perform(aPnt, theTol);
10290 TopAbs_State aState = theClassifier.State();
10291 return (aState == TopAbs_IN || aState == TopAbs_ON );
10294 //================================================================================
10296 * \brief Classifier of the 3D point on the TopoDS_Face
10297 * with interaface suitable for isInside()
10299 //================================================================================
10301 struct _FaceClassifier
10303 Extrema_ExtPS _extremum;
10304 BRepAdaptor_Surface _surface;
10305 TopAbs_State _state;
10307 _FaceClassifier(const TopoDS_Face& face):_extremum(),_surface(face),_state(TopAbs_OUT)
10309 _extremum.Initialize( _surface,
10310 _surface.FirstUParameter(), _surface.LastUParameter(),
10311 _surface.FirstVParameter(), _surface.LastVParameter(),
10312 _surface.Tolerance(), _surface.Tolerance() );
10314 void Perform(const gp_Pnt& aPnt, double theTol)
10316 _state = TopAbs_OUT;
10317 _extremum.Perform(aPnt);
10318 if ( _extremum.IsDone() )
10319 for ( int iSol = 1; iSol <= _extremum.NbExt() && _state == TopAbs_OUT; ++iSol)
10320 #if OCC_VERSION_LARGE > 0x06040000 // Porting to OCCT6.5.1
10321 _state = ( _extremum.SquareDistance(iSol) <= theTol ? TopAbs_IN : TopAbs_OUT );
10323 _state = ( _extremum.Value(iSol) <= theTol ? TopAbs_IN : TopAbs_OUT );
10326 TopAbs_State State() const
10333 //================================================================================
10335 \brief Identify the elements that will be affected by node duplication (actual duplication is not performed).
10336 This method is the first step of DoubleNodeElemGroupsInRegion.
10337 \param theElems - list of groups of elements (edges or faces) to be replicated
10338 \param theNodesNot - list of groups of nodes not to replicated
10339 \param theShape - shape to detect affected elements (element which geometric center
10340 located on or inside shape). If the shape is null, detection is done on faces orientations
10341 (select elements with a gravity center on the side given by faces normals).
10342 This mode (null shape) is faster, but works only when theElems are faces, with coherents orientations.
10343 The replicated nodes should be associated to affected elements.
10344 \return groups of affected elements
10345 \sa DoubleNodeElemGroupsInRegion()
10347 //================================================================================
10349 bool SMESH_MeshEditor::AffectedElemGroupsInRegion( const TIDSortedElemSet& theElems,
10350 const TIDSortedElemSet& theNodesNot,
10351 const TopoDS_Shape& theShape,
10352 TIDSortedElemSet& theAffectedElems)
10354 if ( theShape.IsNull() )
10356 std::set<const SMDS_MeshNode*> alreadyCheckedNodes;
10357 std::set<const SMDS_MeshElement*> alreadyCheckedElems;
10358 std::set<const SMDS_MeshElement*> edgesToCheck;
10359 alreadyCheckedNodes.clear();
10360 alreadyCheckedElems.clear();
10361 edgesToCheck.clear();
10363 // --- iterates on elements to be replicated and get elements by back references from their nodes
10365 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10367 for ( ielem=1; elemItr != theElems.end(); ++elemItr )
10369 SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
10370 if (!anElem || (anElem->GetType() != SMDSAbs_Face))
10373 SMESH_MeshAlgos::FaceNormal( anElem, normal, /*normalized=*/true );
10374 MESSAGE("element " << ielem++ << " normal " << normal.X() << " " << normal.Y() << " " << normal.Z());
10375 std::set<const SMDS_MeshNode*> nodesElem;
10377 SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
10378 while ( nodeItr->more() )
10380 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10381 nodesElem.insert(aNode);
10383 std::set<const SMDS_MeshNode*>::iterator nodit = nodesElem.begin();
10384 for (; nodit != nodesElem.end(); nodit++)
10386 MESSAGE(" noeud ");
10387 const SMDS_MeshNode* aNode = *nodit;
10388 if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
10390 if (alreadyCheckedNodes.find(aNode) != alreadyCheckedNodes.end())
10392 alreadyCheckedNodes.insert(aNode);
10393 SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
10394 while ( backElemItr->more() )
10396 MESSAGE(" backelem ");
10397 const SMDS_MeshElement* curElem = backElemItr->next();
10398 if (alreadyCheckedElems.find(curElem) != alreadyCheckedElems.end())
10400 if (theElems.find(curElem) != theElems.end())
10402 alreadyCheckedElems.insert(curElem);
10403 double x=0, y=0, z=0;
10405 SMDS_ElemIteratorPtr nodeItr2 = curElem->nodesIterator();
10406 while ( nodeItr2->more() )
10408 const SMDS_MeshNode* anotherNode = cast2Node(nodeItr2->next());
10409 x += anotherNode->X();
10410 y += anotherNode->Y();
10411 z += anotherNode->Z();
10415 p.SetCoord( x/nb -aNode->X(),
10417 z/nb -aNode->Z() );
10418 MESSAGE(" check " << p.X() << " " << p.Y() << " " << p.Z());
10421 MESSAGE(" --- inserted")
10422 theAffectedElems.insert( curElem );
10424 else if (curElem->GetType() == SMDSAbs_Edge)
10425 edgesToCheck.insert(curElem);
10429 // --- add also edges lying on the set of faces (all nodes in alreadyCheckedNodes)
10430 std::set<const SMDS_MeshElement*>::iterator eit = edgesToCheck.begin();
10431 for( ; eit != edgesToCheck.end(); eit++)
10433 bool onside = true;
10434 const SMDS_MeshElement* anEdge = *eit;
10435 SMDS_ElemIteratorPtr nodeItr = anEdge->nodesIterator();
10436 while ( nodeItr->more() )
10438 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10439 if (alreadyCheckedNodes.find(aNode) == alreadyCheckedNodes.end())
10447 MESSAGE(" --- edge onside inserted")
10448 theAffectedElems.insert(anEdge);
10454 const double aTol = Precision::Confusion();
10455 auto_ptr< BRepClass3d_SolidClassifier> bsc3d;
10456 auto_ptr<_FaceClassifier> aFaceClassifier;
10457 if ( theShape.ShapeType() == TopAbs_SOLID )
10459 bsc3d.reset( new BRepClass3d_SolidClassifier(theShape));;
10460 bsc3d->PerformInfinitePoint(aTol);
10462 else if (theShape.ShapeType() == TopAbs_FACE )
10464 aFaceClassifier.reset( new _FaceClassifier(TopoDS::Face(theShape)));
10467 // iterates on indicated elements and get elements by back references from their nodes
10468 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10470 for ( ielem = 1; elemItr != theElems.end(); ++elemItr )
10472 MESSAGE("element " << ielem++);
10473 SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
10476 SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
10477 while ( nodeItr->more() )
10479 MESSAGE(" noeud ");
10480 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10481 if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
10483 SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
10484 while ( backElemItr->more() )
10486 MESSAGE(" backelem ");
10487 const SMDS_MeshElement* curElem = backElemItr->next();
10488 if ( curElem && theElems.find(curElem) == theElems.end() &&
10490 isInside( curElem, *bsc3d, aTol ) :
10491 isInside( curElem, *aFaceClassifier, aTol )))
10492 theAffectedElems.insert( curElem );
10500 //================================================================================
10502 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10503 \param theElems - group of of elements (edges or faces) to be replicated
10504 \param theNodesNot - group of nodes not to replicate
10505 \param theShape - shape to detect affected elements (element which geometric center
10506 located on or inside shape).
10507 The replicated nodes should be associated to affected elements.
10508 \return TRUE if operation has been completed successfully, FALSE otherwise
10510 //================================================================================
10512 bool SMESH_MeshEditor::DoubleNodesInRegion( const TIDSortedElemSet& theElems,
10513 const TIDSortedElemSet& theNodesNot,
10514 const TopoDS_Shape& theShape )
10516 if ( theShape.IsNull() )
10519 const double aTol = Precision::Confusion();
10520 auto_ptr< BRepClass3d_SolidClassifier> bsc3d;
10521 auto_ptr<_FaceClassifier> aFaceClassifier;
10522 if ( theShape.ShapeType() == TopAbs_SOLID )
10524 bsc3d.reset( new BRepClass3d_SolidClassifier(theShape));;
10525 bsc3d->PerformInfinitePoint(aTol);
10527 else if (theShape.ShapeType() == TopAbs_FACE )
10529 aFaceClassifier.reset( new _FaceClassifier(TopoDS::Face(theShape)));
10532 // iterates on indicated elements and get elements by back references from their nodes
10533 TIDSortedElemSet anAffected;
10534 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10535 for ( ; elemItr != theElems.end(); ++elemItr )
10537 SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
10541 SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
10542 while ( nodeItr->more() )
10544 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10545 if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
10547 SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
10548 while ( backElemItr->more() )
10550 const SMDS_MeshElement* curElem = backElemItr->next();
10551 if ( curElem && theElems.find(curElem) == theElems.end() &&
10553 isInside( curElem, *bsc3d, aTol ) :
10554 isInside( curElem, *aFaceClassifier, aTol )))
10555 anAffected.insert( curElem );
10559 return DoubleNodes( theElems, theNodesNot, anAffected );
10563 * \brief compute an oriented angle between two planes defined by four points.
10564 * The vector (p0,p1) defines the intersection of the 2 planes (p0,p1,g1) and (p0,p1,g2)
10565 * @param p0 base of the rotation axe
10566 * @param p1 extremity of the rotation axe
10567 * @param g1 belongs to the first plane
10568 * @param g2 belongs to the second plane
10570 double SMESH_MeshEditor::OrientedAngle(const gp_Pnt& p0, const gp_Pnt& p1, const gp_Pnt& g1, const gp_Pnt& g2)
10572 // MESSAGE(" p0: " << p0.X() << " " << p0.Y() << " " << p0.Z());
10573 // MESSAGE(" p1: " << p1.X() << " " << p1.Y() << " " << p1.Z());
10574 // MESSAGE(" g1: " << g1.X() << " " << g1.Y() << " " << g1.Z());
10575 // MESSAGE(" g2: " << g2.X() << " " << g2.Y() << " " << g2.Z());
10576 gp_Vec vref(p0, p1);
10579 gp_Vec n1 = vref.Crossed(v1);
10580 gp_Vec n2 = vref.Crossed(v2);
10582 return n2.AngleWithRef(n1, vref);
10584 catch ( Standard_Failure ) {
10586 return Max( v1.Magnitude(), v2.Magnitude() );
10590 * \brief Double nodes on shared faces between groups of volumes and create flat elements on demand.
10591 * The list of groups must contain at least two groups. The groups have to be disjoint: no common element into two different groups.
10592 * The nodes of the internal faces at the boundaries of the groups are doubled. Optionally, the internal faces are replaced by flat elements.
10593 * Triangles are transformed into prisms, and quadrangles into hexahedrons.
10594 * The flat elements are stored in groups of volumes. These groups are named according to the position of the group in the list:
10595 * 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.
10596 * 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.
10597 * All the flat elements are gathered into the group named "joints3D" (or "joints2D" in 2D situation).
10598 * The flat element of the multiple junctions between the simple junction are stored in a group named "jointsMultiples".
10599 * \param theElems - list of groups of volumes, where a group of volume is a set of
10600 * SMDS_MeshElements sorted by Id.
10601 * \param createJointElems - if TRUE, create the elements
10602 * \param onAllBoundaries - if TRUE, the nodes and elements are also created on
10603 * the boundary between \a theDomains and the rest mesh
10604 * \return TRUE if operation has been completed successfully, FALSE otherwise
10606 bool SMESH_MeshEditor::DoubleNodesOnGroupBoundaries( const std::vector<TIDSortedElemSet>& theElems,
10607 bool createJointElems,
10608 bool onAllBoundaries)
10610 MESSAGE("----------------------------------------------");
10611 MESSAGE("SMESH_MeshEditor::doubleNodesOnGroupBoundaries");
10612 MESSAGE("----------------------------------------------");
10614 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
10615 meshDS->BuildDownWardConnectivity(true);
10617 SMDS_UnstructuredGrid *grid = meshDS->getGrid();
10619 // --- build the list of faces shared by 2 domains (group of elements), with their domain and volume indexes
10620 // build the list of cells with only a node or an edge on the border, with their domain and volume indexes
10621 // build the list of nodes shared by 2 or more domains, with their domain indexes
10623 std::map<DownIdType, std::map<int,int>, DownIdCompare> faceDomains; // face --> (id domain --> id volume)
10624 std::map<int,int>celldom; // cell vtkId --> domain
10625 std::map<DownIdType, std::map<int,int>, DownIdCompare> cellDomains; // oldNode --> (id domain --> id cell)
10626 std::map<int, std::map<int,int> > nodeDomains; // oldId --> (domainId --> newId)
10627 faceDomains.clear();
10629 cellDomains.clear();
10630 nodeDomains.clear();
10631 std::map<int,int> emptyMap;
10632 std::set<int> emptySet;
10635 MESSAGE(".. Number of domains :"<<theElems.size());
10637 TIDSortedElemSet theRestDomElems;
10638 const int iRestDom = -1;
10639 const int idom0 = onAllBoundaries ? iRestDom : 0;
10640 const int nbDomains = theElems.size();
10642 // Check if the domains do not share an element
10643 for (int idom = 0; idom < nbDomains-1; idom++)
10645 // MESSAGE("... Check of domain #" << idom);
10646 const TIDSortedElemSet& domain = theElems[idom];
10647 TIDSortedElemSet::const_iterator elemItr = domain.begin();
10648 for (; elemItr != domain.end(); ++elemItr)
10650 const SMDS_MeshElement* anElem = *elemItr;
10651 int idombisdeb = idom + 1 ;
10652 for (int idombis = idombisdeb; idombis < theElems.size(); idombis++) // check if the element belongs to a domain further in the list
10654 const TIDSortedElemSet& domainbis = theElems[idombis];
10655 if ( domainbis.count(anElem) )
10657 MESSAGE(".... Domain #" << idom);
10658 MESSAGE(".... Domain #" << idombis);
10659 throw SALOME_Exception("The domains are not disjoint.");
10666 for (int idom = 0; idom < nbDomains; idom++)
10669 // --- build a map (face to duplicate --> volume to modify)
10670 // with all the faces shared by 2 domains (group of elements)
10671 // and corresponding volume of this domain, for each shared face.
10672 // a volume has a face shared by 2 domains if it has a neighbor which is not in his domain.
10674 MESSAGE("... Neighbors of domain #" << idom);
10675 const TIDSortedElemSet& domain = theElems[idom];
10676 TIDSortedElemSet::const_iterator elemItr = domain.begin();
10677 for (; elemItr != domain.end(); ++elemItr)
10679 const SMDS_MeshElement* anElem = *elemItr;
10682 int vtkId = anElem->getVtkId();
10683 //MESSAGE(" vtkId " << vtkId << " smdsId " << anElem->GetID());
10684 int neighborsVtkIds[NBMAXNEIGHBORS];
10685 int downIds[NBMAXNEIGHBORS];
10686 unsigned char downTypes[NBMAXNEIGHBORS];
10687 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
10688 for (int n = 0; n < nbNeighbors; n++)
10690 int smdsId = meshDS->fromVtkToSmds(neighborsVtkIds[n]);
10691 const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
10692 if (elem && ! domain.count(elem)) // neighbor is in another domain : face is shared
10695 for (int idombis = 0; idombis < theElems.size() && !ok; idombis++) // check if the neighbor belongs to another domain of the list
10697 // MESSAGE("Domain " << idombis);
10698 const TIDSortedElemSet& domainbis = theElems[idombis];
10699 if ( domainbis.count(elem)) ok = true ; // neighbor is in a correct domain : face is kept
10701 if ( ok || onAllBoundaries ) // the characteristics of the face is stored
10703 DownIdType face(downIds[n], downTypes[n]);
10704 if (!faceDomains[face].count(idom))
10706 faceDomains[face][idom] = vtkId; // volume associated to face in this domain
10707 celldom[vtkId] = idom;
10708 //MESSAGE(" cell with a border " << vtkId << " domain " << idom);
10712 theRestDomElems.insert( elem );
10713 faceDomains[face][iRestDom] = neighborsVtkIds[n];
10714 celldom[neighborsVtkIds[n]] = iRestDom;
10722 //MESSAGE("Number of shared faces " << faceDomains.size());
10723 std::map<DownIdType, std::map<int, int>, DownIdCompare>::iterator itface;
10725 // --- explore the shared faces domain by domain,
10726 // explore the nodes of the face and see if they belong to a cell in the domain,
10727 // which has only a node or an edge on the border (not a shared face)
10729 for (int idomain = idom0; idomain < nbDomains; idomain++)
10731 //MESSAGE("Domain " << idomain);
10732 const TIDSortedElemSet& domain = (idomain == iRestDom) ? theRestDomElems : theElems[idomain];
10733 itface = faceDomains.begin();
10734 for (; itface != faceDomains.end(); ++itface)
10736 const std::map<int, int>& domvol = itface->second;
10737 if (!domvol.count(idomain))
10739 DownIdType face = itface->first;
10740 //MESSAGE(" --- face " << face.cellId);
10741 std::set<int> oldNodes;
10743 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
10744 std::set<int>::iterator itn = oldNodes.begin();
10745 for (; itn != oldNodes.end(); ++itn)
10748 //MESSAGE(" node " << oldId);
10749 vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
10750 for (int i=0; i<l.ncells; i++)
10752 int vtkId = l.cells[i];
10753 const SMDS_MeshElement* anElem = GetMeshDS()->FindElement(GetMeshDS()->fromVtkToSmds(vtkId));
10754 if (!domain.count(anElem))
10756 int vtkType = grid->GetCellType(vtkId);
10757 int downId = grid->CellIdToDownId(vtkId);
10760 MESSAGE("doubleNodesOnGroupBoundaries: internal algorithm problem");
10761 continue; // not OK at this stage of the algorithm:
10762 //no cells created after BuildDownWardConnectivity
10764 DownIdType aCell(downId, vtkType);
10765 cellDomains[aCell][idomain] = vtkId;
10766 celldom[vtkId] = idomain;
10767 //MESSAGE(" cell " << vtkId << " domain " << idomain);
10773 // --- explore the shared faces domain by domain, to duplicate the nodes in a coherent way
10774 // for each shared face, get the nodes
10775 // for each node, for each domain of the face, create a clone of the node
10777 // --- edges at the intersection of 3 or 4 domains, with the order of domains to build
10778 // junction elements of type prism or hexa. the key is the pair of nodesId (lower first)
10779 // the value is the ordered domain ids. (more than 4 domains not taken into account)
10781 std::map<std::vector<int>, std::vector<int> > edgesMultiDomains; // nodes of edge --> ordered domains
10782 std::map<int, std::vector<int> > mutipleNodes; // nodes multi domains with domain order
10783 std::map<int, std::vector<int> > mutipleNodesToFace; // nodes multi domains with domain order to transform in Face (junction between 3 or more 2D domains)
10785 MESSAGE(".. Duplication of the nodes");
10786 for (int idomain = idom0; idomain < nbDomains; idomain++)
10788 itface = faceDomains.begin();
10789 for (; itface != faceDomains.end(); ++itface)
10791 const std::map<int, int>& domvol = itface->second;
10792 if (!domvol.count(idomain))
10794 DownIdType face = itface->first;
10795 //MESSAGE(" --- face " << face.cellId);
10796 std::set<int> oldNodes;
10798 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
10799 std::set<int>::iterator itn = oldNodes.begin();
10800 for (; itn != oldNodes.end(); ++itn)
10803 if (nodeDomains[oldId].empty())
10805 nodeDomains[oldId][idomain] = oldId; // keep the old node in the first domain
10806 //MESSAGE("-+-+-b oldNode " << oldId << " domain " << idomain);
10808 std::map<int, int>::const_iterator itdom = domvol.begin();
10809 for (; itdom != domvol.end(); ++itdom)
10811 int idom = itdom->first;
10812 //MESSAGE(" domain " << idom);
10813 if (!nodeDomains[oldId].count(idom)) // --- node to clone
10815 if (nodeDomains[oldId].size() >= 2) // a multiple node
10817 vector<int> orderedDoms;
10818 //MESSAGE("multiple node " << oldId);
10819 if (mutipleNodes.count(oldId))
10820 orderedDoms = mutipleNodes[oldId];
10823 map<int,int>::iterator it = nodeDomains[oldId].begin();
10824 for (; it != nodeDomains[oldId].end(); ++it)
10825 orderedDoms.push_back(it->first);
10827 orderedDoms.push_back(idom); // TODO order ==> push_front or back
10828 //stringstream txt;
10829 //for (int i=0; i<orderedDoms.size(); i++)
10830 // txt << orderedDoms[i] << " ";
10831 //MESSAGE("orderedDoms " << txt.str());
10832 mutipleNodes[oldId] = orderedDoms;
10834 double *coords = grid->GetPoint(oldId);
10835 SMDS_MeshNode *newNode = meshDS->AddNode(coords[0], coords[1], coords[2]);
10836 int newId = newNode->getVtkId();
10837 nodeDomains[oldId][idom] = newId; // cloned node for other domains
10838 //MESSAGE("-+-+-c oldNode " << oldId << " domain " << idomain << " newNode " << newId << " domain " << idom << " size=" <<nodeDomains[oldId].size());
10845 MESSAGE(".. Creation of elements");
10846 for (int idomain = idom0; idomain < nbDomains; idomain++)
10848 itface = faceDomains.begin();
10849 for (; itface != faceDomains.end(); ++itface)
10851 std::map<int, int> domvol = itface->second;
10852 if (!domvol.count(idomain))
10854 DownIdType face = itface->first;
10855 //MESSAGE(" --- face " << face.cellId);
10856 std::set<int> oldNodes;
10858 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
10859 int nbMultipleNodes = 0;
10860 std::set<int>::iterator itn = oldNodes.begin();
10861 for (; itn != oldNodes.end(); ++itn)
10864 if (mutipleNodes.count(oldId))
10867 if (nbMultipleNodes > 1) // check if an edge of the face is shared between 3 or more domains
10869 //MESSAGE("multiple Nodes detected on a shared face");
10870 int downId = itface->first.cellId;
10871 unsigned char cellType = itface->first.cellType;
10872 // --- shared edge or shared face ?
10873 if ((cellType == VTK_LINE) || (cellType == VTK_QUADRATIC_EDGE)) // shared edge (between two faces)
10876 int nbNodes = grid->getDownArray(cellType)->getNodes(downId, nodes);
10877 for (int i=0; i< nbNodes; i=i+nbNodes-1) // i=0 , i=nbNodes-1
10878 if (mutipleNodes.count(nodes[i]))
10879 if (!mutipleNodesToFace.count(nodes[i]))
10880 mutipleNodesToFace[nodes[i]] = mutipleNodes[nodes[i]];
10882 else // shared face (between two volumes)
10884 int nbEdges = grid->getDownArray(cellType)->getNumberOfDownCells(downId);
10885 const int* downEdgeIds = grid->getDownArray(cellType)->getDownCells(downId);
10886 const unsigned char* edgeType = grid->getDownArray(cellType)->getDownTypes(downId);
10887 for (int ie =0; ie < nbEdges; ie++)
10890 int nbNodes = grid->getDownArray(edgeType[ie])->getNodes(downEdgeIds[ie], nodes);
10891 if (mutipleNodes.count(nodes[0]) && mutipleNodes.count(nodes[nbNodes-1]))
10893 vector<int> vn0 = mutipleNodes[nodes[0]];
10894 vector<int> vn1 = mutipleNodes[nodes[nbNodes - 1]];
10896 for (int i0 = 0; i0 < vn0.size(); i0++)
10897 for (int i1 = 0; i1 < vn1.size(); i1++)
10898 if (vn0[i0] == vn1[i1])
10899 doms.push_back(vn0[i0]);
10900 if (doms.size() >2)
10902 //MESSAGE(" detect edgesMultiDomains " << nodes[0] << " " << nodes[nbNodes - 1]);
10903 double *coords = grid->GetPoint(nodes[0]);
10904 gp_Pnt p0(coords[0], coords[1], coords[2]);
10905 coords = grid->GetPoint(nodes[nbNodes - 1]);
10906 gp_Pnt p1(coords[0], coords[1], coords[2]);
10908 int vtkVolIds[1000]; // an edge can belong to a lot of volumes
10909 map<int, SMDS_VtkVolume*> domvol; // domain --> a volume with the edge
10910 map<int, double> angleDom; // oriented angles between planes defined by edge and volume centers
10911 int nbvol = grid->GetParentVolumes(vtkVolIds, downEdgeIds[ie], edgeType[ie]);
10912 for (int id=0; id < doms.size(); id++)
10914 int idom = doms[id];
10915 const TIDSortedElemSet& domain = (idom == iRestDom) ? theRestDomElems : theElems[idom];
10916 for (int ivol=0; ivol<nbvol; ivol++)
10918 int smdsId = meshDS->fromVtkToSmds(vtkVolIds[ivol]);
10919 SMDS_MeshElement* elem = (SMDS_MeshElement*)meshDS->FindElement(smdsId);
10920 if (domain.count(elem))
10922 SMDS_VtkVolume* svol = dynamic_cast<SMDS_VtkVolume*>(elem);
10923 domvol[idom] = svol;
10924 //MESSAGE(" domain " << idom << " volume " << elem->GetID());
10926 vtkIdType npts = 0;
10927 vtkIdType* pts = 0;
10928 grid->GetCellPoints(vtkVolIds[ivol], npts, pts);
10929 SMDS_VtkVolume::gravityCenter(grid, pts, npts, values);
10932 gref.SetXYZ(gp_XYZ(values[0], values[1], values[2]));
10933 angleDom[idom] = 0;
10937 gp_Pnt g(values[0], values[1], values[2]);
10938 angleDom[idom] = OrientedAngle(p0, p1, gref, g); // -pi<angle<+pi
10939 //MESSAGE(" angle=" << angleDom[idom]);
10945 map<double, int> sortedDom; // sort domains by angle
10946 for (map<int, double>::iterator ia = angleDom.begin(); ia != angleDom.end(); ++ia)
10947 sortedDom[ia->second] = ia->first;
10948 vector<int> vnodes;
10950 for (map<double, int>::iterator ib = sortedDom.begin(); ib != sortedDom.end(); ++ib)
10952 vdom.push_back(ib->second);
10953 //MESSAGE(" ordered domain " << ib->second << " angle " << ib->first);
10955 for (int ino = 0; ino < nbNodes; ino++)
10956 vnodes.push_back(nodes[ino]);
10957 edgesMultiDomains[vnodes] = vdom; // nodes vector --> ordered domains
10966 // --- iterate on shared faces (volumes to modify, face to extrude)
10967 // get node id's of the face (id SMDS = id VTK)
10968 // create flat element with old and new nodes if requested
10970 // --- new quad nodes on flat quad elements: oldId --> ((domain1 X domain2) --> newId)
10971 // (domain1 X domain2) = domain1 + MAXINT*domain2
10973 std::map<int, std::map<long,int> > nodeQuadDomains;
10974 std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
10976 MESSAGE(".. Creation of elements: simple junction");
10977 if (createJointElems)
10980 string joints2DName = "joints2D";
10981 mapOfJunctionGroups[joints2DName] = this->myMesh->AddGroup(SMDSAbs_Face, joints2DName.c_str(), idg);
10982 SMESHDS_Group *joints2DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints2DName]->GetGroupDS());
10983 string joints3DName = "joints3D";
10984 mapOfJunctionGroups[joints3DName] = this->myMesh->AddGroup(SMDSAbs_Volume, joints3DName.c_str(), idg);
10985 SMESHDS_Group *joints3DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints3DName]->GetGroupDS());
10987 itface = faceDomains.begin();
10988 for (; itface != faceDomains.end(); ++itface)
10990 DownIdType face = itface->first;
10991 std::set<int> oldNodes;
10992 std::set<int>::iterator itn;
10994 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
10996 std::map<int, int> domvol = itface->second;
10997 std::map<int, int>::iterator itdom = domvol.begin();
10998 int dom1 = itdom->first;
10999 int vtkVolId = itdom->second;
11001 int dom2 = itdom->first;
11002 SMDS_MeshCell *vol = grid->extrudeVolumeFromFace(vtkVolId, dom1, dom2, oldNodes, nodeDomains,
11004 stringstream grpname;
11007 grpname << dom1 << "_" << dom2;
11009 grpname << dom2 << "_" << dom1;
11010 string namegrp = grpname.str();
11011 if (!mapOfJunctionGroups.count(namegrp))
11012 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(vol->GetType(), namegrp.c_str(), idg);
11013 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11015 sgrp->Add(vol->GetID());
11016 if (vol->GetType() == SMDSAbs_Volume)
11017 joints3DGrp->Add(vol->GetID());
11018 else if (vol->GetType() == SMDSAbs_Face)
11019 joints2DGrp->Add(vol->GetID());
11023 // --- create volumes on multiple domain intersection if requested
11024 // iterate on mutipleNodesToFace
11025 // iterate on edgesMultiDomains
11027 MESSAGE(".. Creation of elements: multiple junction");
11028 if (createJointElems)
11030 // --- iterate on mutipleNodesToFace
11032 std::map<int, std::vector<int> >::iterator itn = mutipleNodesToFace.begin();
11033 for (; itn != mutipleNodesToFace.end(); ++itn)
11035 int node = itn->first;
11036 vector<int> orderDom = itn->second;
11037 vector<vtkIdType> orderedNodes;
11038 for (int idom = 0; idom <orderDom.size(); idom++)
11039 orderedNodes.push_back( nodeDomains[node][orderDom[idom]] );
11040 SMDS_MeshFace* face = this->GetMeshDS()->AddFaceFromVtkIds(orderedNodes);
11042 stringstream grpname;
11044 grpname << 0 << "_" << 0;
11046 string namegrp = grpname.str();
11047 if (!mapOfJunctionGroups.count(namegrp))
11048 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Face, namegrp.c_str(), idg);
11049 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11051 sgrp->Add(face->GetID());
11054 // --- iterate on edgesMultiDomains
11056 std::map<std::vector<int>, std::vector<int> >::iterator ite = edgesMultiDomains.begin();
11057 for (; ite != edgesMultiDomains.end(); ++ite)
11059 vector<int> nodes = ite->first;
11060 vector<int> orderDom = ite->second;
11061 vector<vtkIdType> orderedNodes;
11062 if (nodes.size() == 2)
11064 //MESSAGE(" use edgesMultiDomains " << nodes[0] << " " << nodes[1]);
11065 for (int ino=0; ino < nodes.size(); ino++)
11066 if (orderDom.size() == 3)
11067 for (int idom = 0; idom <orderDom.size(); idom++)
11068 orderedNodes.push_back( nodeDomains[nodes[ino]][orderDom[idom]] );
11070 for (int idom = orderDom.size()-1; idom >=0; idom--)
11071 orderedNodes.push_back( nodeDomains[nodes[ino]][orderDom[idom]] );
11072 SMDS_MeshVolume* vol = this->GetMeshDS()->AddVolumeFromVtkIds(orderedNodes);
11075 string namegrp = "jointsMultiples";
11076 if (!mapOfJunctionGroups.count(namegrp))
11077 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
11078 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11080 sgrp->Add(vol->GetID());
11084 INFOS("Quadratic multiple joints not implemented");
11085 // TODO quadratic nodes
11090 // --- list the explicit faces and edges of the mesh that need to be modified,
11091 // i.e. faces and edges built with one or more duplicated nodes.
11092 // associate these faces or edges to their corresponding domain.
11093 // only the first domain found is kept when a face or edge is shared
11095 std::map<DownIdType, std::map<int,int>, DownIdCompare> faceOrEdgeDom; // cellToModify --> (id domain --> id cell)
11096 std::map<int,int> feDom; // vtk id of cell to modify --> id domain
11097 faceOrEdgeDom.clear();
11100 MESSAGE(".. Modification of elements");
11101 for (int idomain = idom0; idomain < nbDomains; idomain++)
11103 std::map<int, std::map<int, int> >::const_iterator itnod = nodeDomains.begin();
11104 for (; itnod != nodeDomains.end(); ++itnod)
11106 int oldId = itnod->first;
11107 //MESSAGE(" node " << oldId);
11108 vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
11109 for (int i = 0; i < l.ncells; i++)
11111 int vtkId = l.cells[i];
11112 int vtkType = grid->GetCellType(vtkId);
11113 int downId = grid->CellIdToDownId(vtkId);
11115 continue; // new cells: not to be modified
11116 DownIdType aCell(downId, vtkType);
11117 int volParents[1000];
11118 int nbvol = grid->GetParentVolumes(volParents, vtkId);
11119 for (int j = 0; j < nbvol; j++)
11120 if (celldom.count(volParents[j]) && (celldom[volParents[j]] == idomain))
11121 if (!feDom.count(vtkId))
11123 feDom[vtkId] = idomain;
11124 faceOrEdgeDom[aCell] = emptyMap;
11125 faceOrEdgeDom[aCell][idomain] = vtkId; // affect face or edge to the first domain only
11126 //MESSAGE("affect cell " << this->GetMeshDS()->fromVtkToSmds(vtkId) << " domain " << idomain
11127 // << " type " << vtkType << " downId " << downId);
11133 // --- iterate on shared faces (volumes to modify, face to extrude)
11134 // get node id's of the face
11135 // replace old nodes by new nodes in volumes, and update inverse connectivity
11137 std::map<DownIdType, std::map<int,int>, DownIdCompare>* maps[3] = {&faceDomains, &cellDomains, &faceOrEdgeDom};
11138 for (int m=0; m<3; m++)
11140 std::map<DownIdType, std::map<int,int>, DownIdCompare>* amap = maps[m];
11141 itface = (*amap).begin();
11142 for (; itface != (*amap).end(); ++itface)
11144 DownIdType face = itface->first;
11145 std::set<int> oldNodes;
11146 std::set<int>::iterator itn;
11148 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11149 //MESSAGE("examine cell, downId " << face.cellId << " type " << int(face.cellType));
11150 std::map<int, int> localClonedNodeIds;
11152 std::map<int, int> domvol = itface->second;
11153 std::map<int, int>::iterator itdom = domvol.begin();
11154 for (; itdom != domvol.end(); ++itdom)
11156 int idom = itdom->first;
11157 int vtkVolId = itdom->second;
11158 //MESSAGE("modify nodes of cell " << this->GetMeshDS()->fromVtkToSmds(vtkVolId) << " domain " << idom);
11159 localClonedNodeIds.clear();
11160 for (itn = oldNodes.begin(); itn != oldNodes.end(); ++itn)
11163 if (nodeDomains[oldId].count(idom))
11165 localClonedNodeIds[oldId] = nodeDomains[oldId][idom];
11166 //MESSAGE(" node " << oldId << " --> " << localClonedNodeIds[oldId]);
11169 meshDS->ModifyCellNodes(vtkVolId, localClonedNodeIds);
11174 meshDS->CleanDownWardConnectivity(); // Mesh has been modified, downward connectivity is no more usable, free memory
11175 grid->BuildLinks();
11183 * \brief Double nodes on some external faces and create flat elements.
11184 * Flat elements are mainly used by some types of mechanic calculations.
11186 * Each group of the list must be constituted of faces.
11187 * Triangles are transformed in prisms, and quadrangles in hexahedrons.
11188 * @param theElems - list of groups of faces, where a group of faces is a set of
11189 * SMDS_MeshElements sorted by Id.
11190 * @return TRUE if operation has been completed successfully, FALSE otherwise
11192 bool SMESH_MeshEditor::CreateFlatElementsOnFacesGroups(const std::vector<TIDSortedElemSet>& theElems)
11194 MESSAGE("-------------------------------------------------");
11195 MESSAGE("SMESH_MeshEditor::CreateFlatElementsOnFacesGroups");
11196 MESSAGE("-------------------------------------------------");
11198 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11200 // --- For each group of faces
11201 // duplicate the nodes, create a flat element based on the face
11202 // replace the nodes of the faces by their clones
11204 std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> clonedNodes;
11205 std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> intermediateNodes;
11206 clonedNodes.clear();
11207 intermediateNodes.clear();
11208 std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11209 mapOfJunctionGroups.clear();
11211 for (int idom = 0; idom < theElems.size(); idom++)
11213 const TIDSortedElemSet& domain = theElems[idom];
11214 TIDSortedElemSet::const_iterator elemItr = domain.begin();
11215 for (; elemItr != domain.end(); ++elemItr)
11217 SMDS_MeshElement* anElem = (SMDS_MeshElement*) *elemItr;
11218 SMDS_MeshFace* aFace = dynamic_cast<SMDS_MeshFace*> (anElem);
11221 // MESSAGE("aFace=" << aFace->GetID());
11222 bool isQuad = aFace->IsQuadratic();
11223 vector<const SMDS_MeshNode*> ln0, ln1, ln2, ln3, ln4;
11225 // --- clone the nodes, create intermediate nodes for non medium nodes of a quad face
11227 SMDS_ElemIteratorPtr nodeIt = aFace->nodesIterator();
11228 while (nodeIt->more())
11230 const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*> (nodeIt->next());
11231 bool isMedium = isQuad && (aFace->IsMediumNode(node));
11233 ln2.push_back(node);
11235 ln0.push_back(node);
11237 const SMDS_MeshNode* clone = 0;
11238 if (!clonedNodes.count(node))
11240 clone = meshDS->AddNode(node->X(), node->Y(), node->Z());
11241 clonedNodes[node] = clone;
11244 clone = clonedNodes[node];
11247 ln3.push_back(clone);
11249 ln1.push_back(clone);
11251 const SMDS_MeshNode* inter = 0;
11252 if (isQuad && (!isMedium))
11254 if (!intermediateNodes.count(node))
11256 inter = meshDS->AddNode(node->X(), node->Y(), node->Z());
11257 intermediateNodes[node] = inter;
11260 inter = intermediateNodes[node];
11261 ln4.push_back(inter);
11265 // --- extrude the face
11267 vector<const SMDS_MeshNode*> ln;
11268 SMDS_MeshVolume* vol = 0;
11269 vtkIdType aType = aFace->GetVtkType();
11273 vol = meshDS->AddVolume(ln0[2], ln0[1], ln0[0], ln1[2], ln1[1], ln1[0]);
11274 // MESSAGE("vol prism " << vol->GetID());
11275 ln.push_back(ln1[0]);
11276 ln.push_back(ln1[1]);
11277 ln.push_back(ln1[2]);
11280 vol = meshDS->AddVolume(ln0[3], ln0[2], ln0[1], ln0[0], ln1[3], ln1[2], ln1[1], ln1[0]);
11281 // MESSAGE("vol hexa " << vol->GetID());
11282 ln.push_back(ln1[0]);
11283 ln.push_back(ln1[1]);
11284 ln.push_back(ln1[2]);
11285 ln.push_back(ln1[3]);
11287 case VTK_QUADRATIC_TRIANGLE:
11288 vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln0[0], ln0[1], ln0[2], ln3[0], ln3[1], ln3[2],
11289 ln2[0], ln2[1], ln2[2], ln4[0], ln4[1], ln4[2]);
11290 // MESSAGE("vol quad prism " << vol->GetID());
11291 ln.push_back(ln1[0]);
11292 ln.push_back(ln1[1]);
11293 ln.push_back(ln1[2]);
11294 ln.push_back(ln3[0]);
11295 ln.push_back(ln3[1]);
11296 ln.push_back(ln3[2]);
11298 case VTK_QUADRATIC_QUAD:
11299 // vol = meshDS->AddVolume(ln0[0], ln0[1], ln0[2], ln0[3], ln1[0], ln1[1], ln1[2], ln1[3],
11300 // ln2[0], ln2[1], ln2[2], ln2[3], ln3[0], ln3[1], ln3[2], ln3[3],
11301 // ln4[0], ln4[1], ln4[2], ln4[3]);
11302 vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln1[3], ln0[0], ln0[1], ln0[2], ln0[3],
11303 ln3[0], ln3[1], ln3[2], ln3[3], ln2[0], ln2[1], ln2[2], ln2[3],
11304 ln4[0], ln4[1], ln4[2], ln4[3]);
11305 // MESSAGE("vol quad hexa " << vol->GetID());
11306 ln.push_back(ln1[0]);
11307 ln.push_back(ln1[1]);
11308 ln.push_back(ln1[2]);
11309 ln.push_back(ln1[3]);
11310 ln.push_back(ln3[0]);
11311 ln.push_back(ln3[1]);
11312 ln.push_back(ln3[2]);
11313 ln.push_back(ln3[3]);
11323 stringstream grpname;
11327 string namegrp = grpname.str();
11328 if (!mapOfJunctionGroups.count(namegrp))
11329 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
11330 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11332 sgrp->Add(vol->GetID());
11335 // --- modify the face
11337 aFace->ChangeNodes(&ln[0], ln.size());
11344 * \brief identify all the elements around a geom shape, get the faces delimiting the hole
11345 * Build groups of volume to remove, groups of faces to replace on the skin of the object,
11346 * groups of faces to remove inside the object, (idem edges).
11347 * Build ordered list of nodes at the border of each group of faces to replace (to be used to build a geom subshape)
11349 void SMESH_MeshEditor::CreateHoleSkin(double radius,
11350 const TopoDS_Shape& theShape,
11351 SMESH_NodeSearcher* theNodeSearcher,
11352 const char* groupName,
11353 std::vector<double>& nodesCoords,
11354 std::vector<std::vector<int> >& listOfListOfNodes)
11356 MESSAGE("--------------------------------");
11357 MESSAGE("SMESH_MeshEditor::CreateHoleSkin");
11358 MESSAGE("--------------------------------");
11360 // --- zone of volumes to remove is given :
11361 // 1 either by a geom shape (one or more vertices) and a radius,
11362 // 2 either by a group of nodes (representative of the shape)to use with the radius,
11363 // 3 either by a group of nodes where all the elements build on one of this nodes are to remove,
11364 // In the case 2, the group of nodes is an external group of nodes from another mesh,
11365 // In the case 3, the group of nodes is an internal group of the mesh (obtained for instance by a filter),
11366 // defined by it's name.
11368 SMESHDS_GroupBase* groupDS = 0;
11369 SMESH_Mesh::GroupIteratorPtr groupIt = this->myMesh->GetGroups();
11370 while ( groupIt->more() )
11373 SMESH_Group * group = groupIt->next();
11374 if ( !group ) continue;
11375 groupDS = group->GetGroupDS();
11376 if ( !groupDS || groupDS->IsEmpty() ) continue;
11377 std::string grpName = group->GetName();
11378 //MESSAGE("grpName=" << grpName);
11379 if (grpName == groupName)
11385 bool isNodeGroup = false;
11386 bool isNodeCoords = false;
11389 if (groupDS->GetType() != SMDSAbs_Node)
11391 isNodeGroup = true; // a group of nodes exists and it is in this mesh
11394 if (nodesCoords.size() > 0)
11395 isNodeCoords = true; // a list o nodes given by their coordinates
11396 //MESSAGE("---" << isNodeGroup << " " << isNodeCoords);
11398 // --- define groups to build
11400 int idg; // --- group of SMDS volumes
11401 string grpvName = groupName;
11402 grpvName += "_vol";
11403 SMESH_Group *grp = this->myMesh->AddGroup(SMDSAbs_Volume, grpvName.c_str(), idg);
11406 MESSAGE("group not created " << grpvName);
11409 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(grp->GetGroupDS());
11411 int idgs; // --- group of SMDS faces on the skin
11412 string grpsName = groupName;
11413 grpsName += "_skin";
11414 SMESH_Group *grps = this->myMesh->AddGroup(SMDSAbs_Face, grpsName.c_str(), idgs);
11417 MESSAGE("group not created " << grpsName);
11420 SMESHDS_Group *sgrps = dynamic_cast<SMESHDS_Group*>(grps->GetGroupDS());
11422 int idgi; // --- group of SMDS faces internal (several shapes)
11423 string grpiName = groupName;
11424 grpiName += "_internalFaces";
11425 SMESH_Group *grpi = this->myMesh->AddGroup(SMDSAbs_Face, grpiName.c_str(), idgi);
11428 MESSAGE("group not created " << grpiName);
11431 SMESHDS_Group *sgrpi = dynamic_cast<SMESHDS_Group*>(grpi->GetGroupDS());
11433 int idgei; // --- group of SMDS faces internal (several shapes)
11434 string grpeiName = groupName;
11435 grpeiName += "_internalEdges";
11436 SMESH_Group *grpei = this->myMesh->AddGroup(SMDSAbs_Edge, grpeiName.c_str(), idgei);
11439 MESSAGE("group not created " << grpeiName);
11442 SMESHDS_Group *sgrpei = dynamic_cast<SMESHDS_Group*>(grpei->GetGroupDS());
11444 // --- build downward connectivity
11446 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11447 meshDS->BuildDownWardConnectivity(true);
11448 SMDS_UnstructuredGrid* grid = meshDS->getGrid();
11450 // --- set of volumes detected inside
11452 std::set<int> setOfInsideVol;
11453 std::set<int> setOfVolToCheck;
11455 std::vector<gp_Pnt> gpnts;
11458 if (isNodeGroup) // --- a group of nodes is provided : find all the volumes using one or more of this nodes
11460 MESSAGE("group of nodes provided");
11461 SMDS_ElemIteratorPtr elemIt = groupDS->GetElements();
11462 while ( elemIt->more() )
11464 const SMDS_MeshElement* elem = elemIt->next();
11467 const SMDS_MeshNode* node = dynamic_cast<const SMDS_MeshNode*>(elem);
11470 SMDS_MeshElement* vol = 0;
11471 SMDS_ElemIteratorPtr volItr = node->GetInverseElementIterator(SMDSAbs_Volume);
11472 while (volItr->more())
11474 vol = (SMDS_MeshElement*)volItr->next();
11475 setOfInsideVol.insert(vol->getVtkId());
11476 sgrp->Add(vol->GetID());
11480 else if (isNodeCoords)
11482 MESSAGE("list of nodes coordinates provided");
11485 while (i < nodesCoords.size()-2)
11487 double x = nodesCoords[i++];
11488 double y = nodesCoords[i++];
11489 double z = nodesCoords[i++];
11490 gp_Pnt p = gp_Pnt(x, y ,z);
11491 gpnts.push_back(p);
11492 MESSAGE("TopoDS_Vertex " << k << " " << p.X() << " " << p.Y() << " " << p.Z());
11496 else // --- no group, no coordinates : use the vertices of the geom shape provided, and radius
11498 MESSAGE("no group of nodes provided, using vertices from geom shape, and radius");
11499 TopTools_IndexedMapOfShape vertexMap;
11500 TopExp::MapShapes( theShape, TopAbs_VERTEX, vertexMap );
11501 gp_Pnt p = gp_Pnt(0,0,0);
11502 if (vertexMap.Extent() < 1)
11505 for ( int i = 1; i <= vertexMap.Extent(); ++i )
11507 const TopoDS_Vertex& vertex = TopoDS::Vertex( vertexMap( i ));
11508 p = BRep_Tool::Pnt(vertex);
11509 gpnts.push_back(p);
11510 MESSAGE("TopoDS_Vertex " << i << " " << p.X() << " " << p.Y() << " " << p.Z());
11514 if (gpnts.size() > 0)
11517 const SMDS_MeshNode* startNode = theNodeSearcher->FindClosestTo(gpnts[0]);
11519 nodeId = startNode->GetID();
11520 MESSAGE("nodeId " << nodeId);
11522 double radius2 = radius*radius;
11523 MESSAGE("radius2 " << radius2);
11525 // --- volumes on start node
11527 setOfVolToCheck.clear();
11528 SMDS_MeshElement* startVol = 0;
11529 SMDS_ElemIteratorPtr volItr = startNode->GetInverseElementIterator(SMDSAbs_Volume);
11530 while (volItr->more())
11532 startVol = (SMDS_MeshElement*)volItr->next();
11533 setOfVolToCheck.insert(startVol->getVtkId());
11535 if (setOfVolToCheck.empty())
11537 MESSAGE("No volumes found");
11541 // --- starting with central volumes then their neighbors, check if they are inside
11542 // or outside the domain, until no more new neighbor volume is inside.
11543 // Fill the group of inside volumes
11545 std::map<int, double> mapOfNodeDistance2;
11546 mapOfNodeDistance2.clear();
11547 std::set<int> setOfOutsideVol;
11548 while (!setOfVolToCheck.empty())
11550 std::set<int>::iterator it = setOfVolToCheck.begin();
11552 MESSAGE("volume to check, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
11553 bool volInside = false;
11554 vtkIdType npts = 0;
11555 vtkIdType* pts = 0;
11556 grid->GetCellPoints(vtkId, npts, pts);
11557 for (int i=0; i<npts; i++)
11559 double distance2 = 0;
11560 if (mapOfNodeDistance2.count(pts[i]))
11562 distance2 = mapOfNodeDistance2[pts[i]];
11563 MESSAGE("point " << pts[i] << " distance2 " << distance2);
11567 double *coords = grid->GetPoint(pts[i]);
11568 gp_Pnt aPoint = gp_Pnt(coords[0], coords[1], coords[2]);
11570 for (int j=0; j<gpnts.size(); j++)
11572 double d2 = aPoint.SquareDistance(gpnts[j]);
11573 if (d2 < distance2)
11576 if (distance2 < radius2)
11580 mapOfNodeDistance2[pts[i]] = distance2;
11581 MESSAGE(" point " << pts[i] << " distance2 " << distance2 << " coords " << coords[0] << " " << coords[1] << " " << coords[2]);
11583 if (distance2 < radius2)
11585 volInside = true; // one or more nodes inside the domain
11586 sgrp->Add(meshDS->fromVtkToSmds(vtkId));
11592 setOfInsideVol.insert(vtkId);
11593 MESSAGE(" volume inside, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
11594 int neighborsVtkIds[NBMAXNEIGHBORS];
11595 int downIds[NBMAXNEIGHBORS];
11596 unsigned char downTypes[NBMAXNEIGHBORS];
11597 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
11598 for (int n = 0; n < nbNeighbors; n++)
11599 if (!setOfInsideVol.count(neighborsVtkIds[n]) ||setOfOutsideVol.count(neighborsVtkIds[n]))
11600 setOfVolToCheck.insert(neighborsVtkIds[n]);
11604 setOfOutsideVol.insert(vtkId);
11605 MESSAGE(" volume outside, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
11607 setOfVolToCheck.erase(vtkId);
11611 // --- for outside hexahedrons, check if they have more than one neighbor volume inside
11612 // If yes, add the volume to the inside set
11614 bool addedInside = true;
11615 std::set<int> setOfVolToReCheck;
11616 while (addedInside)
11618 MESSAGE(" --------------------------- re check");
11619 addedInside = false;
11620 std::set<int>::iterator itv = setOfInsideVol.begin();
11621 for (; itv != setOfInsideVol.end(); ++itv)
11624 int neighborsVtkIds[NBMAXNEIGHBORS];
11625 int downIds[NBMAXNEIGHBORS];
11626 unsigned char downTypes[NBMAXNEIGHBORS];
11627 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
11628 for (int n = 0; n < nbNeighbors; n++)
11629 if (!setOfInsideVol.count(neighborsVtkIds[n]))
11630 setOfVolToReCheck.insert(neighborsVtkIds[n]);
11632 setOfVolToCheck = setOfVolToReCheck;
11633 setOfVolToReCheck.clear();
11634 while (!setOfVolToCheck.empty())
11636 std::set<int>::iterator it = setOfVolToCheck.begin();
11638 if (grid->GetCellType(vtkId) == VTK_HEXAHEDRON)
11640 MESSAGE("volume to recheck, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
11641 int countInside = 0;
11642 int neighborsVtkIds[NBMAXNEIGHBORS];
11643 int downIds[NBMAXNEIGHBORS];
11644 unsigned char downTypes[NBMAXNEIGHBORS];
11645 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
11646 for (int n = 0; n < nbNeighbors; n++)
11647 if (setOfInsideVol.count(neighborsVtkIds[n]))
11649 MESSAGE("countInside " << countInside);
11650 if (countInside > 1)
11652 MESSAGE(" volume inside, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
11653 setOfInsideVol.insert(vtkId);
11654 sgrp->Add(meshDS->fromVtkToSmds(vtkId));
11655 addedInside = true;
11658 setOfVolToReCheck.insert(vtkId);
11660 setOfVolToCheck.erase(vtkId);
11664 // --- map of Downward faces at the boundary, inside the global volume
11665 // map of Downward faces on the skin of the global volume (equivalent to SMDS faces on the skin)
11666 // fill group of SMDS faces inside the volume (when several volume shapes)
11667 // fill group of SMDS faces on the skin of the global volume (if skin)
11669 std::map<DownIdType, int, DownIdCompare> boundaryFaces; // boundary faces inside the volume --> corresponding cell
11670 std::map<DownIdType, int, DownIdCompare> skinFaces; // faces on the skin of the global volume --> corresponding cell
11671 std::set<int>::iterator it = setOfInsideVol.begin();
11672 for (; it != setOfInsideVol.end(); ++it)
11675 //MESSAGE(" vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
11676 int neighborsVtkIds[NBMAXNEIGHBORS];
11677 int downIds[NBMAXNEIGHBORS];
11678 unsigned char downTypes[NBMAXNEIGHBORS];
11679 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId, true);
11680 for (int n = 0; n < nbNeighbors; n++)
11682 int neighborDim = SMDS_Downward::getCellDimension(grid->GetCellType(neighborsVtkIds[n]));
11683 if (neighborDim == 3)
11685 if (! setOfInsideVol.count(neighborsVtkIds[n])) // neighbor volume is not inside : face is boundary
11687 DownIdType face(downIds[n], downTypes[n]);
11688 boundaryFaces[face] = vtkId;
11690 // if the face between to volumes is in the mesh, get it (internal face between shapes)
11691 int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
11692 if (vtkFaceId >= 0)
11694 sgrpi->Add(meshDS->fromVtkToSmds(vtkFaceId));
11695 // find also the smds edges on this face
11696 int nbEdges = grid->getDownArray(downTypes[n])->getNumberOfDownCells(downIds[n]);
11697 const int* dEdges = grid->getDownArray(downTypes[n])->getDownCells(downIds[n]);
11698 const unsigned char* dTypes = grid->getDownArray(downTypes[n])->getDownTypes(downIds[n]);
11699 for (int i = 0; i < nbEdges; i++)
11701 int vtkEdgeId = grid->getDownArray(dTypes[i])->getVtkCellId(dEdges[i]);
11702 if (vtkEdgeId >= 0)
11703 sgrpei->Add(meshDS->fromVtkToSmds(vtkEdgeId));
11707 else if (neighborDim == 2) // skin of the volume
11709 DownIdType face(downIds[n], downTypes[n]);
11710 skinFaces[face] = vtkId;
11711 int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
11712 if (vtkFaceId >= 0)
11713 sgrps->Add(meshDS->fromVtkToSmds(vtkFaceId));
11718 // --- identify the edges constituting the wire of each subshape on the skin
11719 // define polylines with the nodes of edges, equivalent to wires
11720 // project polylines on subshapes, and partition, to get geom faces
11722 std::map<int, std::set<int> > shapeIdToVtkIdSet; // shapeId --> set of vtkId on skin
11723 std::set<int> emptySet;
11725 std::set<int> shapeIds;
11727 SMDS_ElemIteratorPtr itelem = sgrps->GetElements();
11728 while (itelem->more())
11730 const SMDS_MeshElement *elem = itelem->next();
11731 int shapeId = elem->getshapeId();
11732 int vtkId = elem->getVtkId();
11733 if (!shapeIdToVtkIdSet.count(shapeId))
11735 shapeIdToVtkIdSet[shapeId] = emptySet;
11736 shapeIds.insert(shapeId);
11738 shapeIdToVtkIdSet[shapeId].insert(vtkId);
11741 std::map<int, std::set<DownIdType, DownIdCompare> > shapeIdToEdges; // shapeId --> set of downward edges
11742 std::set<DownIdType, DownIdCompare> emptyEdges;
11743 emptyEdges.clear();
11745 std::map<int, std::set<int> >::iterator itShape = shapeIdToVtkIdSet.begin();
11746 for (; itShape != shapeIdToVtkIdSet.end(); ++itShape)
11748 int shapeId = itShape->first;
11749 MESSAGE(" --- Shape ID --- "<< shapeId);
11750 shapeIdToEdges[shapeId] = emptyEdges;
11752 std::vector<int> nodesEdges;
11754 std::set<int>::iterator its = itShape->second.begin();
11755 for (; its != itShape->second.end(); ++its)
11758 MESSAGE(" " << vtkId);
11759 int neighborsVtkIds[NBMAXNEIGHBORS];
11760 int downIds[NBMAXNEIGHBORS];
11761 unsigned char downTypes[NBMAXNEIGHBORS];
11762 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
11763 for (int n = 0; n < nbNeighbors; n++)
11765 if (neighborsVtkIds[n]<0) // only smds faces are considered as neighbors here
11767 int smdsId = meshDS->fromVtkToSmds(neighborsVtkIds[n]);
11768 const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
11769 if ( shapeIds.count(elem->getshapeId()) && !sgrps->Contains(elem)) // edge : neighbor in the set of shape, not in the group
11771 DownIdType edge(downIds[n], downTypes[n]);
11772 if (!shapeIdToEdges[shapeId].count(edge))
11774 shapeIdToEdges[shapeId].insert(edge);
11776 int nbNodes = grid->getDownArray(downTypes[n])->getNodes(downIds[n],vtkNodeId);
11777 nodesEdges.push_back(vtkNodeId[0]);
11778 nodesEdges.push_back(vtkNodeId[nbNodes-1]);
11779 MESSAGE(" --- nodes " << vtkNodeId[0]+1 << " " << vtkNodeId[nbNodes-1]+1);
11785 std::list<int> order;
11787 if (nodesEdges.size() > 0)
11789 order.push_back(nodesEdges[0]); MESSAGE(" --- back " << order.back()+1); // SMDS id = VTK id + 1;
11790 nodesEdges[0] = -1;
11791 order.push_back(nodesEdges[1]); MESSAGE(" --- back " << order.back()+1);
11792 nodesEdges[1] = -1; // do not reuse this edge
11796 int nodeTofind = order.back(); // try first to push back
11798 for (i = 0; i<nodesEdges.size(); i++)
11799 if (nodesEdges[i] == nodeTofind)
11801 if (i == nodesEdges.size())
11802 found = false; // no follower found on back
11805 if (i%2) // odd ==> use the previous one
11806 if (nodesEdges[i-1] < 0)
11810 order.push_back(nodesEdges[i-1]); MESSAGE(" --- back " << order.back()+1);
11811 nodesEdges[i-1] = -1;
11813 else // even ==> use the next one
11814 if (nodesEdges[i+1] < 0)
11818 order.push_back(nodesEdges[i+1]); MESSAGE(" --- back " << order.back()+1);
11819 nodesEdges[i+1] = -1;
11824 // try to push front
11826 nodeTofind = order.front(); // try to push front
11827 for (i = 0; i<nodesEdges.size(); i++)
11828 if (nodesEdges[i] == nodeTofind)
11830 if (i == nodesEdges.size())
11832 found = false; // no predecessor found on front
11835 if (i%2) // odd ==> use the previous one
11836 if (nodesEdges[i-1] < 0)
11840 order.push_front(nodesEdges[i-1]); MESSAGE(" --- front " << order.front()+1);
11841 nodesEdges[i-1] = -1;
11843 else // even ==> use the next one
11844 if (nodesEdges[i+1] < 0)
11848 order.push_front(nodesEdges[i+1]); MESSAGE(" --- front " << order.front()+1);
11849 nodesEdges[i+1] = -1;
11855 std::vector<int> nodes;
11856 nodes.push_back(shapeId);
11857 std::list<int>::iterator itl = order.begin();
11858 for (; itl != order.end(); itl++)
11860 nodes.push_back((*itl) + 1); // SMDS id = VTK id + 1;
11861 MESSAGE(" ordered node " << nodes[nodes.size()-1]);
11863 listOfListOfNodes.push_back(nodes);
11866 // partition geom faces with blocFissure
11867 // mesh blocFissure and geom faces of the skin (external wires given, triangle algo to choose)
11868 // mesh volume around blocFissure (skin triangles and quadrangle given, tetra algo to choose)
11874 //================================================================================
11876 * \brief Generates skin mesh (containing 2D cells) from 3D mesh
11877 * The created 2D mesh elements based on nodes of free faces of boundary volumes
11878 * \return TRUE if operation has been completed successfully, FALSE otherwise
11880 //================================================================================
11882 bool SMESH_MeshEditor::Make2DMeshFrom3D()
11884 // iterates on volume elements and detect all free faces on them
11885 SMESHDS_Mesh* aMesh = GetMeshDS();
11888 //bool res = false;
11889 int nbFree = 0, nbExisted = 0, nbCreated = 0;
11890 SMDS_VolumeIteratorPtr vIt = aMesh->volumesIterator();
11893 const SMDS_MeshVolume* volume = vIt->next();
11894 SMDS_VolumeTool vTool( volume, /*ignoreCentralNodes=*/false );
11895 vTool.SetExternalNormal();
11896 //const bool isPoly = volume->IsPoly();
11897 const int iQuad = volume->IsQuadratic();
11898 for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
11900 if (!vTool.IsFreeFace(iface))
11903 vector<const SMDS_MeshNode *> nodes;
11904 int nbFaceNodes = vTool.NbFaceNodes(iface);
11905 const SMDS_MeshNode** faceNodes = vTool.GetFaceNodes(iface);
11907 for ( ; inode < nbFaceNodes; inode += iQuad+1)
11908 nodes.push_back(faceNodes[inode]);
11909 if (iQuad) { // add medium nodes
11910 for ( inode = 1; inode < nbFaceNodes; inode += 2)
11911 nodes.push_back(faceNodes[inode]);
11912 if ( nbFaceNodes == 9 ) // bi-quadratic quad
11913 nodes.push_back(faceNodes[8]);
11915 // add new face based on volume nodes
11916 if (aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false) ) {
11918 continue; // face already exsist
11920 AddElement(nodes, SMDSAbs_Face, ( !iQuad && nbFaceNodes/(iQuad+1) > 4 ));
11924 return ( nbFree==(nbExisted+nbCreated) );
11929 inline const SMDS_MeshNode* getNodeWithSameID(SMESHDS_Mesh* mesh, const SMDS_MeshNode* node)
11931 if ( const SMDS_MeshNode* n = mesh->FindNode( node->GetID() ))
11933 return mesh->AddNodeWithID( node->X(),node->Y(),node->Z(), node->GetID() );
11936 //================================================================================
11938 * \brief Creates missing boundary elements
11939 * \param elements - elements whose boundary is to be checked
11940 * \param dimension - defines type of boundary elements to create
11941 * \param group - a group to store created boundary elements in
11942 * \param targetMesh - a mesh to store created boundary elements in
11943 * \param toCopyElements - if true, the checked elements will be copied into the targetMesh
11944 * \param toCopyExistingBoundary - if true, not only new but also pre-existing
11945 * boundary elements will be copied into the targetMesh
11946 * \param toAddExistingBondary - if true, not only new but also pre-existing
11947 * boundary elements will be added into the new group
11948 * \param aroundElements - if true, elements will be created on boundary of given
11949 * elements else, on boundary of the whole mesh.
11950 * \return nb of added boundary elements
11952 //================================================================================
11954 int SMESH_MeshEditor::MakeBoundaryMesh(const TIDSortedElemSet& elements,
11955 Bnd_Dimension dimension,
11956 SMESH_Group* group/*=0*/,
11957 SMESH_Mesh* targetMesh/*=0*/,
11958 bool toCopyElements/*=false*/,
11959 bool toCopyExistingBoundary/*=false*/,
11960 bool toAddExistingBondary/*= false*/,
11961 bool aroundElements/*= false*/)
11963 SMDSAbs_ElementType missType = (dimension == BND_2DFROM3D) ? SMDSAbs_Face : SMDSAbs_Edge;
11964 SMDSAbs_ElementType elemType = (dimension == BND_1DFROM2D) ? SMDSAbs_Face : SMDSAbs_Volume;
11965 // hope that all elements are of the same type, do not check them all
11966 if ( !elements.empty() && (*elements.begin())->GetType() != elemType )
11967 throw SALOME_Exception(LOCALIZED("wrong element type"));
11970 toCopyElements = toCopyExistingBoundary = false;
11972 SMESH_MeshEditor tgtEditor( targetMesh ? targetMesh : myMesh );
11973 SMESHDS_Mesh* aMesh = GetMeshDS(), *tgtMeshDS = tgtEditor.GetMeshDS();
11974 int nbAddedBnd = 0;
11976 // editor adding present bnd elements and optionally holding elements to add to the group
11977 SMESH_MeshEditor* presentEditor;
11978 SMESH_MeshEditor tgtEditor2( tgtEditor.GetMesh() );
11979 presentEditor = toAddExistingBondary ? &tgtEditor : &tgtEditor2;
11981 SMESH_MesherHelper helper( *myMesh );
11982 const TopAbs_ShapeEnum missShapeType = ( missType==SMDSAbs_Face ? TopAbs_FACE : TopAbs_EDGE );
11983 SMDS_VolumeTool vTool;
11984 TIDSortedElemSet avoidSet;
11985 const TIDSortedElemSet emptySet, *elemSet = aroundElements ? &elements : &emptySet;
11988 typedef vector<const SMDS_MeshNode*> TConnectivity;
11990 SMDS_ElemIteratorPtr eIt;
11991 if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
11992 else eIt = elemSetIterator( elements );
11994 while (eIt->more())
11996 const SMDS_MeshElement* elem = eIt->next();
11997 const int iQuad = elem->IsQuadratic();
11999 // ------------------------------------------------------------------------------------
12000 // 1. For an elem, get present bnd elements and connectivities of missing bnd elements
12001 // ------------------------------------------------------------------------------------
12002 vector<const SMDS_MeshElement*> presentBndElems;
12003 vector<TConnectivity> missingBndElems;
12004 TConnectivity nodes, elemNodes;
12005 if ( vTool.Set(elem, /*ignoreCentralNodes=*/true) ) // elem is a volume --------------
12007 vTool.SetExternalNormal();
12008 const SMDS_MeshElement* otherVol = 0;
12009 for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
12011 if ( !vTool.IsFreeFace(iface, &otherVol) &&
12012 ( !aroundElements || elements.count( otherVol )))
12014 const SMDS_MeshNode** nn = vTool.GetFaceNodes(iface);
12015 const int nbFaceNodes = vTool.NbFaceNodes (iface);
12016 if ( missType == SMDSAbs_Edge ) // boundary edges
12018 nodes.resize( 2+iQuad );
12019 for ( int i = 0; i < nbFaceNodes; i += 1+iQuad)
12021 for ( int j = 0; j < nodes.size(); ++j )
12023 if ( const SMDS_MeshElement* edge =
12024 aMesh->FindElement(nodes,SMDSAbs_Edge,/*noMedium=*/false))
12025 presentBndElems.push_back( edge );
12027 missingBndElems.push_back( nodes );
12030 else // boundary face
12033 for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12034 nodes.push_back( nn[inode] ); // add corner nodes
12036 for ( inode = 1; inode < nbFaceNodes; inode += 2)
12037 nodes.push_back( nn[inode] ); // add medium nodes
12038 int iCenter = vTool.GetCenterNodeIndex(iface); // for HEX27
12040 nodes.push_back( vTool.GetNodes()[ iCenter ] );
12042 if (const SMDS_MeshElement * f = aMesh->FindElement( nodes,
12043 SMDSAbs_Face, /*noMedium=*/false ))
12044 presentBndElems.push_back( f );
12046 missingBndElems.push_back( nodes );
12048 if ( targetMesh != myMesh )
12050 // add 1D elements on face boundary to be added to a new mesh
12051 const SMDS_MeshElement* edge;
12052 for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12055 edge = aMesh->FindEdge( nn[inode], nn[inode+1], nn[inode+2]);
12057 edge = aMesh->FindEdge( nn[inode], nn[inode+1]);
12058 if ( edge && avoidSet.insert( edge ).second )
12059 presentBndElems.push_back( edge );
12065 else if ( elem->GetType() == SMDSAbs_Face ) // elem is a face ------------------------
12067 avoidSet.clear(), avoidSet.insert( elem );
12068 elemNodes.assign( SMDS_MeshElement::iterator( elem->interlacedNodesElemIterator() ),
12069 SMDS_MeshElement::iterator() );
12070 elemNodes.push_back( elemNodes[0] );
12071 nodes.resize( 2 + iQuad );
12072 const int nbLinks = elem->NbCornerNodes();
12073 for ( int i = 0, iN = 0; i < nbLinks; i++, iN += 1+iQuad )
12075 nodes[0] = elemNodes[iN];
12076 nodes[1] = elemNodes[iN+1+iQuad];
12077 if ( SMESH_MeshAlgos::FindFaceInSet( nodes[0], nodes[1], *elemSet, avoidSet))
12078 continue; // not free link
12080 if ( iQuad ) nodes[2] = elemNodes[iN+1];
12081 if ( const SMDS_MeshElement* edge =
12082 aMesh->FindElement(nodes,SMDSAbs_Edge,/*noMedium=*/false))
12083 presentBndElems.push_back( edge );
12085 missingBndElems.push_back( nodes );
12089 // ---------------------------------
12090 // 2. Add missing boundary elements
12091 // ---------------------------------
12092 if ( targetMesh != myMesh )
12093 // instead of making a map of nodes in this mesh and targetMesh,
12094 // we create nodes with same IDs.
12095 for ( int i = 0; i < missingBndElems.size(); ++i )
12097 TConnectivity& srcNodes = missingBndElems[i];
12098 TConnectivity nodes( srcNodes.size() );
12099 for ( inode = 0; inode < nodes.size(); ++inode )
12100 nodes[inode] = getNodeWithSameID( tgtMeshDS, srcNodes[inode] );
12101 if ( aroundElements && tgtEditor.GetMeshDS()->FindElement( nodes,
12103 /*noMedium=*/false))
12105 tgtEditor.AddElement(nodes, missType, !iQuad && nodes.size()/(iQuad+1)>4);
12109 for ( int i = 0; i < missingBndElems.size(); ++i )
12111 TConnectivity& nodes = missingBndElems[i];
12112 if ( aroundElements && tgtEditor.GetMeshDS()->FindElement( nodes,
12114 /*noMedium=*/false))
12116 SMDS_MeshElement* elem =
12117 tgtEditor.AddElement(nodes, missType, !iQuad && nodes.size()/(iQuad+1)>4);
12120 // try to set a new element to a shape
12121 if ( myMesh->HasShapeToMesh() )
12124 set< pair<TopAbs_ShapeEnum, int > > mediumShapes;
12125 const int nbN = nodes.size() / (iQuad+1 );
12126 for ( inode = 0; inode < nbN && ok; ++inode )
12128 pair<int, TopAbs_ShapeEnum> i_stype =
12129 helper.GetMediumPos( nodes[inode], nodes[(inode+1)%nbN]);
12130 if (( ok = ( i_stype.first > 0 && i_stype.second >= TopAbs_FACE )))
12131 mediumShapes.insert( make_pair ( i_stype.second, i_stype.first ));
12133 if ( ok && mediumShapes.size() > 1 )
12135 set< pair<TopAbs_ShapeEnum, int > >::iterator stype_i = mediumShapes.begin();
12136 pair<TopAbs_ShapeEnum, int> stype_i_0 = *stype_i;
12137 for ( ++stype_i; stype_i != mediumShapes.end() && ok; ++stype_i )
12139 if (( ok = ( stype_i->first != stype_i_0.first )))
12140 ok = helper.IsSubShape( aMesh->IndexToShape( stype_i->second ),
12141 aMesh->IndexToShape( stype_i_0.second ));
12144 if ( ok && mediumShapes.begin()->first == missShapeType )
12145 aMesh->SetMeshElementOnShape( elem, mediumShapes.begin()->second );
12149 // ----------------------------------
12150 // 3. Copy present boundary elements
12151 // ----------------------------------
12152 if ( toCopyExistingBoundary )
12153 for ( int i = 0 ; i < presentBndElems.size(); ++i )
12155 const SMDS_MeshElement* e = presentBndElems[i];
12156 TConnectivity nodes( e->NbNodes() );
12157 for ( inode = 0; inode < nodes.size(); ++inode )
12158 nodes[inode] = getNodeWithSameID( tgtMeshDS, e->GetNode(inode) );
12159 presentEditor->AddElement(nodes, e->GetType(), e->IsPoly());
12161 else // store present elements to add them to a group
12162 for ( int i = 0 ; i < presentBndElems.size(); ++i )
12164 presentEditor->myLastCreatedElems.Append(presentBndElems[i]);
12167 } // loop on given elements
12169 // ---------------------------------------------
12170 // 4. Fill group with boundary elements
12171 // ---------------------------------------------
12174 if ( SMESHDS_Group* g = dynamic_cast<SMESHDS_Group*>( group->GetGroupDS() ))
12175 for ( int i = 0; i < tgtEditor.myLastCreatedElems.Size(); ++i )
12176 g->SMDSGroup().Add( tgtEditor.myLastCreatedElems( i+1 ));
12178 tgtEditor.myLastCreatedElems.Clear();
12179 tgtEditor2.myLastCreatedElems.Clear();
12181 // -----------------------
12182 // 5. Copy given elements
12183 // -----------------------
12184 if ( toCopyElements && targetMesh != myMesh )
12186 if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
12187 else eIt = elemSetIterator( elements );
12188 while (eIt->more())
12190 const SMDS_MeshElement* elem = eIt->next();
12191 TConnectivity nodes( elem->NbNodes() );
12192 for ( inode = 0; inode < nodes.size(); ++inode )
12193 nodes[inode] = getNodeWithSameID( tgtMeshDS, elem->GetNode(inode) );
12194 tgtEditor.AddElement(nodes, elemType, elem->IsPoly());
12196 tgtEditor.myLastCreatedElems.Clear();