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 ), 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, 8, 1, 2, 9, 3, 7, 8, 2, 6, 9, 7, 4, 8, 6, 5, 9, 4, 0, 8, 5, 1, 9, -1
1621 const int theHexTo2Prisms_BT_1[6*2+1] =
1623 0, 1, 3, 4, 5, 7, 1, 2, 3, 5, 6, 7, -1
1625 const int theHexTo2Prisms_BT_2[6*2+1] =
1627 0, 1, 2, 4, 5, 6, 0, 2, 3, 4, 6, 7, -1
1629 const int* theHexTo2Prisms_BT[2] = { theHexTo2Prisms_BT_1, theHexTo2Prisms_BT_2 };
1631 const int theHexTo2Prisms_LR_1[6*2+1] =
1633 1, 0, 4, 2, 3, 7, 1, 4, 5, 2, 7, 6, -1
1635 const int theHexTo2Prisms_LR_2[6*2+1] =
1637 1, 0, 4, 2, 3, 7, 1, 4, 5, 2, 7, 6, -1
1639 const int* theHexTo2Prisms_LR[2] = { theHexTo2Prisms_LR_1, theHexTo2Prisms_LR_2 };
1641 const int theHexTo2Prisms_FB_1[6*2+1] =
1643 0, 3, 4, 1, 2, 5, 3, 7, 4, 2, 6, 5, -1
1645 const int theHexTo2Prisms_FB_2[6*2+1] =
1647 0, 3, 7, 1, 2, 7, 0, 7, 4, 1, 6, 5, -1
1649 const int* theHexTo2Prisms_FB[2] = { theHexTo2Prisms_FB_1, theHexTo2Prisms_FB_2 };
1652 struct TTriangleFacet //!< stores indices of three nodes of tetra facet
1655 TTriangleFacet(int n1, int n2, int n3): _n1(n1), _n2(n2), _n3(n3) {}
1656 bool contains(int n) const { return ( n == _n1 || n == _n2 || n == _n3 ); }
1657 bool hasAdjacentVol( const SMDS_MeshElement* elem,
1658 const SMDSAbs_GeometryType geom = SMDSGeom_TETRA) const;
1664 const int* _connectivity; //!< foursomes of tetra connectivy finished by -1
1665 bool _baryNode; //!< additional node is to be created at cell barycenter
1666 bool _ownConn; //!< to delete _connectivity in destructor
1667 map<int, const SMDS_MeshNode*> _faceBaryNode; //!< map face index to node at BC of face
1669 TSplitMethod( int nbTet=0, const int* conn=0, bool addNode=false)
1670 : _nbSplits(nbTet), _nbCorners(4), _connectivity(conn), _baryNode(addNode), _ownConn(false) {}
1671 ~TSplitMethod() { if ( _ownConn ) delete [] _connectivity; _connectivity = 0; }
1672 bool hasFacet( const TTriangleFacet& facet ) const
1674 if ( _nbCorners == 4 )
1676 const int* tetConn = _connectivity;
1677 for ( ; tetConn[0] >= 0; tetConn += 4 )
1678 if (( facet.contains( tetConn[0] ) +
1679 facet.contains( tetConn[1] ) +
1680 facet.contains( tetConn[2] ) +
1681 facet.contains( tetConn[3] )) == 3 )
1684 else // prism, _nbCorners == 6
1686 const int* prismConn = _connectivity;
1687 for ( ; prismConn[0] >= 0; prismConn += 6 )
1689 if (( facet.contains( prismConn[0] ) &&
1690 facet.contains( prismConn[1] ) &&
1691 facet.contains( prismConn[2] ))
1693 ( facet.contains( prismConn[3] ) &&
1694 facet.contains( prismConn[4] ) &&
1695 facet.contains( prismConn[5] )))
1703 //=======================================================================
1705 * \brief return TSplitMethod for the given element to split into tetrahedra
1707 //=======================================================================
1709 TSplitMethod getTetraSplitMethod( SMDS_VolumeTool& vol, const int theMethodFlags)
1711 const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
1713 // at HEXA_TO_24 method, each face of volume is split into triangles each based on
1714 // an edge and a face barycenter; tertaherdons are based on triangles and
1715 // a volume barycenter
1716 const bool is24TetMode = ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_24 );
1718 // Find out how adjacent volumes are split
1720 vector < list< TTriangleFacet > > triaSplitsByFace( vol.NbFaces() ); // splits of each side
1721 int hasAdjacentSplits = 0, maxTetConnSize = 0;
1722 for ( int iF = 0; iF < vol.NbFaces(); ++iF )
1724 int nbNodes = vol.NbFaceNodes( iF ) / iQ;
1725 maxTetConnSize += 4 * ( nbNodes - (is24TetMode ? 0 : 2));
1726 if ( nbNodes < 4 ) continue;
1728 list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
1729 const int* nInd = vol.GetFaceNodesIndices( iF );
1732 TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
1733 TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
1734 if ( t012.hasAdjacentVol( vol.Element() )) triaSplits.push_back( t012 );
1735 else if ( t123.hasAdjacentVol( vol.Element() )) triaSplits.push_back( t123 );
1739 int iCom = 0; // common node of triangle faces to split into
1740 for ( int iVar = 0; iVar < nbNodes; ++iVar, ++iCom )
1742 TTriangleFacet t012( nInd[ iQ * ( iCom )],
1743 nInd[ iQ * ( (iCom+1)%nbNodes )],
1744 nInd[ iQ * ( (iCom+2)%nbNodes )]);
1745 TTriangleFacet t023( nInd[ iQ * ( iCom )],
1746 nInd[ iQ * ( (iCom+2)%nbNodes )],
1747 nInd[ iQ * ( (iCom+3)%nbNodes )]);
1748 if ( t012.hasAdjacentVol( vol.Element() ) && t023.hasAdjacentVol( vol.Element() ))
1750 triaSplits.push_back( t012 );
1751 triaSplits.push_back( t023 );
1756 if ( !triaSplits.empty() )
1757 hasAdjacentSplits = true;
1760 // Among variants of split method select one compliant with adjacent volumes
1762 TSplitMethod method;
1763 if ( !vol.Element()->IsPoly() && !is24TetMode )
1765 int nbVariants = 2, nbTet = 0;
1766 const int** connVariants = 0;
1767 switch ( vol.Element()->GetEntityType() )
1769 case SMDSEntity_Hexa:
1770 case SMDSEntity_Quad_Hexa:
1771 case SMDSEntity_TriQuad_Hexa:
1772 if ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_5 )
1773 connVariants = theHexTo5, nbTet = 5;
1775 connVariants = theHexTo6, nbTet = 6, nbVariants = 4;
1777 case SMDSEntity_Pyramid:
1778 case SMDSEntity_Quad_Pyramid:
1779 connVariants = thePyraTo2; nbTet = 2;
1781 case SMDSEntity_Penta:
1782 case SMDSEntity_Quad_Penta:
1783 connVariants = thePentaTo3; nbTet = 3; nbVariants = 6;
1788 for ( int variant = 0; variant < nbVariants && method._nbSplits == 0; ++variant )
1790 // check method compliancy with adjacent tetras,
1791 // all found splits must be among facets of tetras described by this method
1792 method = TSplitMethod( nbTet, connVariants[variant] );
1793 if ( hasAdjacentSplits && method._nbSplits > 0 )
1795 bool facetCreated = true;
1796 for ( int iF = 0; facetCreated && iF < triaSplitsByFace.size(); ++iF )
1798 list< TTriangleFacet >::const_iterator facet = triaSplitsByFace[iF].begin();
1799 for ( ; facetCreated && facet != triaSplitsByFace[iF].end(); ++facet )
1800 facetCreated = method.hasFacet( *facet );
1802 if ( !facetCreated )
1803 method = TSplitMethod(0); // incompatible method
1807 if ( method._nbSplits < 1 )
1809 // No standard method is applicable, use a generic solution:
1810 // each facet of a volume is split into triangles and
1811 // each of triangles and a volume barycenter form a tetrahedron.
1813 const bool isHex27 = ( vol.Element()->GetEntityType() == SMDSEntity_TriQuad_Hexa );
1815 int* connectivity = new int[ maxTetConnSize + 1 ];
1816 method._connectivity = connectivity;
1817 method._ownConn = true;
1818 method._baryNode = !isHex27; // to create central node or not
1821 int baryCenInd = vol.NbNodes() - int( isHex27 );
1822 for ( int iF = 0; iF < vol.NbFaces(); ++iF )
1824 const int nbNodes = vol.NbFaceNodes( iF ) / iQ;
1825 const int* nInd = vol.GetFaceNodesIndices( iF );
1826 // find common node of triangle facets of tetra to create
1827 int iCommon = 0; // index in linear numeration
1828 const list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
1829 if ( !triaSplits.empty() )
1832 const TTriangleFacet* facet = &triaSplits.front();
1833 for ( ; iCommon < nbNodes-1 ; ++iCommon )
1834 if ( facet->contains( nInd[ iQ * iCommon ]) &&
1835 facet->contains( nInd[ iQ * ((iCommon+2)%nbNodes) ]))
1838 else if ( nbNodes > 3 && !is24TetMode )
1840 // find the best method of splitting into triangles by aspect ratio
1841 SMESH::Controls::NumericalFunctorPtr aspectRatio( new SMESH::Controls::AspectRatio);
1842 map< double, int > badness2iCommon;
1843 const SMDS_MeshNode** nodes = vol.GetFaceNodes( iF );
1844 int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
1845 for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCommon )
1848 for ( int iLast = iCommon+2; iLast < iCommon+nbNodes; ++iLast )
1850 SMDS_FaceOfNodes tria ( nodes[ iQ*( iCommon )],
1851 nodes[ iQ*((iLast-1)%nbNodes)],
1852 nodes[ iQ*((iLast )%nbNodes)]);
1853 badness += getBadRate( &tria, aspectRatio );
1855 badness2iCommon.insert( make_pair( badness, iCommon ));
1857 // use iCommon with lowest badness
1858 iCommon = badness2iCommon.begin()->second;
1860 if ( iCommon >= nbNodes )
1861 iCommon = 0; // something wrong
1863 // fill connectivity of tetrahedra based on a current face
1864 int nbTet = nbNodes - 2;
1865 if ( is24TetMode && nbNodes > 3 && triaSplits.empty())
1870 faceBaryCenInd = vol.GetCenterNodeIndex( iF );
1871 method._faceBaryNode[ iF ] = vol.GetNodes()[ faceBaryCenInd ];
1875 method._faceBaryNode[ iF ] = 0;
1876 faceBaryCenInd = baryCenInd + method._faceBaryNode.size();
1879 for ( int i = 0; i < nbTet; ++i )
1881 int i1 = i, i2 = (i+1) % nbNodes;
1882 if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
1883 connectivity[ connSize++ ] = nInd[ iQ * i1 ];
1884 connectivity[ connSize++ ] = nInd[ iQ * i2 ];
1885 connectivity[ connSize++ ] = faceBaryCenInd;
1886 connectivity[ connSize++ ] = baryCenInd;
1891 for ( int i = 0; i < nbTet; ++i )
1893 int i1 = (iCommon+1+i) % nbNodes, i2 = (iCommon+2+i) % nbNodes;
1894 if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
1895 connectivity[ connSize++ ] = nInd[ iQ * iCommon ];
1896 connectivity[ connSize++ ] = nInd[ iQ * i1 ];
1897 connectivity[ connSize++ ] = nInd[ iQ * i2 ];
1898 connectivity[ connSize++ ] = baryCenInd;
1901 method._nbSplits += nbTet;
1903 } // loop on volume faces
1905 connectivity[ connSize++ ] = -1;
1907 } // end of generic solution
1911 //=======================================================================
1913 * \brief return TSplitMethod to split haxhedron into prisms
1915 //=======================================================================
1917 TSplitMethod getPrismSplitMethod( SMDS_VolumeTool& vol,
1918 const int methodFlags,
1919 const int facetToSplit)
1921 // order of facets in HEX according to SMDS_VolumeTool::Hexa_F :
1923 const int iF = ( facetToSplit < 2 ) ? 0 : 1 + ( facetToSplit-2 ) % 2; // [0,1,2]
1925 if ( methodFlags == SMESH_MeshEditor::HEXA_TO_4_PRISMS )
1927 static TSplitMethod to4methods[4]; // order BT, LR, FB
1928 if ( to4methods[iF]._nbSplits == 0 )
1932 to4methods[iF]._connectivity = theHexTo4Prisms_BT;
1933 to4methods[iF]._faceBaryNode[ 0 ] = 0;
1934 to4methods[iF]._faceBaryNode[ 1 ] = 0;
1937 to4methods[iF]._connectivity = theHexTo4Prisms_LR;
1938 to4methods[iF]._faceBaryNode[ 2 ] = 0;
1939 to4methods[iF]._faceBaryNode[ 4 ] = 0;
1942 to4methods[iF]._connectivity = theHexTo4Prisms_FB;
1943 to4methods[iF]._faceBaryNode[ 3 ] = 0;
1944 to4methods[iF]._faceBaryNode[ 5 ] = 0;
1946 default: return to4methods[3];
1948 to4methods[iF]._nbSplits = 4;
1949 to4methods[iF]._nbCorners = 6;
1951 return to4methods[iF];
1953 // else if ( methodFlags == HEXA_TO_2_PRISMS )
1955 TSplitMethod method;
1957 const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
1959 const int nbVariants = 2, nbSplits = 2;
1960 const int** connVariants = 0;
1962 case 0: connVariants = theHexTo2Prisms_BT; break;
1963 case 1: connVariants = theHexTo2Prisms_LR; break;
1964 case 2: connVariants = theHexTo2Prisms_FB; break;
1965 default: return method;
1968 // look for prisms adjacent via facetToSplit and an opposite one
1969 for ( int is2nd = 0; is2nd < 2; ++is2nd )
1971 int iFacet = is2nd ? vol.GetOppFaceIndexOfHex( facetToSplit ) : facetToSplit;
1972 int nbNodes = vol.NbFaceNodes( iFacet ) / iQ;
1973 if ( nbNodes != 4 ) return method;
1975 const int* nInd = vol.GetFaceNodesIndices( iFacet );
1976 TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
1977 TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
1979 if ( t012.hasAdjacentVol( vol.Element(), SMDSGeom_PENTA ))
1981 else if ( t123.hasAdjacentVol( vol.Element(), SMDSGeom_PENTA ))
1986 // there are adjacent prism
1987 for ( int variant = 0; variant < nbVariants; ++variant )
1989 // check method compliancy with adjacent prisms,
1990 // the found prism facets must be among facets of prisms described by current method
1991 method._nbSplits = nbSplits;
1992 method._nbCorners = 6;
1993 method._connectivity = connVariants[ variant ];
1994 if ( method.hasFacet( *t ))
1999 // No adjacent prisms. Select a variant with a best aspect ratio.
2001 double badness[2] = { 0, 0 };
2002 static SMESH::Controls::NumericalFunctorPtr aspectRatio( new SMESH::Controls::AspectRatio);
2003 const SMDS_MeshNode** nodes = vol.GetNodes();
2004 for ( int variant = 0; variant < nbVariants; ++variant )
2005 for ( int is2nd = 0; is2nd < 2; ++is2nd )
2007 int iFacet = is2nd ? vol.GetOppFaceIndexOfHex( facetToSplit ) : facetToSplit;
2008 const int* nInd = vol.GetFaceNodesIndices( iFacet );
2010 method._connectivity = connVariants[ variant ];
2011 TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
2012 TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
2013 TTriangleFacet* t = ( method.hasFacet( t012 )) ? & t012 : & t123;
2015 SMDS_FaceOfNodes tria ( nodes[ t->_n1 ],
2018 badness[ variant ] += getBadRate( &tria, aspectRatio );
2020 const int iBetter = ( badness[1] < badness[0] && badness[0]-badness[1] > 0.1 * badness[0] );
2022 method._nbSplits = nbSplits;
2023 method._nbCorners = 6;
2024 method._connectivity = connVariants[ iBetter ];
2029 //================================================================================
2031 * \brief Check if there is a tetraherdon adjacent to the given element via this facet
2033 //================================================================================
2035 bool TTriangleFacet::hasAdjacentVol( const SMDS_MeshElement* elem,
2036 const SMDSAbs_GeometryType geom ) const
2038 // find the tetrahedron including the three nodes of facet
2039 const SMDS_MeshNode* n1 = elem->GetNode(_n1);
2040 const SMDS_MeshNode* n2 = elem->GetNode(_n2);
2041 const SMDS_MeshNode* n3 = elem->GetNode(_n3);
2042 SMDS_ElemIteratorPtr volIt1 = n1->GetInverseElementIterator(SMDSAbs_Volume);
2043 while ( volIt1->more() )
2045 const SMDS_MeshElement* v = volIt1->next();
2046 if ( v->GetGeomType() != geom )
2048 const int lastCornerInd = v->NbCornerNodes() - 1;
2049 if ( v->IsQuadratic() && v->GetNodeIndex( n1 ) > lastCornerInd )
2050 continue; // medium node not allowed
2051 const int ind2 = v->GetNodeIndex( n2 );
2052 if ( ind2 < 0 || lastCornerInd < ind2 )
2054 const int ind3 = v->GetNodeIndex( n3 );
2055 if ( ind3 < 0 || lastCornerInd < ind3 )
2062 //=======================================================================
2064 * \brief A key of a face of volume
2066 //=======================================================================
2068 struct TVolumeFaceKey: pair< pair< int, int>, pair< int, int> >
2070 TVolumeFaceKey( SMDS_VolumeTool& vol, int iF )
2072 TIDSortedNodeSet sortedNodes;
2073 const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
2074 int nbNodes = vol.NbFaceNodes( iF );
2075 const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iF );
2076 for ( int i = 0; i < nbNodes; i += iQ )
2077 sortedNodes.insert( fNodes[i] );
2078 TIDSortedNodeSet::iterator n = sortedNodes.begin();
2079 first.first = (*(n++))->GetID();
2080 first.second = (*(n++))->GetID();
2081 second.first = (*(n++))->GetID();
2082 second.second = ( sortedNodes.size() > 3 ) ? (*(n++))->GetID() : 0;
2087 //=======================================================================
2088 //function : SplitVolumes
2089 //purpose : Split volume elements into tetrahedra or prisms.
2090 // If facet ID < 0, element is split into tetrahedra,
2091 // else a hexahedron is split into prisms so that the given facet is
2092 // split into triangles
2093 //=======================================================================
2095 void SMESH_MeshEditor::SplitVolumes (const TFacetOfElem & theElems,
2096 const int theMethodFlags)
2098 // std-like iterator on coordinates of nodes of mesh element
2099 typedef SMDS_StdIterator< SMESH_TNodeXYZ, SMDS_ElemIteratorPtr > NXyzIterator;
2100 NXyzIterator xyzEnd;
2102 SMDS_VolumeTool volTool;
2103 SMESH_MesherHelper helper( *GetMesh()), fHelper(*GetMesh());
2104 fHelper.ToFixNodeParameters( true );
2106 SMESHDS_SubMesh* subMesh = 0;//GetMeshDS()->MeshElements(1);
2107 SMESHDS_SubMesh* fSubMesh = 0;//subMesh;
2109 SMESH_SequenceOfElemPtr newNodes, newElems;
2111 // map face of volume to it's baricenrtic node
2112 map< TVolumeFaceKey, const SMDS_MeshNode* > volFace2BaryNode;
2115 TFacetOfElem::const_iterator elem2facet = theElems.begin();
2116 for ( ; elem2facet != theElems.end(); ++elem2facet )
2118 const SMDS_MeshElement* elem = elem2facet->first;
2119 const int facetToSplit = elem2facet->second;
2120 if ( elem->GetType() != SMDSAbs_Volume )
2122 const SMDSAbs_EntityType geomType = elem->GetEntityType();
2123 if ( geomType == SMDSEntity_Tetra || geomType == SMDSEntity_Quad_Tetra )
2126 if ( !volTool.Set( elem, /*ignoreCentralNodes=*/false )) continue; // strange...
2128 TSplitMethod splitMethod = ( facetToSplit < 0 ?
2129 getTetraSplitMethod( volTool, theMethodFlags ) :
2130 getPrismSplitMethod( volTool, theMethodFlags, facetToSplit ));
2131 if ( splitMethod._nbSplits < 1 ) continue;
2133 // find submesh to add new tetras to
2134 if ( !subMesh || !subMesh->Contains( elem ))
2136 int shapeID = FindShape( elem );
2137 helper.SetSubShape( shapeID ); // helper will add tetras to the found submesh
2138 subMesh = GetMeshDS()->MeshElements( shapeID );
2141 if ( elem->IsQuadratic() )
2144 // add quadratic links to the helper
2145 for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
2147 const SMDS_MeshNode** fNodes = volTool.GetFaceNodes( iF );
2148 int nbN = volTool.NbFaceNodes( iF ) - bool( volTool.GetCenterNodeIndex(iF) > 0 );
2149 for ( int iN = 0; iN < nbN; iN += iQ )
2150 helper.AddTLinkNode( fNodes[iN], fNodes[iN+2], fNodes[iN+1] );
2152 helper.SetIsQuadratic( true );
2157 helper.SetIsQuadratic( false );
2159 vector<const SMDS_MeshNode*> nodes( volTool.GetNodes(),
2160 volTool.GetNodes() + elem->NbNodes() );
2161 helper.SetElementsOnShape( true );
2162 if ( splitMethod._baryNode )
2164 // make a node at barycenter
2165 volTool.GetBaryCenter( bc[0], bc[1], bc[2] );
2166 SMDS_MeshNode* gcNode = helper.AddNode( bc[0], bc[1], bc[2] );
2167 nodes.push_back( gcNode );
2168 newNodes.Append( gcNode );
2170 if ( !splitMethod._faceBaryNode.empty() )
2172 // make or find baricentric nodes of faces
2173 map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.begin();
2174 for ( ; iF_n != splitMethod._faceBaryNode.end(); ++iF_n )
2176 map< TVolumeFaceKey, const SMDS_MeshNode* >::iterator f_n =
2177 volFace2BaryNode.insert
2178 ( make_pair( TVolumeFaceKey( volTool,iF_n->first ), iF_n->second )).first;
2181 volTool.GetFaceBaryCenter( iF_n->first, bc[0], bc[1], bc[2] );
2182 newNodes.Append( f_n->second = helper.AddNode( bc[0], bc[1], bc[2] ));
2184 nodes.push_back( iF_n->second = f_n->second );
2189 vector<const SMDS_MeshElement* > splitVols( splitMethod._nbSplits ); // splits of a volume
2190 const int* volConn = splitMethod._connectivity;
2191 if ( splitMethod._nbCorners == 4 ) // tetra
2192 for ( int i = 0; i < splitMethod._nbSplits; ++i, volConn += splitMethod._nbCorners )
2193 newElems.Append( splitVols[ i ] = helper.AddVolume( nodes[ volConn[0] ],
2194 nodes[ volConn[1] ],
2195 nodes[ volConn[2] ],
2196 nodes[ volConn[3] ]));
2198 for ( int i = 0; i < splitMethod._nbSplits; ++i, volConn += splitMethod._nbCorners )
2199 newElems.Append( splitVols[ i ] = helper.AddVolume( nodes[ volConn[0] ],
2200 nodes[ volConn[1] ],
2201 nodes[ volConn[2] ],
2202 nodes[ volConn[3] ],
2203 nodes[ volConn[4] ],
2204 nodes[ volConn[5] ]));
2206 ReplaceElemInGroups( elem, splitVols, GetMeshDS() );
2208 // Split faces on sides of the split volume
2210 const SMDS_MeshNode** volNodes = volTool.GetNodes();
2211 for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
2213 const int nbNodes = volTool.NbFaceNodes( iF ) / iQ;
2214 if ( nbNodes < 4 ) continue;
2216 // find an existing face
2217 vector<const SMDS_MeshNode*> fNodes( volTool.GetFaceNodes( iF ),
2218 volTool.GetFaceNodes( iF ) + volTool.NbFaceNodes( iF ));
2219 while ( const SMDS_MeshElement* face = GetMeshDS()->FindElement( fNodes, SMDSAbs_Face,
2220 /*noMedium=*/false))
2223 helper.SetElementsOnShape( false );
2224 vector< const SMDS_MeshElement* > triangles;
2226 // find submesh to add new triangles in
2227 if ( !fSubMesh || !fSubMesh->Contains( face ))
2229 int shapeID = FindShape( face );
2230 fSubMesh = GetMeshDS()->MeshElements( shapeID );
2232 map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.find(iF);
2233 if ( iF_n != splitMethod._faceBaryNode.end() )
2235 const SMDS_MeshNode *baryNode = iF_n->second;
2236 for ( int iN = 0; iN < nbNodes*iQ; iN += iQ )
2238 const SMDS_MeshNode* n1 = fNodes[iN];
2239 const SMDS_MeshNode *n2 = fNodes[(iN+iQ)%(nbNodes*iQ)];
2240 const SMDS_MeshNode *n3 = baryNode;
2241 if ( !volTool.IsFaceExternal( iF ))
2243 triangles.push_back( helper.AddFace( n1,n2,n3 ));
2245 if ( fSubMesh ) // update position of the bary node on geometry
2248 subMesh->RemoveNode( baryNode, false );
2249 GetMeshDS()->SetNodeOnFace( baryNode, fSubMesh->GetID() );
2250 const TopoDS_Shape& s = GetMeshDS()->IndexToShape( fSubMesh->GetID() );
2251 if ( !s.IsNull() && s.ShapeType() == TopAbs_FACE )
2253 fHelper.SetSubShape( s );
2254 gp_XY uv( 1e100, 1e100 );
2256 if ( !fHelper.CheckNodeUV( TopoDS::Face( s ), baryNode,
2257 uv, /*tol=*/1e-7, /*force=*/true, distXYZ ) &&
2260 // node is too far from the surface
2261 GetMeshDS()->MoveNode( baryNode, distXYZ[1], distXYZ[2], distXYZ[3] );
2262 const_cast<SMDS_MeshNode*>( baryNode )->SetPosition
2263 ( SMDS_PositionPtr( new SMDS_FacePosition( uv.X(), uv.Y() )));
2270 // among possible triangles create ones discribed by split method
2271 const int* nInd = volTool.GetFaceNodesIndices( iF );
2272 int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
2273 int iCom = 0; // common node of triangle faces to split into
2274 list< TTriangleFacet > facets;
2275 for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCom )
2277 TTriangleFacet t012( nInd[ iQ * ( iCom )],
2278 nInd[ iQ * ( (iCom+1)%nbNodes )],
2279 nInd[ iQ * ( (iCom+2)%nbNodes )]);
2280 TTriangleFacet t023( nInd[ iQ * ( iCom )],
2281 nInd[ iQ * ( (iCom+2)%nbNodes )],
2282 nInd[ iQ * ( (iCom+3)%nbNodes )]);
2283 if ( splitMethod.hasFacet( t012 ) && splitMethod.hasFacet( t023 ))
2285 facets.push_back( t012 );
2286 facets.push_back( t023 );
2287 for ( int iLast = iCom+4; iLast < iCom+nbNodes; ++iLast )
2288 facets.push_back( TTriangleFacet( nInd[ iQ * ( iCom )],
2289 nInd[ iQ * ((iLast-1)%nbNodes )],
2290 nInd[ iQ * ((iLast )%nbNodes )]));
2294 list< TTriangleFacet >::iterator facet = facets.begin();
2295 if ( facet == facets.end() )
2297 for ( ; facet != facets.end(); ++facet )
2299 if ( !volTool.IsFaceExternal( iF ))
2300 swap( facet->_n2, facet->_n3 );
2301 triangles.push_back( helper.AddFace( volNodes[ facet->_n1 ],
2302 volNodes[ facet->_n2 ],
2303 volNodes[ facet->_n3 ]));
2306 for ( int i = 0; i < triangles.size(); ++i )
2308 if ( !triangles[i] ) continue;
2310 fSubMesh->AddElement( triangles[i]);
2311 newElems.Append( triangles[i] );
2313 ReplaceElemInGroups( face, triangles, GetMeshDS() );
2314 GetMeshDS()->RemoveFreeElement( face, fSubMesh, /*fromGroups=*/false );
2316 } // while a face based on facet nodes exists
2317 } // loop on volume faces to split them into triangles
2319 GetMeshDS()->RemoveFreeElement( elem, subMesh, /*fromGroups=*/false );
2321 if ( geomType == SMDSEntity_TriQuad_Hexa )
2323 // remove medium nodes that could become free
2324 for ( int i = 20; i < volTool.NbNodes(); ++i )
2325 if ( volNodes[i]->NbInverseElements() == 0 )
2326 GetMeshDS()->RemoveNode( volNodes[i] );
2328 } // loop on volumes to split
2330 myLastCreatedNodes = newNodes;
2331 myLastCreatedElems = newElems;
2334 //=======================================================================
2335 //function : GetHexaFacetsToSplit
2336 //purpose : For hexahedra that will be split into prisms, finds facets to
2337 // split into triangles. Only hexahedra adjacent to the one closest
2338 // to theFacetNormal.Location() are returned.
2339 //param [in,out] theHexas - the hexahedra
2340 //param [in] theFacetNormal - facet normal
2341 //param [out] theFacets - the hexahedra and found facet IDs
2342 //=======================================================================
2344 void SMESH_MeshEditor::GetHexaFacetsToSplit( TIDSortedElemSet& theHexas,
2345 const gp_Ax1& theFacetNormal,
2346 TFacetOfElem & theFacets)
2348 #define THIS_METHOD "SMESH_MeshEditor::GetHexaFacetsToSplit(): "
2350 // Find a hexa closest to the location of theFacetNormal
2352 const SMDS_MeshElement* startHex;
2354 // get SMDS_ElemIteratorPtr on theHexas
2355 typedef const SMDS_MeshElement* TValue;
2356 typedef TIDSortedElemSet::iterator TSetIterator;
2357 typedef SMDS::SimpleAccessor<TValue,TSetIterator> TAccesor;
2358 typedef SMDS_MeshElement::GeomFilter TFilter;
2359 typedef SMDS_SetIterator < TValue, TSetIterator, TAccesor, TFilter > TElemSetIter;
2360 SMDS_ElemIteratorPtr elemIt = SMDS_ElemIteratorPtr
2361 ( new TElemSetIter( theHexas.begin(),
2363 SMDS_MeshElement::GeomFilter( SMDSGeom_HEXA )));
2365 SMESH_ElementSearcher* searcher =
2366 SMESH_MeshAlgos::GetElementSearcher( *myMesh->GetMeshDS(), elemIt );
2368 startHex = searcher->FindClosestTo( theFacetNormal.Location(), SMDSAbs_Volume );
2373 throw SALOME_Exception( THIS_METHOD "startHex not found");
2376 // Select a facet of startHex by theFacetNormal
2378 SMDS_VolumeTool vTool( startHex );
2379 double norm[3], dot, maxDot = 0;
2381 for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2382 if ( vTool.GetFaceNormal( iF, norm[0], norm[1], norm[2] ))
2384 dot = Abs( theFacetNormal.Direction().Dot( gp_Dir( norm[0], norm[1], norm[2] )));
2392 throw SALOME_Exception( THIS_METHOD "facet of startHex not found");
2394 // Fill theFacets starting from facetID of startHex
2396 // facets used for seach of volumes adjacent to already treated ones
2397 typedef pair< TFacetOfElem::iterator, int > TElemFacets;
2398 typedef map< TVolumeFaceKey, TElemFacets > TFacetMap;
2399 TFacetMap facetsToCheck;
2401 set<const SMDS_MeshNode*> facetNodes;
2402 const SMDS_MeshElement* curHex;
2404 const bool allHex = ( theHexas.size() == myMesh->NbHexas() );
2408 // move in two directions from startHex via facetID
2409 for ( int is2nd = 0; is2nd < 2; ++is2nd )
2412 int curFacet = facetID;
2413 if ( is2nd ) // do not treat startHex twice
2415 vTool.Set( curHex );
2416 if ( vTool.IsFreeFace( curFacet, &curHex ))
2422 vTool.GetFaceNodes( curFacet, facetNodes );
2423 vTool.Set( curHex );
2424 curFacet = vTool.GetFaceIndex( facetNodes );
2429 // store a facet to split
2430 if ( curHex->GetGeomType() != SMDSGeom_HEXA )
2432 theFacets.insert( make_pair( curHex, -1 ));
2435 if ( !allHex && !theHexas.count( curHex ))
2438 pair< TFacetOfElem::iterator, bool > facetIt2isNew =
2439 theFacets.insert( make_pair( curHex, curFacet ));
2440 if ( !facetIt2isNew.second )
2443 // remember not-to-split facets in facetsToCheck
2444 int oppFacet = vTool.GetOppFaceIndexOfHex( curFacet );
2445 for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2447 if ( iF == curFacet && iF == oppFacet )
2449 TVolumeFaceKey facetKey ( vTool, iF );
2450 TElemFacets elemFacet( facetIt2isNew.first, iF );
2451 pair< TFacetMap::iterator, bool > it2isnew =
2452 facetsToCheck.insert( make_pair( facetKey, elemFacet ));
2453 if ( !it2isnew.second )
2454 facetsToCheck.erase( it2isnew.first ); // adjacent hex already checked
2456 // pass to a volume adjacent via oppFacet
2457 if ( vTool.IsFreeFace( oppFacet, &curHex ))
2463 // get a new curFacet
2464 vTool.GetFaceNodes( oppFacet, facetNodes );
2465 vTool.Set( curHex );
2466 curFacet = vTool.GetFaceIndex( facetNodes, /*hint=*/curFacet );
2469 } // move in two directions from startHex via facetID
2471 // Find a new startHex by facetsToCheck
2475 TFacetMap::iterator fIt = facetsToCheck.begin();
2476 while ( !startHex && fIt != facetsToCheck.end() )
2478 const TElemFacets& elemFacets = fIt->second;
2479 const SMDS_MeshElement* hex = elemFacets.first->first;
2480 int splitFacet = elemFacets.first->second;
2481 int lateralFacet = elemFacets.second;
2482 facetsToCheck.erase( fIt );
2483 fIt = facetsToCheck.begin();
2486 if ( vTool.IsFreeFace( lateralFacet, &curHex ) ||
2487 curHex->GetGeomType() != SMDSGeom_HEXA )
2489 if ( !allHex && !theHexas.count( curHex ))
2494 // find a facet of startHex to split
2496 set<const SMDS_MeshNode*> lateralNodes;
2497 vTool.GetFaceNodes( lateralFacet, lateralNodes );
2498 vTool.GetFaceNodes( splitFacet, facetNodes );
2499 int oppLateralFacet = vTool.GetOppFaceIndexOfHex( lateralFacet );
2500 vTool.Set( startHex );
2501 lateralFacet = vTool.GetFaceIndex( lateralNodes, oppLateralFacet );
2503 // look for a facet of startHex having common nodes with facetNodes
2504 // but not lateralFacet
2505 for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
2507 if ( iF == lateralFacet )
2509 int nbCommonNodes = 0;
2510 const SMDS_MeshNode** nn = vTool.GetFaceNodes( iF );
2511 for ( int iN = 0, nbN = vTool.NbFaceNodes( iF ); iN < nbN; ++iN )
2512 nbCommonNodes += facetNodes.count( nn[ iN ]);
2514 if ( nbCommonNodes >= 2 )
2521 throw SALOME_Exception( THIS_METHOD "facet of a new startHex not found");
2523 } // while ( startHex )
2526 //=======================================================================
2527 //function : AddToSameGroups
2528 //purpose : add elemToAdd to the groups the elemInGroups belongs to
2529 //=======================================================================
2531 void SMESH_MeshEditor::AddToSameGroups (const SMDS_MeshElement* elemToAdd,
2532 const SMDS_MeshElement* elemInGroups,
2533 SMESHDS_Mesh * aMesh)
2535 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2536 if (!groups.empty()) {
2537 set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2538 for ( ; grIt != groups.end(); grIt++ ) {
2539 SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2540 if ( group && group->Contains( elemInGroups ))
2541 group->SMDSGroup().Add( elemToAdd );
2547 //=======================================================================
2548 //function : RemoveElemFromGroups
2549 //purpose : Remove removeelem to the groups the elemInGroups belongs to
2550 //=======================================================================
2551 void SMESH_MeshEditor::RemoveElemFromGroups (const SMDS_MeshElement* removeelem,
2552 SMESHDS_Mesh * aMesh)
2554 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2555 if (!groups.empty())
2557 set<SMESHDS_GroupBase*>::const_iterator GrIt = groups.begin();
2558 for (; GrIt != groups.end(); GrIt++)
2560 SMESHDS_Group* grp = dynamic_cast<SMESHDS_Group*>(*GrIt);
2561 if (!grp || grp->IsEmpty()) continue;
2562 grp->SMDSGroup().Remove(removeelem);
2567 //================================================================================
2569 * \brief Replace elemToRm by elemToAdd in the all groups
2571 //================================================================================
2573 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement* elemToRm,
2574 const SMDS_MeshElement* elemToAdd,
2575 SMESHDS_Mesh * aMesh)
2577 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2578 if (!groups.empty()) {
2579 set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2580 for ( ; grIt != groups.end(); grIt++ ) {
2581 SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2582 if ( group && group->SMDSGroup().Remove( elemToRm ) && elemToAdd )
2583 group->SMDSGroup().Add( elemToAdd );
2588 //================================================================================
2590 * \brief Replace elemToRm by elemToAdd in the all groups
2592 //================================================================================
2594 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement* elemToRm,
2595 const vector<const SMDS_MeshElement*>& elemToAdd,
2596 SMESHDS_Mesh * aMesh)
2598 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2599 if (!groups.empty())
2601 set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2602 for ( ; grIt != groups.end(); grIt++ ) {
2603 SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2604 if ( group && group->SMDSGroup().Remove( elemToRm ) )
2605 for ( int i = 0; i < elemToAdd.size(); ++i )
2606 group->SMDSGroup().Add( elemToAdd[ i ] );
2611 //=======================================================================
2612 //function : QuadToTri
2613 //purpose : Cut quadrangles into triangles.
2614 // theCrit is used to select a diagonal to cut
2615 //=======================================================================
2617 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet & theElems,
2618 const bool the13Diag)
2620 myLastCreatedElems.Clear();
2621 myLastCreatedNodes.Clear();
2623 MESSAGE( "::QuadToTri()" );
2625 SMESHDS_Mesh * aMesh = GetMeshDS();
2627 Handle(Geom_Surface) surface;
2628 SMESH_MesherHelper helper( *GetMesh() );
2630 TIDSortedElemSet::iterator itElem;
2631 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
2632 const SMDS_MeshElement* elem = *itElem;
2633 if ( !elem || elem->GetType() != SMDSAbs_Face )
2635 bool isquad = elem->NbNodes()==4 || elem->NbNodes()==8;
2636 if(!isquad) continue;
2638 if(elem->NbNodes()==4) {
2639 // retrieve element nodes
2640 const SMDS_MeshNode* aNodes [4];
2641 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
2643 while ( itN->more() )
2644 aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
2646 int aShapeId = FindShape( elem );
2647 const SMDS_MeshElement* newElem1 = 0;
2648 const SMDS_MeshElement* newElem2 = 0;
2650 newElem1 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
2651 newElem2 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
2654 newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
2655 newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
2657 myLastCreatedElems.Append(newElem1);
2658 myLastCreatedElems.Append(newElem2);
2659 // put a new triangle on the same shape and add to the same groups
2662 aMesh->SetMeshElementOnShape( newElem1, aShapeId );
2663 aMesh->SetMeshElementOnShape( newElem2, aShapeId );
2665 AddToSameGroups( newElem1, elem, aMesh );
2666 AddToSameGroups( newElem2, elem, aMesh );
2667 //aMesh->RemoveFreeElement(elem, aMesh->MeshElements(aShapeId), true);
2668 aMesh->RemoveElement( elem );
2671 // Quadratic quadrangle
2673 if( elem->NbNodes()==8 && elem->IsQuadratic() ) {
2675 // get surface elem is on
2676 int aShapeId = FindShape( elem );
2677 if ( aShapeId != helper.GetSubShapeID() ) {
2681 shape = aMesh->IndexToShape( aShapeId );
2682 if ( !shape.IsNull() && shape.ShapeType() == TopAbs_FACE ) {
2683 TopoDS_Face face = TopoDS::Face( shape );
2684 surface = BRep_Tool::Surface( face );
2685 if ( !surface.IsNull() )
2686 helper.SetSubShape( shape );
2690 const SMDS_MeshNode* aNodes [8];
2691 const SMDS_MeshNode* inFaceNode = 0;
2692 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
2694 while ( itN->more() ) {
2695 aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
2696 if ( !inFaceNode && helper.GetNodeUVneedInFaceNode() &&
2697 aNodes[ i-1 ]->GetPosition()->GetTypeOfPosition() == SMDS_TOP_FACE )
2699 inFaceNode = aNodes[ i-1 ];
2703 // find middle point for (0,1,2,3)
2704 // and create a node in this point;
2706 if ( surface.IsNull() ) {
2708 p += gp_XYZ(aNodes[i]->X(), aNodes[i]->Y(), aNodes[i]->Z() );
2712 TopoDS_Face geomFace = TopoDS::Face( helper.GetSubShape() );
2715 uv += helper.GetNodeUV( geomFace, aNodes[i], inFaceNode );
2717 p = surface->Value( uv.X(), uv.Y() ).XYZ();
2719 const SMDS_MeshNode* newN = aMesh->AddNode( p.X(), p.Y(), p.Z() );
2720 myLastCreatedNodes.Append(newN);
2722 // create a new element
2723 const SMDS_MeshElement* newElem1 = 0;
2724 const SMDS_MeshElement* newElem2 = 0;
2726 newElem1 = aMesh->AddFace(aNodes[2], aNodes[3], aNodes[0],
2727 aNodes[6], aNodes[7], newN );
2728 newElem2 = aMesh->AddFace(aNodes[2], aNodes[0], aNodes[1],
2729 newN, aNodes[4], aNodes[5] );
2732 newElem1 = aMesh->AddFace(aNodes[3], aNodes[0], aNodes[1],
2733 aNodes[7], aNodes[4], newN );
2734 newElem2 = aMesh->AddFace(aNodes[3], aNodes[1], aNodes[2],
2735 newN, aNodes[5], aNodes[6] );
2737 myLastCreatedElems.Append(newElem1);
2738 myLastCreatedElems.Append(newElem2);
2739 // put a new triangle on the same shape and add to the same groups
2742 aMesh->SetMeshElementOnShape( newElem1, aShapeId );
2743 aMesh->SetMeshElementOnShape( newElem2, aShapeId );
2745 AddToSameGroups( newElem1, elem, aMesh );
2746 AddToSameGroups( newElem2, elem, aMesh );
2747 aMesh->RemoveElement( elem );
2754 //=======================================================================
2755 //function : getAngle
2757 //=======================================================================
2759 double getAngle(const SMDS_MeshElement * tr1,
2760 const SMDS_MeshElement * tr2,
2761 const SMDS_MeshNode * n1,
2762 const SMDS_MeshNode * n2)
2764 double angle = 2. * M_PI; // bad angle
2767 SMESH::Controls::TSequenceOfXYZ P1, P2;
2768 if ( !SMESH::Controls::NumericalFunctor::GetPoints( tr1, P1 ) ||
2769 !SMESH::Controls::NumericalFunctor::GetPoints( tr2, P2 ))
2772 if(!tr1->IsQuadratic())
2773 N1 = gp_Vec( P1(2) - P1(1) ) ^ gp_Vec( P1(3) - P1(1) );
2775 N1 = gp_Vec( P1(3) - P1(1) ) ^ gp_Vec( P1(5) - P1(1) );
2776 if ( N1.SquareMagnitude() <= gp::Resolution() )
2778 if(!tr2->IsQuadratic())
2779 N2 = gp_Vec( P2(2) - P2(1) ) ^ gp_Vec( P2(3) - P2(1) );
2781 N2 = gp_Vec( P2(3) - P2(1) ) ^ gp_Vec( P2(5) - P2(1) );
2782 if ( N2.SquareMagnitude() <= gp::Resolution() )
2785 // find the first diagonal node n1 in the triangles:
2786 // take in account a diagonal link orientation
2787 const SMDS_MeshElement *nFirst[2], *tr[] = { tr1, tr2 };
2788 for ( int t = 0; t < 2; t++ ) {
2789 SMDS_ElemIteratorPtr it = tr[ t ]->nodesIterator();
2790 int i = 0, iDiag = -1;
2791 while ( it->more()) {
2792 const SMDS_MeshElement *n = it->next();
2793 if ( n == n1 || n == n2 ) {
2797 if ( i - iDiag == 1 )
2798 nFirst[ t ] = ( n == n1 ? n2 : n1 );
2807 if ( nFirst[ 0 ] == nFirst[ 1 ] )
2810 angle = N1.Angle( N2 );
2815 // =================================================
2816 // class generating a unique ID for a pair of nodes
2817 // and able to return nodes by that ID
2818 // =================================================
2822 LinkID_Gen( const SMESHDS_Mesh* theMesh )
2823 :myMesh( theMesh ), myMaxID( theMesh->MaxNodeID() + 1)
2826 long GetLinkID (const SMDS_MeshNode * n1,
2827 const SMDS_MeshNode * n2) const
2829 return ( Min(n1->GetID(),n2->GetID()) * myMaxID + Max(n1->GetID(),n2->GetID()));
2832 bool GetNodes (const long theLinkID,
2833 const SMDS_MeshNode* & theNode1,
2834 const SMDS_MeshNode* & theNode2) const
2836 theNode1 = myMesh->FindNode( theLinkID / myMaxID );
2837 if ( !theNode1 ) return false;
2838 theNode2 = myMesh->FindNode( theLinkID % myMaxID );
2839 if ( !theNode2 ) return false;
2845 const SMESHDS_Mesh* myMesh;
2850 //=======================================================================
2851 //function : TriToQuad
2852 //purpose : Fuse neighbour triangles into quadrangles.
2853 // theCrit is used to select a neighbour to fuse with.
2854 // theMaxAngle is a max angle between element normals at which
2855 // fusion is still performed.
2856 //=======================================================================
2858 bool SMESH_MeshEditor::TriToQuad (TIDSortedElemSet & theElems,
2859 SMESH::Controls::NumericalFunctorPtr theCrit,
2860 const double theMaxAngle)
2862 myLastCreatedElems.Clear();
2863 myLastCreatedNodes.Clear();
2865 MESSAGE( "::TriToQuad()" );
2867 if ( !theCrit.get() )
2870 SMESHDS_Mesh * aMesh = GetMeshDS();
2872 // Prepare data for algo: build
2873 // 1. map of elements with their linkIDs
2874 // 2. map of linkIDs with their elements
2876 map< SMESH_TLink, list< const SMDS_MeshElement* > > mapLi_listEl;
2877 map< SMESH_TLink, list< const SMDS_MeshElement* > >::iterator itLE;
2878 map< const SMDS_MeshElement*, set< SMESH_TLink > > mapEl_setLi;
2879 map< const SMDS_MeshElement*, set< SMESH_TLink > >::iterator itEL;
2881 TIDSortedElemSet::iterator itElem;
2882 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
2884 const SMDS_MeshElement* elem = *itElem;
2885 if(!elem || elem->GetType() != SMDSAbs_Face ) continue;
2886 bool IsTria = ( elem->NbCornerNodes()==3 );
2887 if (!IsTria) continue;
2889 // retrieve element nodes
2890 const SMDS_MeshNode* aNodes [4];
2891 SMDS_NodeIteratorPtr itN = elem->nodeIterator();
2894 aNodes[ i++ ] = itN->next();
2895 aNodes[ 3 ] = aNodes[ 0 ];
2898 for ( i = 0; i < 3; i++ ) {
2899 SMESH_TLink link( aNodes[i], aNodes[i+1] );
2900 // check if elements sharing a link can be fused
2901 itLE = mapLi_listEl.find( link );
2902 if ( itLE != mapLi_listEl.end() ) {
2903 if ((*itLE).second.size() > 1 ) // consider only 2 elems adjacent by a link
2905 const SMDS_MeshElement* elem2 = (*itLE).second.front();
2906 //if ( FindShape( elem ) != FindShape( elem2 ))
2907 // continue; // do not fuse triangles laying on different shapes
2908 if ( getAngle( elem, elem2, aNodes[i], aNodes[i+1] ) > theMaxAngle )
2909 continue; // avoid making badly shaped quads
2910 (*itLE).second.push_back( elem );
2913 mapLi_listEl[ link ].push_back( elem );
2915 mapEl_setLi [ elem ].insert( link );
2918 // Clean the maps from the links shared by a sole element, ie
2919 // links to which only one element is bound in mapLi_listEl
2921 for ( itLE = mapLi_listEl.begin(); itLE != mapLi_listEl.end(); itLE++ ) {
2922 int nbElems = (*itLE).second.size();
2923 if ( nbElems < 2 ) {
2924 const SMDS_MeshElement* elem = (*itLE).second.front();
2925 SMESH_TLink link = (*itLE).first;
2926 mapEl_setLi[ elem ].erase( link );
2927 if ( mapEl_setLi[ elem ].empty() )
2928 mapEl_setLi.erase( elem );
2932 // Algo: fuse triangles into quadrangles
2934 while ( ! mapEl_setLi.empty() ) {
2935 // Look for the start element:
2936 // the element having the least nb of shared links
2937 const SMDS_MeshElement* startElem = 0;
2939 for ( itEL = mapEl_setLi.begin(); itEL != mapEl_setLi.end(); itEL++ ) {
2940 int nbLinks = (*itEL).second.size();
2941 if ( nbLinks < minNbLinks ) {
2942 startElem = (*itEL).first;
2943 minNbLinks = nbLinks;
2944 if ( minNbLinks == 1 )
2949 // search elements to fuse starting from startElem or links of elements
2950 // fused earlyer - startLinks
2951 list< SMESH_TLink > startLinks;
2952 while ( startElem || !startLinks.empty() ) {
2953 while ( !startElem && !startLinks.empty() ) {
2954 // Get an element to start, by a link
2955 SMESH_TLink linkId = startLinks.front();
2956 startLinks.pop_front();
2957 itLE = mapLi_listEl.find( linkId );
2958 if ( itLE != mapLi_listEl.end() ) {
2959 list< const SMDS_MeshElement* > & listElem = (*itLE).second;
2960 list< const SMDS_MeshElement* >::iterator itE = listElem.begin();
2961 for ( ; itE != listElem.end() ; itE++ )
2962 if ( mapEl_setLi.find( (*itE) ) != mapEl_setLi.end() )
2964 mapLi_listEl.erase( itLE );
2969 // Get candidates to be fused
2970 const SMDS_MeshElement *tr1 = startElem, *tr2 = 0, *tr3 = 0;
2971 const SMESH_TLink *link12, *link13;
2973 ASSERT( mapEl_setLi.find( tr1 ) != mapEl_setLi.end() );
2974 set< SMESH_TLink >& setLi = mapEl_setLi[ tr1 ];
2975 ASSERT( !setLi.empty() );
2976 set< SMESH_TLink >::iterator itLi;
2977 for ( itLi = setLi.begin(); itLi != setLi.end(); itLi++ )
2979 const SMESH_TLink & link = (*itLi);
2980 itLE = mapLi_listEl.find( link );
2981 if ( itLE == mapLi_listEl.end() )
2984 const SMDS_MeshElement* elem = (*itLE).second.front();
2986 elem = (*itLE).second.back();
2987 mapLi_listEl.erase( itLE );
2988 if ( mapEl_setLi.find( elem ) == mapEl_setLi.end())
2999 // add other links of elem to list of links to re-start from
3000 set< SMESH_TLink >& links = mapEl_setLi[ elem ];
3001 set< SMESH_TLink >::iterator it;
3002 for ( it = links.begin(); it != links.end(); it++ ) {
3003 const SMESH_TLink& link2 = (*it);
3004 if ( link2 != link )
3005 startLinks.push_back( link2 );
3009 // Get nodes of possible quadrangles
3010 const SMDS_MeshNode *n12 [4], *n13 [4];
3011 bool Ok12 = false, Ok13 = false;
3012 const SMDS_MeshNode *linkNode1, *linkNode2;
3014 linkNode1 = link12->first;
3015 linkNode2 = link12->second;
3016 if ( tr2 && getQuadrangleNodes( n12, linkNode1, linkNode2, tr1, tr2 ))
3020 linkNode1 = link13->first;
3021 linkNode2 = link13->second;
3022 if ( tr3 && getQuadrangleNodes( n13, linkNode1, linkNode2, tr1, tr3 ))
3026 // Choose a pair to fuse
3027 if ( Ok12 && Ok13 ) {
3028 SMDS_FaceOfNodes quad12 ( n12[ 0 ], n12[ 1 ], n12[ 2 ], n12[ 3 ] );
3029 SMDS_FaceOfNodes quad13 ( n13[ 0 ], n13[ 1 ], n13[ 2 ], n13[ 3 ] );
3030 double aBadRate12 = getBadRate( &quad12, theCrit );
3031 double aBadRate13 = getBadRate( &quad13, theCrit );
3032 if ( aBadRate13 < aBadRate12 )
3039 // and remove fused elems and remove links from the maps
3040 mapEl_setLi.erase( tr1 );
3043 mapEl_setLi.erase( tr2 );
3044 mapLi_listEl.erase( *link12 );
3045 if ( tr1->NbNodes() == 3 )
3047 const SMDS_MeshElement* newElem = 0;
3048 newElem = aMesh->AddFace(n12[0], n12[1], n12[2], n12[3] );
3049 myLastCreatedElems.Append(newElem);
3050 AddToSameGroups( newElem, tr1, aMesh );
3051 int aShapeId = tr1->getshapeId();
3053 aMesh->SetMeshElementOnShape( newElem, aShapeId );
3054 aMesh->RemoveElement( tr1 );
3055 aMesh->RemoveElement( tr2 );
3058 vector< const SMDS_MeshNode* > N1;
3059 vector< const SMDS_MeshNode* > N2;
3060 getNodesFromTwoTria(tr1,tr2,N1,N2);
3061 // now we receive following N1 and N2 (using numeration as in image in InverseDiag())
3062 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
3063 // i.e. first nodes from both arrays form a new diagonal
3064 const SMDS_MeshNode* aNodes[8];
3073 const SMDS_MeshElement* newElem = 0;
3074 if ( N1.size() == 7 || N2.size() == 7 ) // biquadratic
3075 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3076 aNodes[4], aNodes[5], aNodes[6], aNodes[7], N1[4]);
3078 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3079 aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
3080 myLastCreatedElems.Append(newElem);
3081 AddToSameGroups( newElem, tr1, aMesh );
3082 int aShapeId = tr1->getshapeId();
3084 aMesh->SetMeshElementOnShape( newElem, aShapeId );
3085 aMesh->RemoveElement( tr1 );
3086 aMesh->RemoveElement( tr2 );
3087 // remove middle node (9)
3088 if ( N1[4]->NbInverseElements() == 0 )
3089 aMesh->RemoveNode( N1[4] );
3090 if ( N1.size() == 7 && N1[6]->NbInverseElements() == 0 )
3091 aMesh->RemoveNode( N1[6] );
3092 if ( N2.size() == 7 && N2[6]->NbInverseElements() == 0 )
3093 aMesh->RemoveNode( N2[6] );
3098 mapEl_setLi.erase( tr3 );
3099 mapLi_listEl.erase( *link13 );
3100 if ( tr1->NbNodes() == 3 ) {
3101 const SMDS_MeshElement* newElem = 0;
3102 newElem = aMesh->AddFace(n13[0], n13[1], n13[2], n13[3] );
3103 myLastCreatedElems.Append(newElem);
3104 AddToSameGroups( newElem, tr1, aMesh );
3105 int aShapeId = tr1->getshapeId();
3107 aMesh->SetMeshElementOnShape( newElem, aShapeId );
3108 aMesh->RemoveElement( tr1 );
3109 aMesh->RemoveElement( tr3 );
3112 vector< const SMDS_MeshNode* > N1;
3113 vector< const SMDS_MeshNode* > N2;
3114 getNodesFromTwoTria(tr1,tr3,N1,N2);
3115 // now we receive following N1 and N2 (using numeration as above image)
3116 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
3117 // i.e. first nodes from both arrays form a new diagonal
3118 const SMDS_MeshNode* aNodes[8];
3127 const SMDS_MeshElement* newElem = 0;
3128 if ( N1.size() == 7 || N2.size() == 7 ) // biquadratic
3129 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3130 aNodes[4], aNodes[5], aNodes[6], aNodes[7], N1[4]);
3132 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
3133 aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
3134 myLastCreatedElems.Append(newElem);
3135 AddToSameGroups( newElem, tr1, aMesh );
3136 int aShapeId = tr1->getshapeId();
3138 aMesh->SetMeshElementOnShape( newElem, aShapeId );
3139 aMesh->RemoveElement( tr1 );
3140 aMesh->RemoveElement( tr3 );
3141 // remove middle node (9)
3142 if ( N1[4]->NbInverseElements() == 0 )
3143 aMesh->RemoveNode( N1[4] );
3144 if ( N1.size() == 7 && N1[6]->NbInverseElements() == 0 )
3145 aMesh->RemoveNode( N1[6] );
3146 if ( N2.size() == 7 && N2[6]->NbInverseElements() == 0 )
3147 aMesh->RemoveNode( N2[6] );
3151 // Next element to fuse: the rejected one
3153 startElem = Ok12 ? tr3 : tr2;
3155 } // if ( startElem )
3156 } // while ( startElem || !startLinks.empty() )
3157 } // while ( ! mapEl_setLi.empty() )
3163 /*#define DUMPSO(txt) \
3164 // cout << txt << endl;
3165 //=============================================================================
3169 //=============================================================================
3170 static void swap( int i1, int i2, int idNodes[], gp_Pnt P[] )
3174 int tmp = idNodes[ i1 ];
3175 idNodes[ i1 ] = idNodes[ i2 ];
3176 idNodes[ i2 ] = tmp;
3177 gp_Pnt Ptmp = P[ i1 ];
3180 DUMPSO( i1 << "(" << idNodes[ i2 ] << ") <-> " << i2 << "(" << idNodes[ i1 ] << ")");
3183 //=======================================================================
3184 //function : SortQuadNodes
3185 //purpose : Set 4 nodes of a quadrangle face in a good order.
3186 // Swap 1<->2 or 2<->3 nodes and correspondingly return
3188 //=======================================================================
3190 int SMESH_MeshEditor::SortQuadNodes (const SMDS_Mesh * theMesh,
3195 for ( i = 0; i < 4; i++ ) {
3196 const SMDS_MeshNode *n = theMesh->FindNode( idNodes[i] );
3198 P[ i ].SetCoord( n->X(), n->Y(), n->Z() );
3201 gp_Vec V1(P[0], P[1]);
3202 gp_Vec V2(P[0], P[2]);
3203 gp_Vec V3(P[0], P[3]);
3205 gp_Vec Cross1 = V1 ^ V2;
3206 gp_Vec Cross2 = V2 ^ V3;
3209 if (Cross1.Dot(Cross2) < 0)
3214 if (Cross1.Dot(Cross2) < 0)
3218 swap ( i, i + 1, idNodes, P );
3220 // for ( int ii = 0; ii < 4; ii++ ) {
3221 // const SMDS_MeshNode *n = theMesh->FindNode( idNodes[ii] );
3222 // DUMPSO( ii << "(" << idNodes[ii] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
3228 //=======================================================================
3229 //function : SortHexaNodes
3230 //purpose : Set 8 nodes of a hexahedron in a good order.
3231 // Return success status
3232 //=======================================================================
3234 bool SMESH_MeshEditor::SortHexaNodes (const SMDS_Mesh * theMesh,
3239 DUMPSO( "INPUT: ========================================");
3240 for ( i = 0; i < 8; i++ ) {
3241 const SMDS_MeshNode *n = theMesh->FindNode( idNodes[i] );
3242 if ( !n ) return false;
3243 P[ i ].SetCoord( n->X(), n->Y(), n->Z() );
3244 DUMPSO( i << "(" << idNodes[i] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
3246 DUMPSO( "========================================");
3249 set<int> faceNodes; // ids of bottom face nodes, to be found
3250 set<int> checkedId1; // ids of tried 2-nd nodes
3251 Standard_Real leastDist = DBL_MAX; // dist of the 4-th node from 123 plane
3252 const Standard_Real tol = 1.e-6; // tolerance to find nodes in plane
3253 int iMin, iLoop1 = 0;
3255 // Loop to try the 2-nd nodes
3257 while ( leastDist > DBL_MIN && ++iLoop1 < 8 )
3259 // Find not checked 2-nd node
3260 for ( i = 1; i < 8; i++ )
3261 if ( checkedId1.find( idNodes[i] ) == checkedId1.end() ) {
3262 int id1 = idNodes[i];
3263 swap ( 1, i, idNodes, P );
3264 checkedId1.insert ( id1 );
3268 // Find the 3-d node so that 1-2-3 triangle to be on a hexa face,
3269 // ie that all but meybe one (id3 which is on the same face) nodes
3270 // lay on the same side from the triangle plane.
3272 bool manyInPlane = false; // more than 4 nodes lay in plane
3274 while ( ++iLoop2 < 6 ) {
3276 // get 1-2-3 plane coeffs
3277 Standard_Real A, B, C, D;
3278 gp_Vec N = gp_Vec (P[0], P[1]).Crossed( gp_Vec (P[0], P[2]) );
3279 if ( N.SquareMagnitude() > gp::Resolution() )
3281 gp_Pln pln ( P[0], N );
3282 pln.Coefficients( A, B, C, D );
3284 // find the node (iMin) closest to pln
3285 Standard_Real dist[ 8 ], minDist = DBL_MAX;
3287 for ( i = 3; i < 8; i++ ) {
3288 dist[i] = A * P[i].X() + B * P[i].Y() + C * P[i].Z() + D;
3289 if ( fabs( dist[i] ) < minDist ) {
3290 minDist = fabs( dist[i] );
3293 if ( fabs( dist[i] ) <= tol )
3294 idInPln.insert( idNodes[i] );
3297 // there should not be more than 4 nodes in bottom plane
3298 if ( idInPln.size() > 1 )
3300 DUMPSO( "### idInPln.size() = " << idInPln.size());
3301 // idInPlane does not contain the first 3 nodes
3302 if ( manyInPlane || idInPln.size() == 5)
3303 return false; // all nodes in one plane
3306 // set the 1-st node to be not in plane
3307 for ( i = 3; i < 8; i++ ) {
3308 if ( idInPln.find( idNodes[ i ] ) == idInPln.end() ) {
3309 DUMPSO( "### Reset 0-th node");
3310 swap( 0, i, idNodes, P );
3315 // reset to re-check second nodes
3316 leastDist = DBL_MAX;
3320 break; // from iLoop2;
3323 // check that the other 4 nodes are on the same side
3324 bool sameSide = true;
3325 bool isNeg = dist[ iMin == 3 ? 4 : 3 ] <= 0.;
3326 for ( i = 3; sameSide && i < 8; i++ ) {
3328 sameSide = ( isNeg == dist[i] <= 0.);
3331 // keep best solution
3332 if ( sameSide && minDist < leastDist ) {
3333 leastDist = minDist;
3335 faceNodes.insert( idNodes[ 1 ] );
3336 faceNodes.insert( idNodes[ 2 ] );
3337 faceNodes.insert( idNodes[ iMin ] );
3338 DUMPSO( "loop " << iLoop2 << " id2 " << idNodes[ 1 ] << " id3 " << idNodes[ 2 ]
3339 << " leastDist = " << leastDist);
3340 if ( leastDist <= DBL_MIN )
3345 // set next 3-d node to check
3346 int iNext = 2 + iLoop2;
3348 DUMPSO( "Try 2-nd");
3349 swap ( 2, iNext, idNodes, P );
3351 } // while ( iLoop2 < 6 )
3354 if ( faceNodes.empty() ) return false;
3356 // Put the faceNodes in proper places
3357 for ( i = 4; i < 8; i++ ) {
3358 if ( faceNodes.find( idNodes[ i ] ) != faceNodes.end() ) {
3359 // find a place to put
3361 while ( faceNodes.find( idNodes[ iTo ] ) != faceNodes.end() )
3363 DUMPSO( "Set faceNodes");
3364 swap ( iTo, i, idNodes, P );
3369 // Set nodes of the found bottom face in good order
3370 DUMPSO( " Found bottom face: ");
3371 i = SortQuadNodes( theMesh, idNodes );
3373 gp_Pnt Ptmp = P[ i ];
3378 // for ( int ii = 0; ii < 4; ii++ ) {
3379 // const SMDS_MeshNode *n = theMesh->FindNode( idNodes[ii] );
3380 // DUMPSO( ii << "(" << idNodes[ii] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
3383 // Gravity center of the top and bottom faces
3384 gp_Pnt aGCb = ( P[0].XYZ() + P[1].XYZ() + P[2].XYZ() + P[3].XYZ() ) / 4.;
3385 gp_Pnt aGCt = ( P[4].XYZ() + P[5].XYZ() + P[6].XYZ() + P[7].XYZ() ) / 4.;
3387 // Get direction from the bottom to the top face
3388 gp_Vec upDir ( aGCb, aGCt );
3389 Standard_Real upDirSize = upDir.Magnitude();
3390 if ( upDirSize <= gp::Resolution() ) return false;
3393 // Assure that the bottom face normal points up
3394 gp_Vec Nb = gp_Vec (P[0], P[1]).Crossed( gp_Vec (P[0], P[2]) );
3395 Nb += gp_Vec (P[0], P[2]).Crossed( gp_Vec (P[0], P[3]) );
3396 if ( Nb.Dot( upDir ) < 0 ) {
3397 DUMPSO( "Reverse bottom face");
3398 swap( 1, 3, idNodes, P );
3401 // Find 5-th node - the one closest to the 1-st among the last 4 nodes.
3402 Standard_Real minDist = DBL_MAX;
3403 for ( i = 4; i < 8; i++ ) {
3404 // projection of P[i] to the plane defined by P[0] and upDir
3405 gp_Pnt Pp = P[i].Translated( upDir * ( upDir.Dot( gp_Vec( P[i], P[0] ))));
3406 Standard_Real sqDist = P[0].SquareDistance( Pp );
3407 if ( sqDist < minDist ) {
3412 DUMPSO( "Set 4-th");
3413 swap ( 4, iMin, idNodes, P );
3415 // Set nodes of the top face in good order
3416 DUMPSO( "Sort top face");
3417 i = SortQuadNodes( theMesh, &idNodes[4] );
3420 gp_Pnt Ptmp = P[ i ];
3425 // Assure that direction of the top face normal is from the bottom face
3426 gp_Vec Nt = gp_Vec (P[4], P[5]).Crossed( gp_Vec (P[4], P[6]) );
3427 Nt += gp_Vec (P[4], P[6]).Crossed( gp_Vec (P[4], P[7]) );
3428 if ( Nt.Dot( upDir ) < 0 ) {
3429 DUMPSO( "Reverse top face");
3430 swap( 5, 7, idNodes, P );
3433 // DUMPSO( "OUTPUT: ========================================");
3434 // for ( i = 0; i < 8; i++ ) {
3435 // float *p = ugrid->GetPoint(idNodes[i]);
3436 // DUMPSO( i << "(" << idNodes[i] << ") : " << p[0] << " " << p[1] << " " << p[2]);
3442 //================================================================================
3444 * \brief Return nodes linked to the given one
3445 * \param theNode - the node
3446 * \param linkedNodes - the found nodes
3447 * \param type - the type of elements to check
3449 * Medium nodes are ignored
3451 //================================================================================
3453 void SMESH_MeshEditor::GetLinkedNodes( const SMDS_MeshNode* theNode,
3454 TIDSortedElemSet & linkedNodes,
3455 SMDSAbs_ElementType type )
3457 SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(type);
3458 while ( elemIt->more() )
3460 const SMDS_MeshElement* elem = elemIt->next();
3461 if(elem->GetType() == SMDSAbs_0DElement)
3464 SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
3465 if ( elem->GetType() == SMDSAbs_Volume )
3467 SMDS_VolumeTool vol( elem );
3468 while ( nodeIt->more() ) {
3469 const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
3470 if ( theNode != n && vol.IsLinked( theNode, n ))
3471 linkedNodes.insert( n );
3476 for ( int i = 0; nodeIt->more(); ++i ) {
3477 const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
3478 if ( n == theNode ) {
3479 int iBefore = i - 1;
3481 if ( elem->IsQuadratic() ) {
3482 int nb = elem->NbNodes() / 2;
3483 iAfter = SMESH_MesherHelper::WrapIndex( iAfter, nb );
3484 iBefore = SMESH_MesherHelper::WrapIndex( iBefore, nb );
3486 linkedNodes.insert( elem->GetNodeWrap( iAfter ));
3487 linkedNodes.insert( elem->GetNodeWrap( iBefore ));
3494 //=======================================================================
3495 //function : laplacianSmooth
3496 //purpose : pulls theNode toward the center of surrounding nodes directly
3497 // connected to that node along an element edge
3498 //=======================================================================
3500 void laplacianSmooth(const SMDS_MeshNode* theNode,
3501 const Handle(Geom_Surface)& theSurface,
3502 map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
3504 // find surrounding nodes
3506 TIDSortedElemSet nodeSet;
3507 SMESH_MeshEditor::GetLinkedNodes( theNode, nodeSet, SMDSAbs_Face );
3509 // compute new coodrs
3511 double coord[] = { 0., 0., 0. };
3512 TIDSortedElemSet::iterator nodeSetIt = nodeSet.begin();
3513 for ( ; nodeSetIt != nodeSet.end(); nodeSetIt++ ) {
3514 const SMDS_MeshNode* node = cast2Node(*nodeSetIt);
3515 if ( theSurface.IsNull() ) { // smooth in 3D
3516 coord[0] += node->X();
3517 coord[1] += node->Y();
3518 coord[2] += node->Z();
3520 else { // smooth in 2D
3521 ASSERT( theUVMap.find( node ) != theUVMap.end() );
3522 gp_XY* uv = theUVMap[ node ];
3523 coord[0] += uv->X();
3524 coord[1] += uv->Y();
3527 int nbNodes = nodeSet.size();
3530 coord[0] /= nbNodes;
3531 coord[1] /= nbNodes;
3533 if ( !theSurface.IsNull() ) {
3534 ASSERT( theUVMap.find( theNode ) != theUVMap.end() );
3535 theUVMap[ theNode ]->SetCoord( coord[0], coord[1] );
3536 gp_Pnt p3d = theSurface->Value( coord[0], coord[1] );
3542 coord[2] /= nbNodes;
3546 const_cast< SMDS_MeshNode* >( theNode )->setXYZ(coord[0],coord[1],coord[2]);
3549 //=======================================================================
3550 //function : centroidalSmooth
3551 //purpose : pulls theNode toward the element-area-weighted centroid of the
3552 // surrounding elements
3553 //=======================================================================
3555 void centroidalSmooth(const SMDS_MeshNode* theNode,
3556 const Handle(Geom_Surface)& theSurface,
3557 map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
3559 gp_XYZ aNewXYZ(0.,0.,0.);
3560 SMESH::Controls::Area anAreaFunc;
3561 double totalArea = 0.;
3566 SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(SMDSAbs_Face);
3567 while ( elemIt->more() )
3569 const SMDS_MeshElement* elem = elemIt->next();
3572 gp_XYZ elemCenter(0.,0.,0.);
3573 SMESH::Controls::TSequenceOfXYZ aNodePoints;
3574 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3575 int nn = elem->NbNodes();
3576 if(elem->IsQuadratic()) nn = nn/2;
3578 //while ( itN->more() ) {
3580 const SMDS_MeshNode* aNode = static_cast<const SMDS_MeshNode*>( itN->next() );
3582 gp_XYZ aP( aNode->X(), aNode->Y(), aNode->Z() );
3583 aNodePoints.push_back( aP );
3584 if ( !theSurface.IsNull() ) { // smooth in 2D
3585 ASSERT( theUVMap.find( aNode ) != theUVMap.end() );
3586 gp_XY* uv = theUVMap[ aNode ];
3587 aP.SetCoord( uv->X(), uv->Y(), 0. );
3591 double elemArea = anAreaFunc.GetValue( aNodePoints );
3592 totalArea += elemArea;
3594 aNewXYZ += elemCenter * elemArea;
3596 aNewXYZ /= totalArea;
3597 if ( !theSurface.IsNull() ) {
3598 theUVMap[ theNode ]->SetCoord( aNewXYZ.X(), aNewXYZ.Y() );
3599 aNewXYZ = theSurface->Value( aNewXYZ.X(), aNewXYZ.Y() ).XYZ();
3604 const_cast< SMDS_MeshNode* >( theNode )->setXYZ(aNewXYZ.X(),aNewXYZ.Y(),aNewXYZ.Z());
3607 //=======================================================================
3608 //function : getClosestUV
3609 //purpose : return UV of closest projection
3610 //=======================================================================
3612 static bool getClosestUV (Extrema_GenExtPS& projector,
3613 const gp_Pnt& point,
3616 projector.Perform( point );
3617 if ( projector.IsDone() ) {
3618 double u, v, minVal = DBL_MAX;
3619 for ( int i = projector.NbExt(); i > 0; i-- )
3620 #if OCC_VERSION_LARGE > 0x06040000 // Porting to OCCT6.5.1
3621 if ( projector.SquareDistance( i ) < minVal ) {
3622 minVal = projector.SquareDistance( i );
3624 if ( projector.Value( i ) < minVal ) {
3625 minVal = projector.Value( i );
3627 projector.Point( i ).Parameter( u, v );
3629 result.SetCoord( u, v );
3635 //=======================================================================
3637 //purpose : Smooth theElements during theNbIterations or until a worst
3638 // element has aspect ratio <= theTgtAspectRatio.
3639 // Aspect Ratio varies in range [1.0, inf].
3640 // If theElements is empty, the whole mesh is smoothed.
3641 // theFixedNodes contains additionally fixed nodes. Nodes built
3642 // on edges and boundary nodes are always fixed.
3643 //=======================================================================
3645 void SMESH_MeshEditor::Smooth (TIDSortedElemSet & theElems,
3646 set<const SMDS_MeshNode*> & theFixedNodes,
3647 const SmoothMethod theSmoothMethod,
3648 const int theNbIterations,
3649 double theTgtAspectRatio,
3652 myLastCreatedElems.Clear();
3653 myLastCreatedNodes.Clear();
3655 MESSAGE((theSmoothMethod==LAPLACIAN ? "LAPLACIAN" : "CENTROIDAL") << "--::Smooth()");
3657 if ( theTgtAspectRatio < 1.0 )
3658 theTgtAspectRatio = 1.0;
3660 const double disttol = 1.e-16;
3662 SMESH::Controls::AspectRatio aQualityFunc;
3664 SMESHDS_Mesh* aMesh = GetMeshDS();
3666 if ( theElems.empty() ) {
3667 // add all faces to theElems
3668 SMDS_FaceIteratorPtr fIt = aMesh->facesIterator();
3669 while ( fIt->more() ) {
3670 const SMDS_MeshElement* face = fIt->next();
3671 theElems.insert( theElems.end(), face );
3674 // get all face ids theElems are on
3675 set< int > faceIdSet;
3676 TIDSortedElemSet::iterator itElem;
3678 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
3679 int fId = FindShape( *itElem );
3680 // check that corresponding submesh exists and a shape is face
3682 faceIdSet.find( fId ) == faceIdSet.end() &&
3683 aMesh->MeshElements( fId )) {
3684 TopoDS_Shape F = aMesh->IndexToShape( fId );
3685 if ( !F.IsNull() && F.ShapeType() == TopAbs_FACE )
3686 faceIdSet.insert( fId );
3689 faceIdSet.insert( 0 ); // to smooth elements that are not on any TopoDS_Face
3691 // ===============================================
3692 // smooth elements on each TopoDS_Face separately
3693 // ===============================================
3695 set< int >::reverse_iterator fId = faceIdSet.rbegin(); // treate 0 fId at the end
3696 for ( ; fId != faceIdSet.rend(); ++fId ) {
3697 // get face surface and submesh
3698 Handle(Geom_Surface) surface;
3699 SMESHDS_SubMesh* faceSubMesh = 0;
3701 double fToler2 = 0, f,l;
3702 double u1 = 0, u2 = 0, v1 = 0, v2 = 0;
3703 bool isUPeriodic = false, isVPeriodic = false;
3705 face = TopoDS::Face( aMesh->IndexToShape( *fId ));
3706 surface = BRep_Tool::Surface( face );
3707 faceSubMesh = aMesh->MeshElements( *fId );
3708 fToler2 = BRep_Tool::Tolerance( face );
3709 fToler2 *= fToler2 * 10.;
3710 isUPeriodic = surface->IsUPeriodic();
3713 isVPeriodic = surface->IsVPeriodic();
3716 surface->Bounds( u1, u2, v1, v2 );
3718 // ---------------------------------------------------------
3719 // for elements on a face, find movable and fixed nodes and
3720 // compute UV for them
3721 // ---------------------------------------------------------
3722 bool checkBoundaryNodes = false;
3723 bool isQuadratic = false;
3724 set<const SMDS_MeshNode*> setMovableNodes;
3725 map< const SMDS_MeshNode*, gp_XY* > uvMap, uvMap2;
3726 list< gp_XY > listUV; // uvs the 2 uvMaps refer to
3727 list< const SMDS_MeshElement* > elemsOnFace;
3729 Extrema_GenExtPS projector;
3730 GeomAdaptor_Surface surfAdaptor;
3731 if ( !surface.IsNull() ) {
3732 surfAdaptor.Load( surface );
3733 projector.Initialize( surfAdaptor, 20,20, 1e-5,1e-5 );
3735 int nbElemOnFace = 0;
3736 itElem = theElems.begin();
3737 // loop on not yet smoothed elements: look for elems on a face
3738 while ( itElem != theElems.end() ) {
3739 if ( faceSubMesh && nbElemOnFace == faceSubMesh->NbElements() )
3740 break; // all elements found
3742 const SMDS_MeshElement* elem = *itElem;
3743 if ( !elem || elem->GetType() != SMDSAbs_Face || elem->NbNodes() < 3 ||
3744 ( faceSubMesh && !faceSubMesh->Contains( elem ))) {
3748 elemsOnFace.push_back( elem );
3749 theElems.erase( itElem++ );
3753 isQuadratic = elem->IsQuadratic();
3755 // get movable nodes of elem
3756 const SMDS_MeshNode* node;
3757 SMDS_TypeOfPosition posType;
3758 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3759 int nn = 0, nbn = elem->NbNodes();
3760 if(elem->IsQuadratic())
3762 while ( nn++ < nbn ) {
3763 node = static_cast<const SMDS_MeshNode*>( itN->next() );
3764 const SMDS_PositionPtr& pos = node->GetPosition();
3765 posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
3766 if (posType != SMDS_TOP_EDGE &&
3767 posType != SMDS_TOP_VERTEX &&
3768 theFixedNodes.find( node ) == theFixedNodes.end())
3770 // check if all faces around the node are on faceSubMesh
3771 // because a node on edge may be bound to face
3772 SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
3774 if ( faceSubMesh ) {
3775 while ( eIt->more() && all ) {
3776 const SMDS_MeshElement* e = eIt->next();
3777 all = faceSubMesh->Contains( e );
3781 setMovableNodes.insert( node );
3783 checkBoundaryNodes = true;
3785 if ( posType == SMDS_TOP_3DSPACE )
3786 checkBoundaryNodes = true;
3789 if ( surface.IsNull() )
3792 // get nodes to check UV
3793 list< const SMDS_MeshNode* > uvCheckNodes;
3794 itN = elem->nodesIterator();
3795 nn = 0; nbn = elem->NbNodes();
3796 if(elem->IsQuadratic())
3798 while ( nn++ < nbn ) {
3799 node = static_cast<const SMDS_MeshNode*>( itN->next() );
3800 if ( uvMap.find( node ) == uvMap.end() )
3801 uvCheckNodes.push_back( node );
3802 // add nodes of elems sharing node
3803 // SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
3804 // while ( eIt->more() ) {
3805 // const SMDS_MeshElement* e = eIt->next();
3806 // if ( e != elem ) {
3807 // SMDS_ElemIteratorPtr nIt = e->nodesIterator();
3808 // while ( nIt->more() ) {
3809 // const SMDS_MeshNode* n =
3810 // static_cast<const SMDS_MeshNode*>( nIt->next() );
3811 // if ( uvMap.find( n ) == uvMap.end() )
3812 // uvCheckNodes.push_back( n );
3818 list< const SMDS_MeshNode* >::iterator n = uvCheckNodes.begin();
3819 for ( ; n != uvCheckNodes.end(); ++n ) {
3822 const SMDS_PositionPtr& pos = node->GetPosition();
3823 posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
3825 switch ( posType ) {
3826 case SMDS_TOP_FACE: {
3827 SMDS_FacePosition* fPos = ( SMDS_FacePosition* ) pos;
3828 uv.SetCoord( fPos->GetUParameter(), fPos->GetVParameter() );
3831 case SMDS_TOP_EDGE: {
3832 TopoDS_Shape S = aMesh->IndexToShape( node->getshapeId() );
3833 Handle(Geom2d_Curve) pcurve;
3834 if ( !S.IsNull() && S.ShapeType() == TopAbs_EDGE )
3835 pcurve = BRep_Tool::CurveOnSurface( TopoDS::Edge( S ), face, f,l );
3836 if ( !pcurve.IsNull() ) {
3837 double u = (( SMDS_EdgePosition* ) pos )->GetUParameter();
3838 uv = pcurve->Value( u ).XY();
3842 case SMDS_TOP_VERTEX: {
3843 TopoDS_Shape S = aMesh->IndexToShape( node->getshapeId() );
3844 if ( !S.IsNull() && S.ShapeType() == TopAbs_VERTEX )
3845 uv = BRep_Tool::Parameters( TopoDS::Vertex( S ), face ).XY();
3850 // check existing UV
3851 bool project = true;
3852 gp_Pnt pNode ( node->X(), node->Y(), node->Z() );
3853 double dist1 = DBL_MAX, dist2 = 0;
3854 if ( posType != SMDS_TOP_3DSPACE ) {
3855 dist1 = pNode.SquareDistance( surface->Value( uv.X(), uv.Y() ));
3856 project = dist1 > fToler2;
3858 if ( project ) { // compute new UV
3860 if ( !getClosestUV( projector, pNode, newUV )) {
3861 MESSAGE("Node Projection Failed " << node);
3865 newUV.SetX( ElCLib::InPeriod( newUV.X(), u1, u2 ));
3867 newUV.SetY( ElCLib::InPeriod( newUV.Y(), v1, v2 ));
3869 if ( posType != SMDS_TOP_3DSPACE )
3870 dist2 = pNode.SquareDistance( surface->Value( newUV.X(), newUV.Y() ));
3871 if ( dist2 < dist1 )
3875 // store UV in the map
3876 listUV.push_back( uv );
3877 uvMap.insert( make_pair( node, &listUV.back() ));
3879 } // loop on not yet smoothed elements
3881 if ( !faceSubMesh || nbElemOnFace != faceSubMesh->NbElements() )
3882 checkBoundaryNodes = true;
3884 // fix nodes on mesh boundary
3886 if ( checkBoundaryNodes ) {
3887 map< SMESH_TLink, int > linkNbMap; // how many times a link encounters in elemsOnFace
3888 map< SMESH_TLink, int >::iterator link_nb;
3889 // put all elements links to linkNbMap
3890 list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
3891 for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
3892 const SMDS_MeshElement* elem = (*elemIt);
3893 int nbn = elem->NbCornerNodes();
3894 // loop on elem links: insert them in linkNbMap
3895 for ( int iN = 0; iN < nbn; ++iN ) {
3896 const SMDS_MeshNode* n1 = elem->GetNode( iN );
3897 const SMDS_MeshNode* n2 = elem->GetNode(( iN+1 ) % nbn);
3898 SMESH_TLink link( n1, n2 );
3899 link_nb = linkNbMap.insert( make_pair( link, 0 )).first;
3903 // remove nodes that are in links encountered only once from setMovableNodes
3904 for ( link_nb = linkNbMap.begin(); link_nb != linkNbMap.end(); ++link_nb ) {
3905 if ( link_nb->second == 1 ) {
3906 setMovableNodes.erase( link_nb->first.node1() );
3907 setMovableNodes.erase( link_nb->first.node2() );
3912 // -----------------------------------------------------
3913 // for nodes on seam edge, compute one more UV ( uvMap2 );
3914 // find movable nodes linked to nodes on seam and which
3915 // are to be smoothed using the second UV ( uvMap2 )
3916 // -----------------------------------------------------
3918 set<const SMDS_MeshNode*> nodesNearSeam; // to smooth using uvMap2
3919 if ( !surface.IsNull() ) {
3920 TopExp_Explorer eExp( face, TopAbs_EDGE );
3921 for ( ; eExp.More(); eExp.Next() ) {
3922 TopoDS_Edge edge = TopoDS::Edge( eExp.Current() );
3923 if ( !BRep_Tool::IsClosed( edge, face ))
3925 SMESHDS_SubMesh* sm = aMesh->MeshElements( edge );
3926 if ( !sm ) continue;
3927 // find out which parameter varies for a node on seam
3930 Handle(Geom2d_Curve) pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
3931 if ( pcurve.IsNull() ) continue;
3932 uv1 = pcurve->Value( f );
3934 pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
3935 if ( pcurve.IsNull() ) continue;
3936 uv2 = pcurve->Value( f );
3937 int iPar = Abs( uv1.X() - uv2.X() ) > Abs( uv1.Y() - uv2.Y() ) ? 1 : 2;
3939 if ( uv1.Coord( iPar ) > uv2.Coord( iPar )) {
3940 gp_Pnt2d tmp = uv1; uv1 = uv2; uv2 = tmp;
3942 // get nodes on seam and its vertices
3943 list< const SMDS_MeshNode* > seamNodes;
3944 SMDS_NodeIteratorPtr nSeamIt = sm->GetNodes();
3945 while ( nSeamIt->more() ) {
3946 const SMDS_MeshNode* node = nSeamIt->next();
3947 if ( !isQuadratic || !IsMedium( node ))
3948 seamNodes.push_back( node );
3950 TopExp_Explorer vExp( edge, TopAbs_VERTEX );
3951 for ( ; vExp.More(); vExp.Next() ) {
3952 sm = aMesh->MeshElements( vExp.Current() );
3954 nSeamIt = sm->GetNodes();
3955 while ( nSeamIt->more() )
3956 seamNodes.push_back( nSeamIt->next() );
3959 // loop on nodes on seam
3960 list< const SMDS_MeshNode* >::iterator noSeIt = seamNodes.begin();
3961 for ( ; noSeIt != seamNodes.end(); ++noSeIt ) {
3962 const SMDS_MeshNode* nSeam = *noSeIt;
3963 map< const SMDS_MeshNode*, gp_XY* >::iterator n_uv = uvMap.find( nSeam );
3964 if ( n_uv == uvMap.end() )
3967 n_uv->second->SetCoord( iPar, uv1.Coord( iPar ));
3968 // set the second UV
3969 listUV.push_back( *n_uv->second );
3970 listUV.back().SetCoord( iPar, uv2.Coord( iPar ));
3971 if ( uvMap2.empty() )
3972 uvMap2 = uvMap; // copy the uvMap contents
3973 uvMap2[ nSeam ] = &listUV.back();
3975 // collect movable nodes linked to ones on seam in nodesNearSeam
3976 SMDS_ElemIteratorPtr eIt = nSeam->GetInverseElementIterator(SMDSAbs_Face);
3977 while ( eIt->more() ) {
3978 const SMDS_MeshElement* e = eIt->next();
3979 int nbUseMap1 = 0, nbUseMap2 = 0;
3980 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
3981 int nn = 0, nbn = e->NbNodes();
3982 if(e->IsQuadratic()) nbn = nbn/2;
3983 while ( nn++ < nbn )
3985 const SMDS_MeshNode* n =
3986 static_cast<const SMDS_MeshNode*>( nIt->next() );
3988 setMovableNodes.find( n ) == setMovableNodes.end() )
3990 // add only nodes being closer to uv2 than to uv1
3991 gp_Pnt pMid (0.5 * ( n->X() + nSeam->X() ),
3992 0.5 * ( n->Y() + nSeam->Y() ),
3993 0.5 * ( n->Z() + nSeam->Z() ));
3995 getClosestUV( projector, pMid, uv );
3996 if ( uv.Coord( iPar ) > uvMap[ n ]->Coord( iPar ) ) {
3997 nodesNearSeam.insert( n );
4003 // for centroidalSmooth all element nodes must
4004 // be on one side of a seam
4005 if ( theSmoothMethod == CENTROIDAL && nbUseMap1 && nbUseMap2 ) {
4006 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
4008 while ( nn++ < nbn ) {
4009 const SMDS_MeshNode* n =
4010 static_cast<const SMDS_MeshNode*>( nIt->next() );
4011 setMovableNodes.erase( n );
4015 } // loop on nodes on seam
4016 } // loop on edge of a face
4017 } // if ( !face.IsNull() )
4019 if ( setMovableNodes.empty() ) {
4020 MESSAGE( "Face id : " << *fId << " - NO SMOOTHING: no nodes to move!!!");
4021 continue; // goto next face
4029 double maxRatio = -1., maxDisplacement = -1.;
4030 set<const SMDS_MeshNode*>::iterator nodeToMove;
4031 for ( it = 0; it < theNbIterations; it++ ) {
4032 maxDisplacement = 0.;
4033 nodeToMove = setMovableNodes.begin();
4034 for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
4035 const SMDS_MeshNode* node = (*nodeToMove);
4036 gp_XYZ aPrevPos ( node->X(), node->Y(), node->Z() );
4039 bool map2 = ( nodesNearSeam.find( node ) != nodesNearSeam.end() );
4040 if ( theSmoothMethod == LAPLACIAN )
4041 laplacianSmooth( node, surface, map2 ? uvMap2 : uvMap );
4043 centroidalSmooth( node, surface, map2 ? uvMap2 : uvMap );
4045 // node displacement
4046 gp_XYZ aNewPos ( node->X(), node->Y(), node->Z() );
4047 Standard_Real aDispl = (aPrevPos - aNewPos).SquareModulus();
4048 if ( aDispl > maxDisplacement )
4049 maxDisplacement = aDispl;
4051 // no node movement => exit
4052 //if ( maxDisplacement < 1.e-16 ) {
4053 if ( maxDisplacement < disttol ) {
4054 MESSAGE("-- no node movement --");
4058 // check elements quality
4060 list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4061 for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
4062 const SMDS_MeshElement* elem = (*elemIt);
4063 if ( !elem || elem->GetType() != SMDSAbs_Face )
4065 SMESH::Controls::TSequenceOfXYZ aPoints;
4066 if ( aQualityFunc.GetPoints( elem, aPoints )) {
4067 double aValue = aQualityFunc.GetValue( aPoints );
4068 if ( aValue > maxRatio )
4072 if ( maxRatio <= theTgtAspectRatio ) {
4073 MESSAGE("-- quality achived --");
4076 if (it+1 == theNbIterations) {
4077 MESSAGE("-- Iteration limit exceeded --");
4079 } // smoothing iterations
4081 MESSAGE(" Face id: " << *fId <<
4082 " Nb iterstions: " << it <<
4083 " Displacement: " << maxDisplacement <<
4084 " Aspect Ratio " << maxRatio);
4086 // ---------------------------------------
4087 // new nodes positions are computed,
4088 // record movement in DS and set new UV
4089 // ---------------------------------------
4090 nodeToMove = setMovableNodes.begin();
4091 for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
4092 SMDS_MeshNode* node = const_cast< SMDS_MeshNode* > (*nodeToMove);
4093 aMesh->MoveNode( node, node->X(), node->Y(), node->Z() );
4094 map< const SMDS_MeshNode*, gp_XY* >::iterator node_uv = uvMap.find( node );
4095 if ( node_uv != uvMap.end() ) {
4096 gp_XY* uv = node_uv->second;
4098 ( SMDS_PositionPtr( new SMDS_FacePosition( uv->X(), uv->Y() )));
4102 // move medium nodes of quadratic elements
4105 SMESH_MesherHelper helper( *GetMesh() );
4106 helper.SetSubShape( face );
4107 vector<const SMDS_MeshNode*> nodes;
4109 list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
4110 for ( ; elemIt != elemsOnFace.end(); ++elemIt )
4112 const SMDS_MeshElement* QF = *elemIt;
4113 if ( QF->IsQuadratic() )
4115 nodes.assign( SMDS_MeshElement::iterator( QF->interlacedNodesElemIterator() ),
4116 SMDS_MeshElement::iterator() );
4117 nodes.push_back( nodes[0] );
4119 for (size_t i = 1; i < nodes.size(); i += 2 ) // i points to a medium node
4121 if ( !surface.IsNull() )
4123 gp_XY uv1 = helper.GetNodeUV( face, nodes[i-1], nodes[i+1], &checkUV );
4124 gp_XY uv2 = helper.GetNodeUV( face, nodes[i+1], nodes[i-1], &checkUV );
4125 gp_XY uv = helper.GetMiddleUV( surface, uv1, uv2 );
4126 xyz = surface->Value( uv.X(), uv.Y() );
4129 xyz = 0.5 * ( SMESH_TNodeXYZ( nodes[i-1] ) + SMESH_TNodeXYZ( nodes[i+1] ));
4131 if (( SMESH_TNodeXYZ( nodes[i] ) - xyz.XYZ() ).Modulus() > disttol )
4132 // we have to move a medium node
4133 aMesh->MoveNode( nodes[i], xyz.X(), xyz.Y(), xyz.Z() );
4139 } // loop on face ids
4143 //=======================================================================
4144 //function : isReverse
4145 //purpose : Return true if normal of prevNodes is not co-directied with
4146 // gp_Vec(prevNodes[iNotSame],nextNodes[iNotSame]).
4147 // iNotSame is where prevNodes and nextNodes are different.
4148 // If result is true then future volume orientation is OK
4149 //=======================================================================
4151 static bool isReverse(const SMDS_MeshElement* face,
4152 const vector<const SMDS_MeshNode*>& prevNodes,
4153 const vector<const SMDS_MeshNode*>& nextNodes,
4157 SMESH_TNodeXYZ pP = prevNodes[ iNotSame ];
4158 SMESH_TNodeXYZ pN = nextNodes[ iNotSame ];
4159 gp_XYZ extrDir( pN - pP ), faceNorm;
4160 SMESH_MeshAlgos::FaceNormal( face, faceNorm, /*normalized=*/false );
4162 return faceNorm * extrDir < 0.0;
4165 //=======================================================================
4167 * \brief Create elements by sweeping an element
4168 * \param elem - element to sweep
4169 * \param newNodesItVec - nodes generated from each node of the element
4170 * \param newElems - generated elements
4171 * \param nbSteps - number of sweeping steps
4172 * \param srcElements - to append elem for each generated element
4174 //=======================================================================
4176 void SMESH_MeshEditor::sweepElement(const SMDS_MeshElement* elem,
4177 const vector<TNodeOfNodeListMapItr> & newNodesItVec,
4178 list<const SMDS_MeshElement*>& newElems,
4180 SMESH_SequenceOfElemPtr& srcElements)
4182 //MESSAGE("sweepElement " << nbSteps);
4183 SMESHDS_Mesh* aMesh = GetMeshDS();
4185 const int nbNodes = elem->NbNodes();
4186 const int nbCorners = elem->NbCornerNodes();
4187 SMDSAbs_EntityType baseType = elem->GetEntityType(); /* it can change in case of
4188 polyhedron creation !!! */
4189 // Loop on elem nodes:
4190 // find new nodes and detect same nodes indices
4191 vector < list< const SMDS_MeshNode* >::const_iterator > itNN( nbNodes );
4192 vector<const SMDS_MeshNode*> prevNod( nbNodes );
4193 vector<const SMDS_MeshNode*> nextNod( nbNodes );
4194 vector<const SMDS_MeshNode*> midlNod( nbNodes );
4196 int iNode, nbSame = 0, nbDouble = 0, iNotSameNode = 0;
4197 vector<int> sames(nbNodes);
4198 vector<bool> isSingleNode(nbNodes);
4200 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4201 TNodeOfNodeListMapItr nnIt = newNodesItVec[ iNode ];
4202 const SMDS_MeshNode* node = nnIt->first;
4203 const list< const SMDS_MeshNode* > & listNewNodes = nnIt->second;
4204 if ( listNewNodes.empty() )
4207 itNN [ iNode ] = listNewNodes.begin();
4208 prevNod[ iNode ] = node;
4209 nextNod[ iNode ] = listNewNodes.front();
4211 isSingleNode[iNode] = (listNewNodes.size()==nbSteps); /* medium node of quadratic or
4212 corner node of linear */
4213 if ( prevNod[ iNode ] != nextNod [ iNode ])
4214 nbDouble += !isSingleNode[iNode];
4216 if( iNode < nbCorners ) { // check corners only
4217 if ( prevNod[ iNode ] == nextNod [ iNode ])
4218 sames[nbSame++] = iNode;
4220 iNotSameNode = iNode;
4224 if ( nbSame == nbNodes || nbSame > 2) {
4225 MESSAGE( " Too many same nodes of element " << elem->GetID() );
4229 if ( elem->GetType() == SMDSAbs_Face && !isReverse( elem, prevNod, nextNod, iNotSameNode ))
4231 // fix nodes order to have bottom normal external
4232 if ( baseType == SMDSEntity_Polygon )
4234 std::reverse( itNN.begin(), itNN.end() );
4235 std::reverse( prevNod.begin(), prevNod.end() );
4236 std::reverse( midlNod.begin(), midlNod.end() );
4237 std::reverse( nextNod.begin(), nextNod.end() );
4238 std::reverse( isSingleNode.begin(), isSingleNode.end() );
4242 const vector<int>& ind = SMDS_MeshCell::reverseSmdsOrder( baseType );
4243 SMDS_MeshCell::applyInterlace( ind, itNN );
4244 SMDS_MeshCell::applyInterlace( ind, prevNod );
4245 SMDS_MeshCell::applyInterlace( ind, nextNod );
4246 SMDS_MeshCell::applyInterlace( ind, midlNod );
4247 SMDS_MeshCell::applyInterlace( ind, isSingleNode );
4250 sames[nbSame] = iNotSameNode;
4251 for ( int j = 0; j <= nbSame; ++j )
4252 for ( size_t i = 0; i < ind.size(); ++i )
4253 if ( ind[i] == sames[j] )
4258 iNotSameNode = sames[nbSame];
4263 int iSameNode = 0, iBeforeSame = 0, iAfterSame = 0, iOpposSame = 0;
4265 iSameNode = sames[ nbSame-1 ];
4266 iBeforeSame = ( iSameNode + nbCorners - 1 ) % nbCorners;
4267 iAfterSame = ( iSameNode + 1 ) % nbCorners;
4268 iOpposSame = ( iSameNode - 2 < 0 ? iSameNode + 2 : iSameNode - 2 );
4271 // make new elements
4272 for (int iStep = 0; iStep < nbSteps; iStep++ )
4275 for ( iNode = 0; iNode < nbNodes; iNode++ )
4277 midlNod[ iNode ] = isSingleNode[iNode] ? 0 : *itNN[ iNode ]++;
4278 nextNod[ iNode ] = *itNN[ iNode ]++;
4281 SMDS_MeshElement* aNewElem = 0;
4282 /*if(!elem->IsPoly())*/ {
4283 switch ( baseType ) {
4285 case SMDSEntity_Node: { // sweep NODE
4286 if ( nbSame == 0 ) {
4287 if ( isSingleNode[0] )
4288 aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ] );
4290 aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ], midlNod[ 0 ] );
4296 case SMDSEntity_Edge: { // sweep EDGE
4297 if ( nbDouble == 0 )
4299 if ( nbSame == 0 ) // ---> quadrangle
4300 aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
4301 nextNod[ 1 ], nextNod[ 0 ] );
4302 else // ---> triangle
4303 aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
4304 nextNod[ iNotSameNode ] );
4306 else // ---> polygon
4308 vector<const SMDS_MeshNode*> poly_nodes;
4309 poly_nodes.push_back( prevNod[0] );
4310 poly_nodes.push_back( prevNod[1] );
4311 if ( prevNod[1] != nextNod[1] )
4313 if ( midlNod[1]) poly_nodes.push_back( midlNod[1]);
4314 poly_nodes.push_back( nextNod[1] );
4316 if ( prevNod[0] != nextNod[0] )
4318 poly_nodes.push_back( nextNod[0] );
4319 if ( midlNod[0]) poly_nodes.push_back( midlNod[0]);
4321 switch ( poly_nodes.size() ) {
4323 aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ], poly_nodes[ 2 ]);
4326 aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ],
4327 poly_nodes[ 2 ], poly_nodes[ 3 ]);
4330 aNewElem = aMesh->AddPolygonalFace (poly_nodes);
4335 case SMDSEntity_Triangle: // TRIANGLE --->
4337 if ( nbDouble > 0 ) break;
4338 if ( nbSame == 0 ) // ---> pentahedron
4339 aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
4340 nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ] );
4342 else if ( nbSame == 1 ) // ---> pyramid
4343 aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
4344 nextNod[ iAfterSame ], nextNod[ iBeforeSame ],
4345 nextNod[ iSameNode ]);
4347 else // 2 same nodes: ---> tetrahedron
4348 aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
4349 nextNod[ iNotSameNode ]);
4352 case SMDSEntity_Quad_Edge: // sweep quadratic EDGE --->
4356 if ( nbDouble+nbSame == 2 )
4358 if(nbSame==0) { // ---> quadratic quadrangle
4359 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
4360 prevNod[2], midlNod[1], nextNod[2], midlNod[0]);
4362 else { //(nbSame==1) // ---> quadratic triangle
4364 return; // medium node on axis
4366 else if(sames[0]==0)
4367 aNewElem = aMesh->AddFace(prevNod[0], nextNod[1], prevNod[1],
4368 nextNod[2], midlNod[1], prevNod[2]);
4370 aNewElem = aMesh->AddFace(prevNod[0], nextNod[0], prevNod[1],
4371 midlNod[0], nextNod[2], prevNod[2]);
4374 else if ( nbDouble == 3 )
4376 if ( nbSame == 0 ) { // ---> bi-quadratic quadrangle
4377 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
4378 prevNod[2], midlNod[1], nextNod[2], midlNod[0], midlNod[2]);
4385 case SMDSEntity_Quadrangle: { // sweep QUADRANGLE --->
4386 if ( nbDouble > 0 ) break;
4388 if ( nbSame == 0 ) // ---> hexahedron
4389 aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ], prevNod[ 3 ],
4390 nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ], nextNod[ 3 ]);
4392 else if ( nbSame == 1 ) { // ---> pyramid + pentahedron
4393 aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
4394 nextNod[ iAfterSame ], nextNod[ iBeforeSame ],
4395 nextNod[ iSameNode ]);
4396 newElems.push_back( aNewElem );
4397 aNewElem = aMesh->AddVolume (prevNod[ iAfterSame ], prevNod[ iOpposSame ],
4398 prevNod[ iBeforeSame ], nextNod[ iAfterSame ],
4399 nextNod[ iOpposSame ], nextNod[ iBeforeSame ] );
4401 else if ( nbSame == 2 ) { // ---> pentahedron
4402 if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] )
4403 // iBeforeSame is same too
4404 aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iOpposSame ],
4405 nextNod[ iOpposSame ], prevNod[ iSameNode ],
4406 prevNod[ iAfterSame ], nextNod[ iAfterSame ]);
4408 // iAfterSame is same too
4409 aNewElem = aMesh->AddVolume (prevNod[ iSameNode ], prevNod[ iBeforeSame ],
4410 nextNod[ iBeforeSame ], prevNod[ iAfterSame ],
4411 prevNod[ iOpposSame ], nextNod[ iOpposSame ]);
4415 case SMDSEntity_Quad_Triangle: // sweep (Bi)Quadratic TRIANGLE --->
4416 case SMDSEntity_BiQuad_Triangle: /* ??? */ {
4417 if ( nbDouble+nbSame != 3 ) break;
4419 // ---> pentahedron with 15 nodes
4420 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
4421 nextNod[0], nextNod[1], nextNod[2],
4422 prevNod[3], prevNod[4], prevNod[5],
4423 nextNod[3], nextNod[4], nextNod[5],
4424 midlNod[0], midlNod[1], midlNod[2]);
4426 else if(nbSame==1) {
4427 // ---> 2d order pyramid of 13 nodes
4428 int apex = iSameNode;
4429 int i0 = ( apex + 1 ) % nbCorners;
4430 int i1 = ( apex - 1 + nbCorners ) % nbCorners;
4434 aNewElem = aMesh->AddVolume(prevNod[i1], prevNod[i0],
4435 nextNod[i0], nextNod[i1], prevNod[apex],
4436 prevNod[i01], midlNod[i0],
4437 nextNod[i01], midlNod[i1],
4438 prevNod[i1a], prevNod[i0a],
4439 nextNod[i0a], nextNod[i1a]);
4441 else if(nbSame==2) {
4442 // ---> 2d order tetrahedron of 10 nodes
4443 int n1 = iNotSameNode;
4444 int n2 = ( n1 + 1 ) % nbCorners;
4445 int n3 = ( n1 + nbCorners - 1 ) % nbCorners;
4449 aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], prevNod[n3], nextNod[n1],
4450 prevNod[n12], prevNod[n23], prevNod[n31],
4451 midlNod[n1], nextNod[n12], nextNod[n31]);
4455 case SMDSEntity_Quad_Quadrangle: { // sweep Quadratic QUADRANGLE --->
4457 if ( nbDouble != 4 ) break;
4458 // ---> hexahedron with 20 nodes
4459 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
4460 nextNod[0], nextNod[1], nextNod[2], nextNod[3],
4461 prevNod[4], prevNod[5], prevNod[6], prevNod[7],
4462 nextNod[4], nextNod[5], nextNod[6], nextNod[7],
4463 midlNod[0], midlNod[1], midlNod[2], midlNod[3]);
4465 else if(nbSame==1) {
4466 // ---> pyramid + pentahedron - can not be created since it is needed
4467 // additional middle node at the center of face
4468 INFOS( " Sweep for face " << elem->GetID() << " can not be created" );
4471 else if( nbSame == 2 ) {
4472 if ( nbDouble != 2 ) break;
4473 // ---> 2d order Pentahedron with 15 nodes
4475 if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] ) {
4476 // iBeforeSame is same too
4483 // iAfterSame is same too
4493 aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], nextNod[n2],
4494 prevNod[n4], prevNod[n5], nextNod[n5],
4495 prevNod[n12], midlNod[n2], nextNod[n12],
4496 prevNod[n45], midlNod[n5], nextNod[n45],
4497 prevNod[n14], prevNod[n25], nextNod[n25]);
4501 case SMDSEntity_BiQuad_Quadrangle: { // sweep BiQuadratic QUADRANGLE --->
4503 if( nbSame == 0 && nbDouble == 9 ) {
4504 // ---> tri-quadratic hexahedron with 27 nodes
4505 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
4506 nextNod[0], nextNod[1], nextNod[2], nextNod[3],
4507 prevNod[4], prevNod[5], prevNod[6], prevNod[7],
4508 nextNod[4], nextNod[5], nextNod[6], nextNod[7],
4509 midlNod[0], midlNod[1], midlNod[2], midlNod[3],
4510 prevNod[8], // bottom center
4511 midlNod[4], midlNod[5], midlNod[6], midlNod[7],
4512 nextNod[8], // top center
4513 midlNod[8]);// elem center
4521 case SMDSEntity_Polygon: { // sweep POLYGON
4523 if ( nbNodes == 6 && nbSame == 0 && nbDouble == 0 ) {
4524 // ---> hexagonal prism
4525 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
4526 prevNod[3], prevNod[4], prevNod[5],
4527 nextNod[0], nextNod[1], nextNod[2],
4528 nextNod[3], nextNod[4], nextNod[5]);
4532 case SMDSEntity_Ball:
4540 if ( !aNewElem && elem->GetType() == SMDSAbs_Face ) // try to create a polyherdal prism
4542 if ( baseType != SMDSEntity_Polygon )
4544 const std::vector<int>& ind = SMDS_MeshCell::interlacedSmdsOrder(baseType);
4545 SMDS_MeshCell::applyInterlace( ind, prevNod );
4546 SMDS_MeshCell::applyInterlace( ind, nextNod );
4547 SMDS_MeshCell::applyInterlace( ind, midlNod );
4548 SMDS_MeshCell::applyInterlace( ind, itNN );
4549 SMDS_MeshCell::applyInterlace( ind, isSingleNode );
4550 baseType = SMDSEntity_Polygon; // WARNING: change baseType !!!!
4552 vector<const SMDS_MeshNode*> polyedre_nodes (nbNodes*2 + 4*nbNodes);
4553 vector<int> quantities (nbNodes + 2);
4554 polyedre_nodes.clear();
4558 for (int inode = 0; inode < nbNodes; inode++)
4559 polyedre_nodes.push_back( prevNod[inode] );
4560 quantities.push_back( nbNodes );
4563 polyedre_nodes.push_back( nextNod[0] );
4564 for (int inode = nbNodes; inode-1; --inode )
4565 polyedre_nodes.push_back( nextNod[inode-1] );
4566 quantities.push_back( nbNodes );
4569 for (int iface = 0; iface < nbNodes; iface++)
4571 const int prevNbNodes = polyedre_nodes.size();
4572 int inextface = (iface+1) % nbNodes;
4573 polyedre_nodes.push_back( prevNod[inextface] );
4574 polyedre_nodes.push_back( prevNod[iface] );
4575 if ( prevNod[iface] != nextNod[iface] )
4577 if ( midlNod[ iface ]) polyedre_nodes.push_back( midlNod[ iface ]);
4578 polyedre_nodes.push_back( nextNod[iface] );
4580 if ( prevNod[inextface] != nextNod[inextface] )
4582 polyedre_nodes.push_back( nextNod[inextface] );
4583 if ( midlNod[ inextface ]) polyedre_nodes.push_back( midlNod[ inextface ]);
4585 const int nbFaceNodes = polyedre_nodes.size() - prevNbNodes;
4586 if ( nbFaceNodes > 2 )
4587 quantities.push_back( nbFaceNodes );
4588 else // degenerated face
4589 polyedre_nodes.resize( prevNbNodes );
4591 aNewElem = aMesh->AddPolyhedralVolume (polyedre_nodes, quantities);
4595 newElems.push_back( aNewElem );
4596 myLastCreatedElems.Append(aNewElem);
4597 srcElements.Append( elem );
4600 // set new prev nodes
4601 for ( iNode = 0; iNode < nbNodes; iNode++ )
4602 prevNod[ iNode ] = nextNod[ iNode ];
4607 //=======================================================================
4609 * \brief Create 1D and 2D elements around swept elements
4610 * \param mapNewNodes - source nodes and ones generated from them
4611 * \param newElemsMap - source elements and ones generated from them
4612 * \param elemNewNodesMap - nodes generated from each node of each element
4613 * \param elemSet - all swept elements
4614 * \param nbSteps - number of sweeping steps
4615 * \param srcElements - to append elem for each generated element
4617 //=======================================================================
4619 void SMESH_MeshEditor::makeWalls (TNodeOfNodeListMap & mapNewNodes,
4620 TTElemOfElemListMap & newElemsMap,
4621 TElemOfVecOfNnlmiMap & elemNewNodesMap,
4622 TIDSortedElemSet& elemSet,
4624 SMESH_SequenceOfElemPtr& srcElements)
4626 ASSERT( newElemsMap.size() == elemNewNodesMap.size() );
4627 SMESHDS_Mesh* aMesh = GetMeshDS();
4629 // Find nodes belonging to only one initial element - sweep them into edges.
4631 TNodeOfNodeListMapItr nList = mapNewNodes.begin();
4632 for ( ; nList != mapNewNodes.end(); nList++ )
4634 const SMDS_MeshNode* node =
4635 static_cast<const SMDS_MeshNode*>( nList->first );
4636 if ( newElemsMap.count( node ))
4637 continue; // node was extruded into edge
4638 SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator();
4639 int nbInitElems = 0;
4640 const SMDS_MeshElement* el = 0;
4641 SMDSAbs_ElementType highType = SMDSAbs_Edge; // count most complex elements only
4642 while ( eIt->more() && nbInitElems < 2 ) {
4644 SMDSAbs_ElementType type = el->GetType();
4645 if ( type == SMDSAbs_Volume || type < highType ) continue;
4646 if ( type > highType ) {
4650 nbInitElems += elemSet.count(el);
4652 if ( nbInitElems < 2 ) {
4653 bool NotCreateEdge = el && el->IsMediumNode(node);
4654 if(!NotCreateEdge) {
4655 vector<TNodeOfNodeListMapItr> newNodesItVec( 1, nList );
4656 list<const SMDS_MeshElement*> newEdges;
4657 sweepElement( node, newNodesItVec, newEdges, nbSteps, srcElements );
4662 // Make a ceiling for each element ie an equal element of last new nodes.
4663 // Find free links of faces - make edges and sweep them into faces.
4665 TTElemOfElemListMap::iterator itElem = newElemsMap.begin();
4666 TElemOfVecOfNnlmiMap::iterator itElemNodes = elemNewNodesMap.begin();
4667 for ( ; itElem != newElemsMap.end(); itElem++, itElemNodes++ )
4669 const SMDS_MeshElement* elem = itElem->first;
4670 vector<TNodeOfNodeListMapItr>& vecNewNodes = itElemNodes->second;
4672 if(itElem->second.size()==0) continue;
4674 const bool isQuadratic = elem->IsQuadratic();
4676 if ( elem->GetType() == SMDSAbs_Edge ) {
4677 // create a ceiling edge
4678 if ( !isQuadratic ) {
4679 if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
4680 vecNewNodes[ 1 ]->second.back())) {
4681 myLastCreatedElems.Append(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
4682 vecNewNodes[ 1 ]->second.back()));
4683 srcElements.Append( elem );
4687 if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
4688 vecNewNodes[ 1 ]->second.back(),
4689 vecNewNodes[ 2 ]->second.back())) {
4690 myLastCreatedElems.Append(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
4691 vecNewNodes[ 1 ]->second.back(),
4692 vecNewNodes[ 2 ]->second.back()));
4693 srcElements.Append( elem );
4697 if ( elem->GetType() != SMDSAbs_Face )
4700 bool hasFreeLinks = false;
4702 TIDSortedElemSet avoidSet;
4703 avoidSet.insert( elem );
4705 set<const SMDS_MeshNode*> aFaceLastNodes;
4706 int iNode, nbNodes = vecNewNodes.size();
4707 if ( !isQuadratic ) {
4708 // loop on the face nodes
4709 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4710 aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4711 // look for free links of the face
4712 int iNext = ( iNode + 1 == nbNodes ) ? 0 : iNode + 1;
4713 const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
4714 const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
4715 // check if a link n1-n2 is free
4716 if ( ! SMESH_MeshAlgos::FindFaceInSet ( n1, n2, elemSet, avoidSet )) {
4717 hasFreeLinks = true;
4718 // make a new edge and a ceiling for a new edge
4719 const SMDS_MeshElement* edge;
4720 if ( ! ( edge = aMesh->FindEdge( n1, n2 ))) {
4721 myLastCreatedElems.Append( edge = aMesh->AddEdge( n1, n2 )); // free link edge
4722 srcElements.Append( myLastCreatedElems.Last() );
4724 n1 = vecNewNodes[ iNode ]->second.back();
4725 n2 = vecNewNodes[ iNext ]->second.back();
4726 if ( !aMesh->FindEdge( n1, n2 )) {
4727 myLastCreatedElems.Append(aMesh->AddEdge( n1, n2 )); // new edge ceiling
4728 srcElements.Append( edge );
4733 else { // elem is quadratic face
4734 int nbn = nbNodes/2;
4735 for ( iNode = 0; iNode < nbn; iNode++ ) {
4736 aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4737 int iNext = ( iNode + 1 == nbn ) ? 0 : iNode + 1;
4738 const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
4739 const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
4740 const SMDS_MeshNode* n3 = vecNewNodes[ iNode+nbn ]->first;
4741 // check if a link is free
4742 if ( ! SMESH_MeshAlgos::FindFaceInSet ( n1, n2, elemSet, avoidSet ) &&
4743 ! SMESH_MeshAlgos::FindFaceInSet ( n1, n3, elemSet, avoidSet ) &&
4744 ! SMESH_MeshAlgos::FindFaceInSet ( n3, n2, elemSet, avoidSet ) ) {
4745 hasFreeLinks = true;
4746 // make an edge and a ceiling for a new edge
4748 if ( !aMesh->FindEdge( n1, n2, n3 )) {
4749 myLastCreatedElems.Append(aMesh->AddEdge( n1, n2, n3 )); // free link edge
4750 srcElements.Append( elem );
4752 n1 = vecNewNodes[ iNode ]->second.back();
4753 n2 = vecNewNodes[ iNext ]->second.back();
4754 n3 = vecNewNodes[ iNode+nbn ]->second.back();
4755 if ( !aMesh->FindEdge( n1, n2, n3 )) {
4756 myLastCreatedElems.Append(aMesh->AddEdge( n1, n2, n3 )); // ceiling edge
4757 srcElements.Append( elem );
4761 for ( iNode = nbn; iNode < nbNodes; iNode++ ) {
4762 aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4766 // sweep free links into faces
4768 if ( hasFreeLinks ) {
4769 list<const SMDS_MeshElement*> & newVolumes = itElem->second;
4770 int iVol, volNb, nbVolumesByStep = newVolumes.size() / nbSteps;
4772 set<const SMDS_MeshNode*> initNodeSet, topNodeSet, faceNodeSet;
4773 set<const SMDS_MeshNode*> initNodeSetNoCenter/*, topNodeSetNoCenter*/;
4774 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4775 initNodeSet.insert( vecNewNodes[ iNode ]->first );
4776 topNodeSet .insert( vecNewNodes[ iNode ]->second.back() );
4778 if ( isQuadratic && nbNodes % 2 ) { // node set for the case of a biquadratic
4779 initNodeSetNoCenter = initNodeSet; // swept face and a not biquadratic volume
4780 initNodeSetNoCenter.erase( vecNewNodes.back()->first );
4782 for ( volNb = 0; volNb < nbVolumesByStep; volNb++ ) {
4783 list<const SMDS_MeshElement*>::iterator v = newVolumes.begin();
4784 std::advance( v, volNb );
4785 // find indices of free faces of a volume and their source edges
4786 list< int > freeInd;
4787 list< const SMDS_MeshElement* > srcEdges; // source edges of free faces
4788 SMDS_VolumeTool vTool( *v, /*ignoreCentralNodes=*/false );
4789 int iF, nbF = vTool.NbFaces();
4790 for ( iF = 0; iF < nbF; iF ++ ) {
4791 if (vTool.IsFreeFace( iF ) &&
4792 vTool.GetFaceNodes( iF, faceNodeSet ) &&
4793 initNodeSet != faceNodeSet) // except an initial face
4795 if ( nbSteps == 1 && faceNodeSet == topNodeSet )
4797 if ( faceNodeSet == initNodeSetNoCenter )
4799 freeInd.push_back( iF );
4800 // find source edge of a free face iF
4801 vector<const SMDS_MeshNode*> commonNodes; // shared by the initial and free faces
4802 commonNodes.resize( initNodeSet.size(), NULL ); // avoid spoiling memory
4803 std::set_intersection( faceNodeSet.begin(), faceNodeSet.end(),
4804 initNodeSet.begin(), initNodeSet.end(),
4805 commonNodes.begin());
4806 if ( (*v)->IsQuadratic() )
4807 srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1],commonNodes[2]));
4809 srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1]));
4811 if ( !srcEdges.back() )
4813 cout << "SMESH_MeshEditor::makeWalls(), no source edge found for a free face #"
4814 << iF << " of volume #" << vTool.ID() << endl;
4819 if ( freeInd.empty() )
4822 // create faces for all steps;
4823 // if such a face has been already created by sweep of edge,
4824 // assure that its orientation is OK
4825 for ( int iStep = 0; iStep < nbSteps; iStep++ ) {
4826 vTool.Set( *v, /*ignoreCentralNodes=*/false );
4827 vTool.SetExternalNormal();
4828 const int nextShift = vTool.IsForward() ? +1 : -1;
4829 list< int >::iterator ind = freeInd.begin();
4830 list< const SMDS_MeshElement* >::iterator srcEdge = srcEdges.begin();
4831 for ( ; ind != freeInd.end(); ++ind, ++srcEdge ) // loop on free faces
4833 const SMDS_MeshNode** nodes = vTool.GetFaceNodes( *ind );
4834 int nbn = vTool.NbFaceNodes( *ind );
4835 const SMDS_MeshElement * f = 0;
4836 if ( nbn == 3 ) ///// triangle
4838 f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ]);
4840 nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
4842 const SMDS_MeshNode* newOrder[3] = { nodes[ 1 - nextShift ],
4844 nodes[ 1 + nextShift ] };
4846 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4848 myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
4852 else if ( nbn == 4 ) ///// quadrangle
4854 f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ], nodes[ 3 ]);
4856 nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
4858 const SMDS_MeshNode* newOrder[4] = { nodes[ 0 ], nodes[ 2-nextShift ],
4859 nodes[ 2 ], nodes[ 2+nextShift ] };
4861 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4863 myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
4864 newOrder[ 2 ], newOrder[ 3 ]));
4867 else if ( nbn == 6 && isQuadratic ) /////// quadratic triangle
4869 f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[1], nodes[3], nodes[5] );
4871 nodes[2] != f->GetNodeWrap( f->GetNodeIndex( nodes[0] ) + 2*nextShift ))
4873 const SMDS_MeshNode* newOrder[6] = { nodes[2 - 2*nextShift],
4875 nodes[2 + 2*nextShift],
4876 nodes[3 - 2*nextShift],
4878 nodes[3 + 2*nextShift]};
4880 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4882 myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ],
4890 else if ( nbn == 8 && isQuadratic ) /////// quadratic quadrangle
4892 f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[6],
4893 nodes[1], nodes[3], nodes[5], nodes[7] );
4895 nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
4897 const SMDS_MeshNode* newOrder[8] = { nodes[0],
4898 nodes[4 - 2*nextShift],
4900 nodes[4 + 2*nextShift],
4902 nodes[5 - 2*nextShift],
4904 nodes[5 + 2*nextShift] };
4906 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4908 myLastCreatedElems.Append(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
4909 newOrder[ 2 ], newOrder[ 3 ],
4910 newOrder[ 4 ], newOrder[ 5 ],
4911 newOrder[ 6 ], newOrder[ 7 ]));
4914 else if ( nbn == 9 && isQuadratic ) /////// bi-quadratic quadrangle
4916 f = aMesh->FindElement( vector<const SMDS_MeshNode*>( nodes, nodes+nbn ),
4917 SMDSAbs_Face, /*noMedium=*/false);
4919 nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
4921 const SMDS_MeshNode* newOrder[9] = { nodes[0],
4922 nodes[4 - 2*nextShift],
4924 nodes[4 + 2*nextShift],
4926 nodes[5 - 2*nextShift],
4928 nodes[5 + 2*nextShift],
4931 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4933 myLastCreatedElems.Append(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
4934 newOrder[ 2 ], newOrder[ 3 ],
4935 newOrder[ 4 ], newOrder[ 5 ],
4936 newOrder[ 6 ], newOrder[ 7 ],
4940 else //////// polygon
4942 vector<const SMDS_MeshNode*> polygon_nodes ( nodes, nodes+nbn );
4943 const SMDS_MeshFace * f = aMesh->FindFace( polygon_nodes );
4945 nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + nextShift ))
4947 if ( !vTool.IsForward() )
4948 std::reverse( polygon_nodes.begin(), polygon_nodes.end());
4950 aMesh->ChangeElementNodes( f, &polygon_nodes[0], nbn );
4952 AddElement(polygon_nodes, SMDSAbs_Face, polygon_nodes.size()>4);
4956 while ( srcElements.Length() < myLastCreatedElems.Length() )
4957 srcElements.Append( *srcEdge );
4959 } // loop on free faces
4961 // go to the next volume
4963 while ( iVol++ < nbVolumesByStep ) v++;
4966 } // loop on volumes of one step
4967 } // sweep free links into faces
4969 // Make a ceiling face with a normal external to a volume
4971 // use SMDS_VolumeTool to get a correctly ordered nodes of a ceiling face
4972 SMDS_VolumeTool lastVol( itElem->second.back(), /*ignoreCentralNodes=*/false );
4973 int iF = lastVol.GetFaceIndex( aFaceLastNodes );
4975 if ( iF < 0 && isQuadratic && nbNodes % 2 ) { // remove a central node of biquadratic
4976 aFaceLastNodes.erase( vecNewNodes.back()->second.back() );
4977 iF = lastVol.GetFaceIndex( aFaceLastNodes );
4980 lastVol.SetExternalNormal();
4981 const SMDS_MeshNode** nodes = lastVol.GetFaceNodes( iF );
4982 int nbn = lastVol.NbFaceNodes( iF );
4983 // we do not use this->AddElement() because nodes are interlaced
4984 vector<const SMDS_MeshNode*> nodeVec( nodes, nodes+nbn );
4985 if ( !hasFreeLinks ||
4986 !aMesh->FindElement( nodeVec, SMDSAbs_Face, /*noMedium=*/false) )
4989 myLastCreatedElems.Append(aMesh->AddFace( nodes[0], nodes[1], nodes[2] ));
4991 else if ( nbn == 4 )
4992 myLastCreatedElems.Append(aMesh->AddFace( nodes[0], nodes[1], nodes[2], nodes[3]));
4994 else if ( nbn == 6 && isQuadratic )
4995 myLastCreatedElems.Append(aMesh->AddFace( nodes[0], nodes[2], nodes[4],
4996 nodes[1], nodes[3], nodes[5]));
4997 else if ( nbn == 7 && isQuadratic )
4998 myLastCreatedElems.Append(aMesh->AddFace( nodes[0], nodes[2], nodes[4],
4999 nodes[1], nodes[3], nodes[5], nodes[6]));
5000 else if ( nbn == 8 && isQuadratic )
5001 myLastCreatedElems.Append(aMesh->AddFace( nodes[0], nodes[2], nodes[4], nodes[6],
5002 nodes[1], nodes[3], nodes[5], nodes[7]));
5003 else if ( nbn == 9 && isQuadratic )
5004 myLastCreatedElems.Append(aMesh->AddFace( nodes[0], nodes[2], nodes[4], nodes[6],
5005 nodes[1], nodes[3], nodes[5], nodes[7],
5008 myLastCreatedElems.Append(aMesh->AddPolygonalFace( nodeVec ));
5010 while ( srcElements.Length() < myLastCreatedElems.Length() )
5011 srcElements.Append( elem );
5014 } // loop on swept elements
5017 //=======================================================================
5018 //function : RotationSweep
5020 //=======================================================================
5022 SMESH_MeshEditor::PGroupIDs
5023 SMESH_MeshEditor::RotationSweep(TIDSortedElemSet & theElems,
5024 const gp_Ax1& theAxis,
5025 const double theAngle,
5026 const int theNbSteps,
5027 const double theTol,
5028 const bool theMakeGroups,
5029 const bool theMakeWalls)
5031 myLastCreatedElems.Clear();
5032 myLastCreatedNodes.Clear();
5034 // source elements for each generated one
5035 SMESH_SequenceOfElemPtr srcElems, srcNodes;
5037 MESSAGE( "RotationSweep()");
5039 aTrsf.SetRotation( theAxis, theAngle );
5041 aTrsf2.SetRotation( theAxis, theAngle/2. );
5043 gp_Lin aLine( theAxis );
5044 double aSqTol = theTol * theTol;
5046 SMESHDS_Mesh* aMesh = GetMeshDS();
5048 TNodeOfNodeListMap mapNewNodes;
5049 TElemOfVecOfNnlmiMap mapElemNewNodes;
5050 TTElemOfElemListMap newElemsMap;
5052 const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
5053 myMesh->NbFaces(ORDER_QUADRATIC) +
5054 myMesh->NbVolumes(ORDER_QUADRATIC) );
5056 TIDSortedElemSet::iterator itElem;
5057 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
5058 const SMDS_MeshElement* elem = *itElem;
5059 if ( !elem || elem->GetType() == SMDSAbs_Volume )
5061 vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5062 newNodesItVec.reserve( elem->NbNodes() );
5064 // loop on elem nodes
5065 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5066 while ( itN->more() )
5068 // check if a node has been already sweeped
5069 const SMDS_MeshNode* node = cast2Node( itN->next() );
5071 gp_XYZ aXYZ( node->X(), node->Y(), node->Z() );
5073 aXYZ.Coord( coord[0], coord[1], coord[2] );
5074 bool isOnAxis = ( aLine.SquareDistance( aXYZ ) <= aSqTol );
5076 TNodeOfNodeListMapItr nIt =
5077 mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5078 list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5079 if ( listNewNodes.empty() )
5081 // check if we are to create medium nodes between corner ones
5082 bool needMediumNodes = false;
5083 if ( isQuadraticMesh )
5085 SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
5086 while (it->more() && !needMediumNodes )
5088 const SMDS_MeshElement* invElem = it->next();
5089 if ( invElem != elem && !theElems.count( invElem )) continue;
5090 needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
5091 if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
5092 needMediumNodes = true;
5097 const SMDS_MeshNode * newNode = node;
5098 for ( int i = 0; i < theNbSteps; i++ ) {
5100 if ( needMediumNodes ) // create a medium node
5102 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
5103 newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5104 myLastCreatedNodes.Append(newNode);
5105 srcNodes.Append( node );
5106 listNewNodes.push_back( newNode );
5107 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
5110 aTrsf.Transforms( coord[0], coord[1], coord[2] );
5112 // create a corner node
5113 newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5114 myLastCreatedNodes.Append(newNode);
5115 srcNodes.Append( node );
5116 listNewNodes.push_back( newNode );
5119 listNewNodes.push_back( newNode );
5120 // if ( needMediumNodes )
5121 // listNewNodes.push_back( newNode );
5125 newNodesItVec.push_back( nIt );
5127 // make new elements
5128 sweepElement( elem, newNodesItVec, newElemsMap[elem], theNbSteps, srcElems );
5132 makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElems, theNbSteps, srcElems );
5134 PGroupIDs newGroupIDs;
5135 if ( theMakeGroups )
5136 newGroupIDs = generateGroups( srcNodes, srcElems, "rotated");
5142 //=======================================================================
5143 //function : CreateNode
5145 //=======================================================================
5146 const SMDS_MeshNode* SMESH_MeshEditor::CreateNode(const double x,
5149 const double tolnode,
5150 SMESH_SequenceOfNode& aNodes)
5152 // myLastCreatedElems.Clear();
5153 // myLastCreatedNodes.Clear();
5156 SMESHDS_Mesh * aMesh = myMesh->GetMeshDS();
5158 // try to search in sequence of existing nodes
5159 // if aNodes.Length()>0 we 'nave to use given sequence
5160 // else - use all nodes of mesh
5161 if(aNodes.Length()>0) {
5163 for(i=1; i<=aNodes.Length(); i++) {
5164 gp_Pnt P2(aNodes.Value(i)->X(),aNodes.Value(i)->Y(),aNodes.Value(i)->Z());
5165 if(P1.Distance(P2)<tolnode)
5166 return aNodes.Value(i);
5170 SMDS_NodeIteratorPtr itn = aMesh->nodesIterator();
5171 while(itn->more()) {
5172 const SMDS_MeshNode* aN = static_cast<const SMDS_MeshNode*> (itn->next());
5173 gp_Pnt P2(aN->X(),aN->Y(),aN->Z());
5174 if(P1.Distance(P2)<tolnode)
5179 // create new node and return it
5180 const SMDS_MeshNode* NewNode = aMesh->AddNode(x,y,z);
5181 //myLastCreatedNodes.Append(NewNode);
5186 //=======================================================================
5187 //function : ExtrusionSweep
5189 //=======================================================================
5191 SMESH_MeshEditor::PGroupIDs
5192 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet & theElems,
5193 const gp_Vec& theStep,
5194 const int theNbSteps,
5195 TTElemOfElemListMap& newElemsMap,
5196 const bool theMakeGroups,
5198 const double theTolerance)
5200 ExtrusParam aParams;
5201 aParams.myDir = gp_Dir(theStep);
5202 aParams.myNodes.Clear();
5203 aParams.mySteps = new TColStd_HSequenceOfReal;
5205 for(i=1; i<=theNbSteps; i++)
5206 aParams.mySteps->Append(theStep.Magnitude());
5209 ExtrusionSweep(theElems,aParams,newElemsMap,theMakeGroups,theFlags,theTolerance);
5213 //=======================================================================
5214 //function : ExtrusionSweep
5216 //=======================================================================
5218 SMESH_MeshEditor::PGroupIDs
5219 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet & theElems,
5220 ExtrusParam& theParams,
5221 TTElemOfElemListMap& newElemsMap,
5222 const bool theMakeGroups,
5224 const double theTolerance)
5226 myLastCreatedElems.Clear();
5227 myLastCreatedNodes.Clear();
5229 // source elements for each generated one
5230 SMESH_SequenceOfElemPtr srcElems, srcNodes;
5232 SMESHDS_Mesh* aMesh = GetMeshDS();
5234 int nbsteps = theParams.mySteps->Length();
5236 TNodeOfNodeListMap mapNewNodes;
5237 //TNodeOfNodeVecMap mapNewNodes;
5238 TElemOfVecOfNnlmiMap mapElemNewNodes;
5239 //TElemOfVecOfMapNodesMap mapElemNewNodes;
5241 const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
5242 myMesh->NbFaces(ORDER_QUADRATIC) +
5243 myMesh->NbVolumes(ORDER_QUADRATIC) );
5245 TIDSortedElemSet::iterator itElem;
5246 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
5247 // check element type
5248 const SMDS_MeshElement* elem = *itElem;
5249 if ( !elem || elem->GetType() == SMDSAbs_Volume )
5252 vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5253 newNodesItVec.reserve( elem->NbNodes() );
5255 // loop on elem nodes
5256 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5257 while ( itN->more() )
5259 // check if a node has been already sweeped
5260 const SMDS_MeshNode* node = cast2Node( itN->next() );
5261 TNodeOfNodeListMap::iterator nIt =
5262 mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5263 list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5264 if ( listNewNodes.empty() )
5268 // check if we are to create medium nodes between corner ones
5269 bool needMediumNodes = false;
5270 if ( isQuadraticMesh )
5272 SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
5273 while (it->more() && !needMediumNodes )
5275 const SMDS_MeshElement* invElem = it->next();
5276 if ( invElem != elem && !theElems.count( invElem )) continue;
5277 needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
5278 if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
5279 needMediumNodes = true;
5283 double coord[] = { node->X(), node->Y(), node->Z() };
5284 for ( int i = 0; i < nbsteps; i++ )
5286 if ( needMediumNodes ) // create a medium node
5288 double x = coord[0] + theParams.myDir.X()*theParams.mySteps->Value(i+1)/2.;
5289 double y = coord[1] + theParams.myDir.Y()*theParams.mySteps->Value(i+1)/2.;
5290 double z = coord[2] + theParams.myDir.Z()*theParams.mySteps->Value(i+1)/2.;
5291 if( theFlags & EXTRUSION_FLAG_SEW ) {
5292 const SMDS_MeshNode * newNode = CreateNode(x, y, z,
5293 theTolerance, theParams.myNodes);
5294 listNewNodes.push_back( newNode );
5297 const SMDS_MeshNode * newNode = aMesh->AddNode(x, y, z);
5298 myLastCreatedNodes.Append(newNode);
5299 srcNodes.Append( node );
5300 listNewNodes.push_back( newNode );
5303 // create a corner node
5304 coord[0] = coord[0] + theParams.myDir.X()*theParams.mySteps->Value(i+1);
5305 coord[1] = coord[1] + theParams.myDir.Y()*theParams.mySteps->Value(i+1);
5306 coord[2] = coord[2] + theParams.myDir.Z()*theParams.mySteps->Value(i+1);
5307 if( theFlags & EXTRUSION_FLAG_SEW ) {
5308 const SMDS_MeshNode * newNode = CreateNode(coord[0], coord[1], coord[2],
5309 theTolerance, theParams.myNodes);
5310 listNewNodes.push_back( newNode );
5313 const SMDS_MeshNode * newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5314 myLastCreatedNodes.Append(newNode);
5315 srcNodes.Append( node );
5316 listNewNodes.push_back( newNode );
5320 newNodesItVec.push_back( nIt );
5322 // make new elements
5323 sweepElement( elem, newNodesItVec, newElemsMap[elem], nbsteps, srcElems );
5326 if( theFlags & EXTRUSION_FLAG_BOUNDARY ) {
5327 makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElems, nbsteps, srcElems );
5329 PGroupIDs newGroupIDs;
5330 if ( theMakeGroups )
5331 newGroupIDs = generateGroups( srcNodes, srcElems, "extruded");
5336 //=======================================================================
5337 //function : ExtrusionAlongTrack
5339 //=======================================================================
5340 SMESH_MeshEditor::Extrusion_Error
5341 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet & theElements,
5342 SMESH_subMesh* theTrack,
5343 const SMDS_MeshNode* theN1,
5344 const bool theHasAngles,
5345 list<double>& theAngles,
5346 const bool theLinearVariation,
5347 const bool theHasRefPoint,
5348 const gp_Pnt& theRefPoint,
5349 const bool theMakeGroups)
5351 MESSAGE("ExtrusionAlongTrack");
5352 myLastCreatedElems.Clear();
5353 myLastCreatedNodes.Clear();
5356 std::list<double> aPrms;
5357 TIDSortedElemSet::iterator itElem;
5360 TopoDS_Edge aTrackEdge;
5361 TopoDS_Vertex aV1, aV2;
5363 SMDS_ElemIteratorPtr aItE;
5364 SMDS_NodeIteratorPtr aItN;
5365 SMDSAbs_ElementType aTypeE;
5367 TNodeOfNodeListMap mapNewNodes;
5370 aNbE = theElements.size();
5373 return EXTR_NO_ELEMENTS;
5375 // 1.1 Track Pattern
5378 SMESHDS_SubMesh* pSubMeshDS = theTrack->GetSubMeshDS();
5380 aItE = pSubMeshDS->GetElements();
5381 while ( aItE->more() ) {
5382 const SMDS_MeshElement* pE = aItE->next();
5383 aTypeE = pE->GetType();
5384 // Pattern must contain links only
5385 if ( aTypeE != SMDSAbs_Edge )
5386 return EXTR_PATH_NOT_EDGE;
5389 list<SMESH_MeshEditor_PathPoint> fullList;
5391 const TopoDS_Shape& aS = theTrack->GetSubShape();
5392 // Sub-shape for the Pattern must be an Edge or Wire
5393 if( aS.ShapeType() == TopAbs_EDGE ) {
5394 aTrackEdge = TopoDS::Edge( aS );
5395 // the Edge must not be degenerated
5396 if ( SMESH_Algo::isDegenerated( aTrackEdge ) )
5397 return EXTR_BAD_PATH_SHAPE;
5398 TopExp::Vertices( aTrackEdge, aV1, aV2 );
5399 aItN = theTrack->GetFather()->GetSubMesh( aV1 )->GetSubMeshDS()->GetNodes();
5400 const SMDS_MeshNode* aN1 = aItN->next();
5401 aItN = theTrack->GetFather()->GetSubMesh( aV2 )->GetSubMeshDS()->GetNodes();
5402 const SMDS_MeshNode* aN2 = aItN->next();
5403 // starting node must be aN1 or aN2
5404 if ( !( aN1 == theN1 || aN2 == theN1 ) )
5405 return EXTR_BAD_STARTING_NODE;
5406 aItN = pSubMeshDS->GetNodes();
5407 while ( aItN->more() ) {
5408 const SMDS_MeshNode* pNode = aItN->next();
5409 const SMDS_EdgePosition* pEPos =
5410 static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
5411 double aT = pEPos->GetUParameter();
5412 aPrms.push_back( aT );
5414 //Extrusion_Error err =
5415 MakeEdgePathPoints(aPrms, aTrackEdge, (aN1==theN1), fullList);
5416 } else if( aS.ShapeType() == TopAbs_WIRE ) {
5417 list< SMESH_subMesh* > LSM;
5418 TopTools_SequenceOfShape Edges;
5419 SMESH_subMeshIteratorPtr itSM = theTrack->getDependsOnIterator(false,true);
5420 while(itSM->more()) {
5421 SMESH_subMesh* SM = itSM->next();
5423 const TopoDS_Shape& aS = SM->GetSubShape();
5426 list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
5427 int startNid = theN1->GetID();
5428 TColStd_MapOfInteger UsedNums;
5430 int NbEdges = Edges.Length();
5432 for(; i<=NbEdges; i++) {
5434 list< SMESH_subMesh* >::iterator itLSM = LSM.begin();
5435 for(; itLSM!=LSM.end(); itLSM++) {
5437 if(UsedNums.Contains(k)) continue;
5438 aTrackEdge = TopoDS::Edge( Edges.Value(k) );
5439 SMESH_subMesh* locTrack = *itLSM;
5440 SMESHDS_SubMesh* locMeshDS = locTrack->GetSubMeshDS();
5441 TopExp::Vertices( aTrackEdge, aV1, aV2 );
5442 aItN = locTrack->GetFather()->GetSubMesh(aV1)->GetSubMeshDS()->GetNodes();
5443 const SMDS_MeshNode* aN1 = aItN->next();
5444 aItN = locTrack->GetFather()->GetSubMesh(aV2)->GetSubMeshDS()->GetNodes();
5445 const SMDS_MeshNode* aN2 = aItN->next();
5446 // starting node must be aN1 or aN2
5447 if ( !( aN1->GetID() == startNid || aN2->GetID() == startNid ) ) continue;
5448 // 2. Collect parameters on the track edge
5450 aItN = locMeshDS->GetNodes();
5451 while ( aItN->more() ) {
5452 const SMDS_MeshNode* pNode = aItN->next();
5453 const SMDS_EdgePosition* pEPos =
5454 static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
5455 double aT = pEPos->GetUParameter();
5456 aPrms.push_back( aT );
5458 list<SMESH_MeshEditor_PathPoint> LPP;
5459 //Extrusion_Error err =
5460 MakeEdgePathPoints(aPrms, aTrackEdge,(aN1->GetID()==startNid), LPP);
5461 LLPPs.push_back(LPP);
5463 // update startN for search following egde
5464 if( aN1->GetID() == startNid ) startNid = aN2->GetID();
5465 else startNid = aN1->GetID();
5469 list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
5470 list<SMESH_MeshEditor_PathPoint> firstList = *itLLPP;
5471 list<SMESH_MeshEditor_PathPoint>::iterator itPP = firstList.begin();
5472 for(; itPP!=firstList.end(); itPP++) {
5473 fullList.push_back( *itPP );
5475 SMESH_MeshEditor_PathPoint PP1 = fullList.back();
5476 fullList.pop_back();
5478 for(; itLLPP!=LLPPs.end(); itLLPP++) {
5479 list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
5480 itPP = currList.begin();
5481 SMESH_MeshEditor_PathPoint PP2 = currList.front();
5482 gp_Dir D1 = PP1.Tangent();
5483 gp_Dir D2 = PP2.Tangent();
5484 gp_Dir Dnew( gp_Vec( (D1.X()+D2.X())/2, (D1.Y()+D2.Y())/2,
5485 (D1.Z()+D2.Z())/2 ) );
5486 PP1.SetTangent(Dnew);
5487 fullList.push_back(PP1);
5489 for(; itPP!=firstList.end(); itPP++) {
5490 fullList.push_back( *itPP );
5492 PP1 = fullList.back();
5493 fullList.pop_back();
5495 // if wire not closed
5496 fullList.push_back(PP1);
5500 return EXTR_BAD_PATH_SHAPE;
5503 return MakeExtrElements(theElements, fullList, theHasAngles, theAngles, theLinearVariation,
5504 theHasRefPoint, theRefPoint, theMakeGroups);
5508 //=======================================================================
5509 //function : ExtrusionAlongTrack
5511 //=======================================================================
5512 SMESH_MeshEditor::Extrusion_Error
5513 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet & theElements,
5514 SMESH_Mesh* theTrack,
5515 const SMDS_MeshNode* theN1,
5516 const bool theHasAngles,
5517 list<double>& theAngles,
5518 const bool theLinearVariation,
5519 const bool theHasRefPoint,
5520 const gp_Pnt& theRefPoint,
5521 const bool theMakeGroups)
5523 myLastCreatedElems.Clear();
5524 myLastCreatedNodes.Clear();
5527 std::list<double> aPrms;
5528 TIDSortedElemSet::iterator itElem;
5531 TopoDS_Edge aTrackEdge;
5532 TopoDS_Vertex aV1, aV2;
5534 SMDS_ElemIteratorPtr aItE;
5535 SMDS_NodeIteratorPtr aItN;
5536 SMDSAbs_ElementType aTypeE;
5538 TNodeOfNodeListMap mapNewNodes;
5541 aNbE = theElements.size();
5544 return EXTR_NO_ELEMENTS;
5546 // 1.1 Track Pattern
5549 SMESHDS_Mesh* pMeshDS = theTrack->GetMeshDS();
5551 aItE = pMeshDS->elementsIterator();
5552 while ( aItE->more() ) {
5553 const SMDS_MeshElement* pE = aItE->next();
5554 aTypeE = pE->GetType();
5555 // Pattern must contain links only
5556 if ( aTypeE != SMDSAbs_Edge )
5557 return EXTR_PATH_NOT_EDGE;
5560 list<SMESH_MeshEditor_PathPoint> fullList;
5562 const TopoDS_Shape& aS = theTrack->GetShapeToMesh();
5564 if ( !theTrack->HasShapeToMesh() ) {
5565 //Mesh without shape
5566 const SMDS_MeshNode* currentNode = NULL;
5567 const SMDS_MeshNode* prevNode = theN1;
5568 std::vector<const SMDS_MeshNode*> aNodesList;
5569 aNodesList.push_back(theN1);
5570 int nbEdges = 0, conn=0;
5571 const SMDS_MeshElement* prevElem = NULL;
5572 const SMDS_MeshElement* currentElem = NULL;
5573 int totalNbEdges = theTrack->NbEdges();
5574 SMDS_ElemIteratorPtr nIt;
5577 if( !theTrack->GetMeshDS()->Contains(theN1) ) {
5578 return EXTR_BAD_STARTING_NODE;
5581 conn = nbEdgeConnectivity(theN1);
5583 return EXTR_PATH_NOT_EDGE;
5585 aItE = theN1->GetInverseElementIterator();
5586 prevElem = aItE->next();
5587 currentElem = prevElem;
5589 if(totalNbEdges == 1 ) {
5590 nIt = currentElem->nodesIterator();
5591 currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
5592 if(currentNode == prevNode)
5593 currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
5594 aNodesList.push_back(currentNode);
5596 nIt = currentElem->nodesIterator();
5597 while( nIt->more() ) {
5598 currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
5599 if(currentNode == prevNode)
5600 currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
5601 aNodesList.push_back(currentNode);
5603 //case of the closed mesh
5604 if(currentNode == theN1) {
5609 conn = nbEdgeConnectivity(currentNode);
5611 return EXTR_PATH_NOT_EDGE;
5612 }else if( conn == 1 && nbEdges > 0 ) {
5617 prevNode = currentNode;
5618 aItE = currentNode->GetInverseElementIterator();
5619 currentElem = aItE->next();
5620 if( currentElem == prevElem)
5621 currentElem = aItE->next();
5622 nIt = currentElem->nodesIterator();
5623 prevElem = currentElem;
5629 if(nbEdges != totalNbEdges)
5630 return EXTR_PATH_NOT_EDGE;
5632 TopTools_SequenceOfShape Edges;
5633 double x1,x2,y1,y2,z1,z2;
5634 list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
5635 int startNid = theN1->GetID();
5636 for(int i = 1; i < aNodesList.size(); i++) {
5637 x1 = aNodesList[i-1]->X();x2 = aNodesList[i]->X();
5638 y1 = aNodesList[i-1]->Y();y2 = aNodesList[i]->Y();
5639 z1 = aNodesList[i-1]->Z();z2 = aNodesList[i]->Z();
5640 TopoDS_Edge e = BRepBuilderAPI_MakeEdge(gp_Pnt(x1,y1,z1),gp_Pnt(x2,y2,z2));
5641 list<SMESH_MeshEditor_PathPoint> LPP;
5643 MakeEdgePathPoints(aPrms, e, (aNodesList[i-1]->GetID()==startNid), LPP);
5644 LLPPs.push_back(LPP);
5645 if( aNodesList[i-1]->GetID() == startNid ) startNid = aNodesList[i]->GetID();
5646 else startNid = aNodesList[i-1]->GetID();
5650 list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
5651 list<SMESH_MeshEditor_PathPoint> firstList = *itLLPP;
5652 list<SMESH_MeshEditor_PathPoint>::iterator itPP = firstList.begin();
5653 for(; itPP!=firstList.end(); itPP++) {
5654 fullList.push_back( *itPP );
5657 SMESH_MeshEditor_PathPoint PP1 = fullList.back();
5658 SMESH_MeshEditor_PathPoint PP2;
5659 fullList.pop_back();
5661 for(; itLLPP!=LLPPs.end(); itLLPP++) {
5662 list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
5663 itPP = currList.begin();
5664 PP2 = currList.front();
5665 gp_Dir D1 = PP1.Tangent();
5666 gp_Dir D2 = PP2.Tangent();
5667 gp_Dir Dnew( gp_Vec( (D1.X()+D2.X())/2, (D1.Y()+D2.Y())/2,
5668 (D1.Z()+D2.Z())/2 ) );
5669 PP1.SetTangent(Dnew);
5670 fullList.push_back(PP1);
5672 for(; itPP!=currList.end(); itPP++) {
5673 fullList.push_back( *itPP );
5675 PP1 = fullList.back();
5676 fullList.pop_back();
5678 fullList.push_back(PP1);
5680 } // Sub-shape for the Pattern must be an Edge or Wire
5681 else if( aS.ShapeType() == TopAbs_EDGE ) {
5682 aTrackEdge = TopoDS::Edge( aS );
5683 // the Edge must not be degenerated
5684 if ( SMESH_Algo::isDegenerated( aTrackEdge ) )
5685 return EXTR_BAD_PATH_SHAPE;
5686 TopExp::Vertices( aTrackEdge, aV1, aV2 );
5687 const SMDS_MeshNode* aN1 = SMESH_Algo::VertexNode( aV1, pMeshDS );
5688 const SMDS_MeshNode* aN2 = SMESH_Algo::VertexNode( aV2, pMeshDS );
5689 // starting node must be aN1 or aN2
5690 if ( !( aN1 == theN1 || aN2 == theN1 ) )
5691 return EXTR_BAD_STARTING_NODE;
5692 aItN = pMeshDS->nodesIterator();
5693 while ( aItN->more() ) {
5694 const SMDS_MeshNode* pNode = aItN->next();
5695 if( pNode==aN1 || pNode==aN2 ) continue;
5696 const SMDS_EdgePosition* pEPos =
5697 static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
5698 double aT = pEPos->GetUParameter();
5699 aPrms.push_back( aT );
5701 //Extrusion_Error err =
5702 MakeEdgePathPoints(aPrms, aTrackEdge, (aN1==theN1), fullList);
5704 else if( aS.ShapeType() == TopAbs_WIRE ) {
5705 list< SMESH_subMesh* > LSM;
5706 TopTools_SequenceOfShape Edges;
5707 TopExp_Explorer eExp(aS, TopAbs_EDGE);
5708 for(; eExp.More(); eExp.Next()) {
5709 TopoDS_Edge E = TopoDS::Edge( eExp.Current() );
5710 if( SMESH_Algo::isDegenerated(E) ) continue;
5711 SMESH_subMesh* SM = theTrack->GetSubMesh(E);
5717 list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
5718 TopoDS_Vertex aVprev;
5719 TColStd_MapOfInteger UsedNums;
5720 int NbEdges = Edges.Length();
5722 for(; i<=NbEdges; i++) {
5724 list< SMESH_subMesh* >::iterator itLSM = LSM.begin();
5725 for(; itLSM!=LSM.end(); itLSM++) {
5727 if(UsedNums.Contains(k)) continue;
5728 aTrackEdge = TopoDS::Edge( Edges.Value(k) );
5729 SMESH_subMesh* locTrack = *itLSM;
5730 SMESHDS_SubMesh* locMeshDS = locTrack->GetSubMeshDS();
5731 TopExp::Vertices( aTrackEdge, aV1, aV2 );
5732 bool aN1isOK = false, aN2isOK = false;
5733 if ( aVprev.IsNull() ) {
5734 // if previous vertex is not yet defined, it means that we in the beginning of wire
5735 // and we have to find initial vertex corresponding to starting node theN1
5736 const SMDS_MeshNode* aN1 = SMESH_Algo::VertexNode( aV1, pMeshDS );
5737 const SMDS_MeshNode* aN2 = SMESH_Algo::VertexNode( aV2, pMeshDS );
5738 // starting node must be aN1 or aN2
5739 aN1isOK = ( aN1 && aN1 == theN1 );
5740 aN2isOK = ( aN2 && aN2 == theN1 );
5743 // we have specified ending vertex of the previous edge on the previous iteration
5744 // and we have just to check that it corresponds to any vertex in current segment
5745 aN1isOK = aVprev.IsSame( aV1 );
5746 aN2isOK = aVprev.IsSame( aV2 );
5748 if ( !aN1isOK && !aN2isOK ) continue;
5749 // 2. Collect parameters on the track edge
5751 aItN = locMeshDS->GetNodes();
5752 while ( aItN->more() ) {
5753 const SMDS_MeshNode* pNode = aItN->next();
5754 const SMDS_EdgePosition* pEPos =
5755 static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
5756 double aT = pEPos->GetUParameter();
5757 aPrms.push_back( aT );
5759 list<SMESH_MeshEditor_PathPoint> LPP;
5760 //Extrusion_Error err =
5761 MakeEdgePathPoints(aPrms, aTrackEdge, aN1isOK, LPP);
5762 LLPPs.push_back(LPP);
5764 // update startN for search following egde
5765 if ( aN1isOK ) aVprev = aV2;
5770 list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
5771 list<SMESH_MeshEditor_PathPoint>& firstList = *itLLPP;
5772 fullList.splice( fullList.end(), firstList );
5774 SMESH_MeshEditor_PathPoint PP1 = fullList.back();
5775 fullList.pop_back();
5777 for(; itLLPP!=LLPPs.end(); itLLPP++) {
5778 list<SMESH_MeshEditor_PathPoint>& currList = *itLLPP;
5779 SMESH_MeshEditor_PathPoint PP2 = currList.front();
5780 gp_Dir D1 = PP1.Tangent();
5781 gp_Dir D2 = PP2.Tangent();
5782 gp_Dir Dnew( ( D1.XYZ() + D2.XYZ() ) / 2 );
5783 PP1.SetTangent(Dnew);
5784 fullList.push_back(PP1);
5785 fullList.splice( fullList.end(), currList, ++currList.begin(), currList.end() );
5786 PP1 = fullList.back();
5787 fullList.pop_back();
5789 // if wire not closed
5790 fullList.push_back(PP1);
5794 return EXTR_BAD_PATH_SHAPE;
5797 return MakeExtrElements(theElements, fullList, theHasAngles, theAngles, theLinearVariation,
5798 theHasRefPoint, theRefPoint, theMakeGroups);
5802 //=======================================================================
5803 //function : MakeEdgePathPoints
5804 //purpose : auxilary for ExtrusionAlongTrack
5805 //=======================================================================
5806 SMESH_MeshEditor::Extrusion_Error
5807 SMESH_MeshEditor::MakeEdgePathPoints(std::list<double>& aPrms,
5808 const TopoDS_Edge& aTrackEdge,
5810 list<SMESH_MeshEditor_PathPoint>& LPP)
5812 Standard_Real aTx1, aTx2, aL2, aTolVec, aTolVec2;
5814 aTolVec2=aTolVec*aTolVec;
5816 TopoDS_Vertex aV1, aV2;
5817 TopExp::Vertices( aTrackEdge, aV1, aV2 );
5818 aT1=BRep_Tool::Parameter( aV1, aTrackEdge );
5819 aT2=BRep_Tool::Parameter( aV2, aTrackEdge );
5820 // 2. Collect parameters on the track edge
5821 aPrms.push_front( aT1 );
5822 aPrms.push_back( aT2 );
5825 if( FirstIsStart ) {
5836 SMESH_MeshEditor_PathPoint aPP;
5837 Handle(Geom_Curve) aC3D = BRep_Tool::Curve( aTrackEdge, aTx1, aTx2 );
5838 std::list<double>::iterator aItD = aPrms.begin();
5839 for(; aItD != aPrms.end(); ++aItD) {
5843 aC3D->D1( aT, aP3D, aVec );
5844 aL2 = aVec.SquareMagnitude();
5845 if ( aL2 < aTolVec2 )
5846 return EXTR_CANT_GET_TANGENT;
5847 gp_Dir aTgt( aVec );
5849 aPP.SetTangent( aTgt );
5850 aPP.SetParameter( aT );
5857 //=======================================================================
5858 //function : MakeExtrElements
5859 //purpose : auxilary for ExtrusionAlongTrack
5860 //=======================================================================
5861 SMESH_MeshEditor::Extrusion_Error
5862 SMESH_MeshEditor::MakeExtrElements(TIDSortedElemSet& theElements,
5863 list<SMESH_MeshEditor_PathPoint>& fullList,
5864 const bool theHasAngles,
5865 list<double>& theAngles,
5866 const bool theLinearVariation,
5867 const bool theHasRefPoint,
5868 const gp_Pnt& theRefPoint,
5869 const bool theMakeGroups)
5871 const int aNbTP = fullList.size();
5873 if( theHasAngles && !theAngles.empty() && theLinearVariation )
5874 LinearAngleVariation(aNbTP-1, theAngles);
5875 // fill vector of path points with angles
5876 vector<SMESH_MeshEditor_PathPoint> aPPs;
5877 list<SMESH_MeshEditor_PathPoint>::iterator itPP = fullList.begin();
5878 list<double>::iterator itAngles = theAngles.begin();
5879 aPPs.push_back( *itPP++ );
5880 for( ; itPP != fullList.end(); itPP++) {
5881 aPPs.push_back( *itPP );
5882 if ( theHasAngles && itAngles != theAngles.end() )
5883 aPPs.back().SetAngle( *itAngles++ );
5886 TNodeOfNodeListMap mapNewNodes;
5887 TElemOfVecOfNnlmiMap mapElemNewNodes;
5888 TTElemOfElemListMap newElemsMap;
5889 TIDSortedElemSet::iterator itElem;
5890 // source elements for each generated one
5891 SMESH_SequenceOfElemPtr srcElems, srcNodes;
5893 // 3. Center of rotation aV0
5894 gp_Pnt aV0 = theRefPoint;
5895 if ( !theHasRefPoint )
5897 gp_XYZ aGC( 0.,0.,0. );
5898 TIDSortedElemSet newNodes;
5900 itElem = theElements.begin();
5901 for ( ; itElem != theElements.end(); itElem++ ) {
5902 const SMDS_MeshElement* elem = *itElem;
5904 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5905 while ( itN->more() ) {
5906 const SMDS_MeshElement* node = itN->next();
5907 if ( newNodes.insert( node ).second )
5908 aGC += SMESH_TNodeXYZ( node );
5911 aGC /= newNodes.size();
5913 } // if (!theHasRefPoint) {
5915 // 4. Processing the elements
5916 SMESHDS_Mesh* aMesh = GetMeshDS();
5918 for ( itElem = theElements.begin(); itElem != theElements.end(); itElem++ ) {
5919 // check element type
5920 const SMDS_MeshElement* elem = *itElem;
5921 SMDSAbs_ElementType aTypeE = elem->GetType();
5922 if ( !elem || ( aTypeE != SMDSAbs_Face && aTypeE != SMDSAbs_Edge ) )
5925 vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5926 newNodesItVec.reserve( elem->NbNodes() );
5928 // loop on elem nodes
5930 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5931 while ( itN->more() )
5934 // check if a node has been already processed
5935 const SMDS_MeshNode* node =
5936 static_cast<const SMDS_MeshNode*>( itN->next() );
5937 TNodeOfNodeListMap::iterator nIt = mapNewNodes.find( node );
5938 if ( nIt == mapNewNodes.end() ) {
5939 nIt = mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5940 list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5943 Standard_Real aAngle1x, aAngleT1T0, aTolAng;
5944 gp_Pnt aP0x, aP1x, aPN0, aPN1, aV0x, aV1x;
5945 gp_Ax1 anAx1, anAxT1T0;
5946 gp_Dir aDT1x, aDT0x, aDT1T0;
5951 aPN0 = SMESH_TNodeXYZ( node );
5953 const SMESH_MeshEditor_PathPoint& aPP0 = aPPs[0];
5955 aDT0x= aPP0.Tangent();
5956 //cout<<"j = 0 PP: Pnt("<<aP0x.X()<<","<<aP0x.Y()<<","<<aP0x.Z()<<")"<<endl;
5958 for ( int j = 1; j < aNbTP; ++j ) {
5959 const SMESH_MeshEditor_PathPoint& aPP1 = aPPs[j];
5961 aDT1x = aPP1.Tangent();
5962 aAngle1x = aPP1.Angle();
5964 gp_Trsf aTrsf, aTrsfRot, aTrsfRotT1T0;
5966 gp_Vec aV01x( aP0x, aP1x );
5967 aTrsf.SetTranslation( aV01x );
5970 aV1x = aV0x.Transformed( aTrsf );
5971 aPN1 = aPN0.Transformed( aTrsf );
5973 // rotation 1 [ T1,T0 ]
5974 aAngleT1T0=-aDT1x.Angle( aDT0x );
5975 if (fabs(aAngleT1T0) > aTolAng) {
5977 anAxT1T0.SetLocation( aV1x );
5978 anAxT1T0.SetDirection( aDT1T0 );
5979 aTrsfRotT1T0.SetRotation( anAxT1T0, aAngleT1T0 );
5981 aPN1 = aPN1.Transformed( aTrsfRotT1T0 );
5985 if ( theHasAngles ) {
5986 anAx1.SetLocation( aV1x );
5987 anAx1.SetDirection( aDT1x );
5988 aTrsfRot.SetRotation( anAx1, aAngle1x );
5990 aPN1 = aPN1.Transformed( aTrsfRot );
5994 //MESSAGE("elem->IsQuadratic " << elem->IsQuadratic() << " " << elem->IsMediumNode(node));
5995 if( elem->IsQuadratic() && !elem->IsMediumNode(node) ) {
5996 // create additional node
5997 double x = ( aPN1.X() + aPN0.X() )/2.;
5998 double y = ( aPN1.Y() + aPN0.Y() )/2.;
5999 double z = ( aPN1.Z() + aPN0.Z() )/2.;
6000 const SMDS_MeshNode* newNode = aMesh->AddNode(x,y,z);
6001 myLastCreatedNodes.Append(newNode);
6002 srcNodes.Append( node );
6003 listNewNodes.push_back( newNode );
6005 const SMDS_MeshNode* newNode = aMesh->AddNode( aPN1.X(), aPN1.Y(), aPN1.Z() );
6006 myLastCreatedNodes.Append(newNode);
6007 srcNodes.Append( node );
6008 listNewNodes.push_back( newNode );
6018 // if current elem is quadratic and current node is not medium
6019 // we have to check - may be it is needed to insert additional nodes
6020 if( elem->IsQuadratic() && !elem->IsMediumNode(node) ) {
6021 list< const SMDS_MeshNode* > & listNewNodes = nIt->second;
6022 if(listNewNodes.size()==aNbTP-1) {
6023 vector<const SMDS_MeshNode*> aNodes(2*(aNbTP-1));
6024 gp_XYZ P(node->X(), node->Y(), node->Z());
6025 list< const SMDS_MeshNode* >::iterator it = listNewNodes.begin();
6027 for(i=0; i<aNbTP-1; i++) {
6028 const SMDS_MeshNode* N = *it;
6029 double x = ( N->X() + P.X() )/2.;
6030 double y = ( N->Y() + P.Y() )/2.;
6031 double z = ( N->Z() + P.Z() )/2.;
6032 const SMDS_MeshNode* newN = aMesh->AddNode(x,y,z);
6033 srcNodes.Append( node );
6034 myLastCreatedNodes.Append(newN);
6037 P = gp_XYZ(N->X(),N->Y(),N->Z());
6039 listNewNodes.clear();
6040 for(i=0; i<2*(aNbTP-1); i++) {
6041 listNewNodes.push_back(aNodes[i]);
6047 newNodesItVec.push_back( nIt );
6049 // make new elements
6050 //sweepElement( aMesh, elem, newNodesItVec, newElemsMap[elem],
6051 // newNodesItVec[0]->second.size(), myLastCreatedElems );
6052 sweepElement( elem, newNodesItVec, newElemsMap[elem], aNbTP-1, srcElems );
6055 makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElements, aNbTP-1, srcElems );
6057 if ( theMakeGroups )
6058 generateGroups( srcNodes, srcElems, "extruded");
6064 //=======================================================================
6065 //function : LinearAngleVariation
6066 //purpose : auxilary for ExtrusionAlongTrack
6067 //=======================================================================
6068 void SMESH_MeshEditor::LinearAngleVariation(const int nbSteps,
6069 list<double>& Angles)
6071 int nbAngles = Angles.size();
6072 if( nbSteps > nbAngles ) {
6073 vector<double> theAngles(nbAngles);
6074 list<double>::iterator it = Angles.begin();
6076 for(; it!=Angles.end(); it++) {
6078 theAngles[i] = (*it);
6081 double rAn2St = double( nbAngles ) / double( nbSteps );
6082 double angPrev = 0, angle;
6083 for ( int iSt = 0; iSt < nbSteps; ++iSt ) {
6084 double angCur = rAn2St * ( iSt+1 );
6085 double angCurFloor = floor( angCur );
6086 double angPrevFloor = floor( angPrev );
6087 if ( angPrevFloor == angCurFloor )
6088 angle = rAn2St * theAngles[ int( angCurFloor ) ];
6090 int iP = int( angPrevFloor );
6091 double angPrevCeil = ceil(angPrev);
6092 angle = ( angPrevCeil - angPrev ) * theAngles[ iP ];
6094 int iC = int( angCurFloor );
6095 if ( iC < nbAngles )
6096 angle += ( angCur - angCurFloor ) * theAngles[ iC ];
6098 iP = int( angPrevCeil );
6100 angle += theAngles[ iC ];
6102 res.push_back(angle);
6107 for(; it!=res.end(); it++)
6108 Angles.push_back( *it );
6113 //================================================================================
6115 * \brief Move or copy theElements applying theTrsf to their nodes
6116 * \param theElems - elements to transform, if theElems is empty then apply to all mesh nodes
6117 * \param theTrsf - transformation to apply
6118 * \param theCopy - if true, create translated copies of theElems
6119 * \param theMakeGroups - if true and theCopy, create translated groups
6120 * \param theTargetMesh - mesh to copy translated elements into
6121 * \return SMESH_MeshEditor::PGroupIDs - list of ids of created groups
6123 //================================================================================
6125 SMESH_MeshEditor::PGroupIDs
6126 SMESH_MeshEditor::Transform (TIDSortedElemSet & theElems,
6127 const gp_Trsf& theTrsf,
6129 const bool theMakeGroups,
6130 SMESH_Mesh* theTargetMesh)
6132 myLastCreatedElems.Clear();
6133 myLastCreatedNodes.Clear();
6135 bool needReverse = false;
6136 string groupPostfix;
6137 switch ( theTrsf.Form() ) {
6139 MESSAGE("gp_PntMirror");
6141 groupPostfix = "mirrored";
6144 MESSAGE("gp_Ax1Mirror");
6145 groupPostfix = "mirrored";
6148 MESSAGE("gp_Ax2Mirror");
6150 groupPostfix = "mirrored";
6153 MESSAGE("gp_Rotation");
6154 groupPostfix = "rotated";
6156 case gp_Translation:
6157 MESSAGE("gp_Translation");
6158 groupPostfix = "translated";
6161 MESSAGE("gp_Scale");
6162 groupPostfix = "scaled";
6164 case gp_CompoundTrsf: // different scale by axis
6165 MESSAGE("gp_CompoundTrsf");
6166 groupPostfix = "scaled";
6170 needReverse = false;
6171 groupPostfix = "transformed";
6174 SMESH_MeshEditor targetMeshEditor( theTargetMesh );
6175 SMESHDS_Mesh* aTgtMesh = theTargetMesh ? theTargetMesh->GetMeshDS() : 0;
6176 SMESHDS_Mesh* aMesh = GetMeshDS();
6179 // map old node to new one
6180 TNodeNodeMap nodeMap;
6182 // elements sharing moved nodes; those of them which have all
6183 // nodes mirrored but are not in theElems are to be reversed
6184 TIDSortedElemSet inverseElemSet;
6186 // source elements for each generated one
6187 SMESH_SequenceOfElemPtr srcElems, srcNodes;
6189 // issue 021015: EDF 1578 SMESH: Free nodes are removed when translating a mesh
6190 TIDSortedElemSet orphanNode;
6192 if ( theElems.empty() ) // transform the whole mesh
6195 SMDS_ElemIteratorPtr eIt = aMesh->elementsIterator();
6196 while ( eIt->more() ) theElems.insert( eIt->next() );
6198 SMDS_NodeIteratorPtr nIt = aMesh->nodesIterator();
6199 while ( nIt->more() )
6201 const SMDS_MeshNode* node = nIt->next();
6202 if ( node->NbInverseElements() == 0)
6203 orphanNode.insert( node );
6207 // loop on elements to transform nodes : first orphan nodes then elems
6208 TIDSortedElemSet::iterator itElem;
6209 TIDSortedElemSet *elements[] = {&orphanNode, &theElems };
6210 for (int i=0; i<2; i++)
6211 for ( itElem = elements[i]->begin(); itElem != elements[i]->end(); itElem++ ) {
6212 const SMDS_MeshElement* elem = *itElem;
6216 // loop on elem nodes
6217 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6218 while ( itN->more() ) {
6220 const SMDS_MeshNode* node = cast2Node( itN->next() );
6221 // check if a node has been already transformed
6222 pair<TNodeNodeMap::iterator,bool> n2n_isnew =
6223 nodeMap.insert( make_pair ( node, node ));
6224 if ( !n2n_isnew.second )
6228 coord[0] = node->X();
6229 coord[1] = node->Y();
6230 coord[2] = node->Z();
6231 theTrsf.Transforms( coord[0], coord[1], coord[2] );
6232 if ( theTargetMesh ) {
6233 const SMDS_MeshNode * newNode = aTgtMesh->AddNode( coord[0], coord[1], coord[2] );
6234 n2n_isnew.first->second = newNode;
6235 myLastCreatedNodes.Append(newNode);
6236 srcNodes.Append( node );
6238 else if ( theCopy ) {
6239 const SMDS_MeshNode * newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
6240 n2n_isnew.first->second = newNode;
6241 myLastCreatedNodes.Append(newNode);
6242 srcNodes.Append( node );
6245 aMesh->MoveNode( node, coord[0], coord[1], coord[2] );
6246 // node position on shape becomes invalid
6247 const_cast< SMDS_MeshNode* > ( node )->SetPosition
6248 ( SMDS_SpacePosition::originSpacePosition() );
6251 // keep inverse elements
6252 if ( !theCopy && !theTargetMesh && needReverse ) {
6253 SMDS_ElemIteratorPtr invElemIt = node->GetInverseElementIterator();
6254 while ( invElemIt->more() ) {
6255 const SMDS_MeshElement* iel = invElemIt->next();
6256 inverseElemSet.insert( iel );
6262 // either create new elements or reverse mirrored ones
6263 if ( !theCopy && !needReverse && !theTargetMesh )
6266 TIDSortedElemSet::iterator invElemIt = inverseElemSet.begin();
6267 for ( ; invElemIt != inverseElemSet.end(); invElemIt++ )
6268 theElems.insert( *invElemIt );
6270 // Replicate or reverse elements
6272 std::vector<int> iForw;
6273 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
6275 const SMDS_MeshElement* elem = *itElem;
6276 if ( !elem ) continue;
6278 SMDSAbs_GeometryType geomType = elem->GetGeomType();
6279 int nbNodes = elem->NbNodes();
6280 if ( geomType == SMDSGeom_NONE ) continue; // node
6282 switch ( geomType ) {
6284 case SMDSGeom_POLYGON: // ---------------------- polygon
6286 vector<const SMDS_MeshNode*> poly_nodes (nbNodes);
6288 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6289 while (itN->more()) {
6290 const SMDS_MeshNode* node =
6291 static_cast<const SMDS_MeshNode*>(itN->next());
6292 TNodeNodeMap::iterator nodeMapIt = nodeMap.find(node);
6293 if (nodeMapIt == nodeMap.end())
6294 break; // not all nodes transformed
6296 // reverse mirrored faces and volumes
6297 poly_nodes[nbNodes - iNode - 1] = (*nodeMapIt).second;
6299 poly_nodes[iNode] = (*nodeMapIt).second;
6303 if ( iNode != nbNodes )
6304 continue; // not all nodes transformed
6306 if ( theTargetMesh ) {
6307 myLastCreatedElems.Append(aTgtMesh->AddPolygonalFace(poly_nodes));
6308 srcElems.Append( elem );
6310 else if ( theCopy ) {
6311 myLastCreatedElems.Append(aMesh->AddPolygonalFace(poly_nodes));
6312 srcElems.Append( elem );
6315 aMesh->ChangePolygonNodes(elem, poly_nodes);
6320 case SMDSGeom_POLYHEDRA: // ------------------ polyhedral volume
6322 const SMDS_VtkVolume* aPolyedre =
6323 dynamic_cast<const SMDS_VtkVolume*>( elem );
6325 MESSAGE("Warning: bad volumic element");
6329 vector<const SMDS_MeshNode*> poly_nodes; poly_nodes.reserve( nbNodes );
6330 vector<int> quantities; quantities.reserve( nbNodes );
6332 bool allTransformed = true;
6333 int nbFaces = aPolyedre->NbFaces();
6334 for (int iface = 1; iface <= nbFaces && allTransformed; iface++) {
6335 int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
6336 for (int inode = 1; inode <= nbFaceNodes && allTransformed; inode++) {
6337 const SMDS_MeshNode* node = aPolyedre->GetFaceNode(iface, inode);
6338 TNodeNodeMap::iterator nodeMapIt = nodeMap.find(node);
6339 if (nodeMapIt == nodeMap.end()) {
6340 allTransformed = false; // not all nodes transformed
6342 poly_nodes.push_back((*nodeMapIt).second);
6344 if ( needReverse && allTransformed )
6345 std::reverse( poly_nodes.end() - nbFaceNodes, poly_nodes.end() );
6347 quantities.push_back(nbFaceNodes);
6349 if ( !allTransformed )
6350 continue; // not all nodes transformed
6352 if ( theTargetMesh ) {
6353 myLastCreatedElems.Append(aTgtMesh->AddPolyhedralVolume(poly_nodes, quantities));
6354 srcElems.Append( elem );
6356 else if ( theCopy ) {
6357 myLastCreatedElems.Append(aMesh->AddPolyhedralVolume(poly_nodes, quantities));
6358 srcElems.Append( elem );
6361 aMesh->ChangePolyhedronNodes(elem, poly_nodes, quantities);
6366 case SMDSGeom_BALL: // -------------------- Ball
6368 if ( !theCopy && !theTargetMesh ) continue;
6370 TNodeNodeMap::iterator nodeMapIt = nodeMap.find( elem->GetNode(0) );
6371 if (nodeMapIt == nodeMap.end())
6372 continue; // not all nodes transformed
6374 double diameter = static_cast<const SMDS_BallElement*>(elem)->GetDiameter();
6375 if ( theTargetMesh ) {
6376 myLastCreatedElems.Append(aTgtMesh->AddBall( nodeMapIt->second, diameter ));
6377 srcElems.Append( elem );
6380 myLastCreatedElems.Append(aMesh->AddBall( nodeMapIt->second, diameter ));
6381 srcElems.Append( elem );
6386 default: // ----------------------- Regular elements
6388 while ( iForw.size() < nbNodes ) iForw.push_back( iForw.size() );
6389 const std::vector<int>& iRev = SMDS_MeshCell::reverseSmdsOrder( elem->GetEntityType() );
6390 const std::vector<int>& i = needReverse ? iRev : iForw;
6392 // find transformed nodes
6393 vector<const SMDS_MeshNode*> nodes(nbNodes);
6395 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6396 while ( itN->more() ) {
6397 const SMDS_MeshNode* node =
6398 static_cast<const SMDS_MeshNode*>( itN->next() );
6399 TNodeNodeMap::iterator nodeMapIt = nodeMap.find( node );
6400 if ( nodeMapIt == nodeMap.end() )
6401 break; // not all nodes transformed
6402 nodes[ i [ iNode++ ]] = (*nodeMapIt).second;
6404 if ( iNode != nbNodes )
6405 continue; // not all nodes transformed
6407 if ( theTargetMesh ) {
6408 if ( SMDS_MeshElement* copy =
6409 targetMeshEditor.AddElement( nodes, elem->GetType(), elem->IsPoly() )) {
6410 myLastCreatedElems.Append( copy );
6411 srcElems.Append( elem );
6414 else if ( theCopy ) {
6415 if ( AddElement( nodes, elem->GetType(), elem->IsPoly() ))
6416 srcElems.Append( elem );
6419 // reverse element as it was reversed by transformation
6421 aMesh->ChangeElementNodes( elem, &nodes[0], nbNodes );
6423 } // switch ( geomType )
6425 } // loop on elements
6427 PGroupIDs newGroupIDs;
6429 if ( ( theMakeGroups && theCopy ) ||
6430 ( theMakeGroups && theTargetMesh ) )
6431 newGroupIDs = generateGroups( srcNodes, srcElems, groupPostfix, theTargetMesh );
6436 //=======================================================================
6438 * \brief Create groups of elements made during transformation
6439 * \param nodeGens - nodes making corresponding myLastCreatedNodes
6440 * \param elemGens - elements making corresponding myLastCreatedElems
6441 * \param postfix - to append to names of new groups
6443 //=======================================================================
6445 SMESH_MeshEditor::PGroupIDs
6446 SMESH_MeshEditor::generateGroups(const SMESH_SequenceOfElemPtr& nodeGens,
6447 const SMESH_SequenceOfElemPtr& elemGens,
6448 const std::string& postfix,
6449 SMESH_Mesh* targetMesh)
6451 PGroupIDs newGroupIDs( new list<int> );
6452 SMESH_Mesh* mesh = targetMesh ? targetMesh : GetMesh();
6454 // Sort existing groups by types and collect their names
6456 // to store an old group and a generated new ones
6458 using boost::make_tuple;
6459 typedef tuple< SMESHDS_GroupBase*, SMESHDS_Group*, SMESHDS_Group* > TOldNewGroup;
6460 vector< list< TOldNewGroup > > groupsByType( SMDSAbs_NbElementTypes );
6461 vector< TOldNewGroup* > orderedOldNewGroups; // in order of old groups
6463 set< string > groupNames;
6465 SMESH_Mesh::GroupIteratorPtr groupIt = GetMesh()->GetGroups();
6466 if ( !groupIt->more() ) return newGroupIDs;
6468 int newGroupID = mesh->GetGroupIds().back()+1;
6469 while ( groupIt->more() )
6471 SMESH_Group * group = groupIt->next();
6472 if ( !group ) continue;
6473 SMESHDS_GroupBase* groupDS = group->GetGroupDS();
6474 if ( !groupDS || groupDS->IsEmpty() ) continue;
6475 groupNames.insert ( group->GetName() );
6476 groupDS->SetStoreName( group->GetName() );
6477 const SMDSAbs_ElementType type = groupDS->GetType();
6478 SMESHDS_Group* newGroup = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
6479 SMESHDS_Group* newTopGroup = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
6480 groupsByType[ type ].push_back( make_tuple( groupDS, newGroup, newTopGroup ));
6481 orderedOldNewGroups.push_back( & groupsByType[ type ].back() );
6484 // Loop on nodes and elements to add them in new groups
6486 for ( int isNodes = 0; isNodes < 2; ++isNodes )
6488 const SMESH_SequenceOfElemPtr& gens = isNodes ? nodeGens : elemGens;
6489 const SMESH_SequenceOfElemPtr& elems = isNodes ? myLastCreatedNodes : myLastCreatedElems;
6490 if ( gens.Length() != elems.Length() )
6491 throw SALOME_Exception("SMESH_MeshEditor::generateGroups(): invalid args");
6493 // loop on created elements
6494 for (int iElem = 1; iElem <= elems.Length(); ++iElem )
6496 const SMDS_MeshElement* sourceElem = gens( iElem );
6497 if ( !sourceElem ) {
6498 MESSAGE("generateGroups(): NULL source element");
6501 list< TOldNewGroup > & groupsOldNew = groupsByType[ sourceElem->GetType() ];
6502 if ( groupsOldNew.empty() ) { // no groups of this type at all
6503 while ( iElem < gens.Length() && gens( iElem+1 ) == sourceElem )
6504 ++iElem; // skip all elements made by sourceElem
6507 // collect all elements made by the iElem-th sourceElem
6508 list< const SMDS_MeshElement* > resultElems;
6509 if ( const SMDS_MeshElement* resElem = elems( iElem ))
6510 if ( resElem != sourceElem )
6511 resultElems.push_back( resElem );
6512 while ( iElem < gens.Length() && gens( iElem+1 ) == sourceElem )
6513 if ( const SMDS_MeshElement* resElem = elems( ++iElem ))
6514 if ( resElem != sourceElem )
6515 resultElems.push_back( resElem );
6517 // there must be a top element
6518 const SMDS_MeshElement* topElem = 0;
6521 topElem = resultElems.back();
6522 resultElems.pop_back();
6526 list< const SMDS_MeshElement* >::reverse_iterator resElemIt = resultElems.rbegin();
6527 for ( ; resElemIt != resultElems.rend() ; ++resElemIt )
6528 if ( (*resElemIt)->GetType() == sourceElem->GetType() )
6530 topElem = *resElemIt;
6531 resultElems.erase( --(resElemIt.base()) ); // erase *resElemIt
6536 // add resultElems to groups originted from ones the sourceElem belongs to
6537 list< TOldNewGroup >::iterator gOldNew, gLast = groupsOldNew.end();
6538 for ( gOldNew = groupsOldNew.begin(); gOldNew != gLast; ++gOldNew )
6540 SMESHDS_GroupBase* oldGroup = gOldNew->get<0>();
6541 if ( oldGroup->Contains( sourceElem )) // sourceElem is in oldGroup
6543 // fill in a new group
6544 SMDS_MeshGroup & newGroup = gOldNew->get<1>()->SMDSGroup();
6545 list< const SMDS_MeshElement* >::iterator resLast = resultElems.end(), resElemIt;
6546 for ( resElemIt = resultElems.begin(); resElemIt != resLast; ++resElemIt )
6547 newGroup.Add( *resElemIt );
6549 // fill a "top" group
6552 SMDS_MeshGroup & newTopGroup = gOldNew->get<2>()->SMDSGroup();
6553 newTopGroup.Add( topElem );
6557 } // loop on created elements
6558 }// loop on nodes and elements
6560 // Create new SMESH_Groups from SMESHDS_Groups and remove empty SMESHDS_Groups
6562 list<int> topGrouIds;
6563 for ( size_t i = 0; i < orderedOldNewGroups.size(); ++i )
6565 SMESHDS_GroupBase* oldGroupDS = orderedOldNewGroups[i]->get<0>();
6566 SMESHDS_Group* newGroups[2] = { orderedOldNewGroups[i]->get<1>(),
6567 orderedOldNewGroups[i]->get<2>() };
6568 const int nbNewGroups = !newGroups[0]->IsEmpty() + !newGroups[1]->IsEmpty();
6569 for ( int is2nd = 0; is2nd < 2; ++is2nd )
6571 SMESHDS_Group* newGroupDS = newGroups[ is2nd ];
6572 if ( newGroupDS->IsEmpty() )
6574 mesh->GetMeshDS()->RemoveGroup( newGroupDS );
6579 newGroupDS->SetType( newGroupDS->GetElements()->next()->GetType() );
6582 const bool isTop = ( nbNewGroups == 2 &&
6583 newGroupDS->GetType() == oldGroupDS->GetType() &&
6586 string name = oldGroupDS->GetStoreName();
6587 if ( !targetMesh ) {
6588 string suffix = ( isTop ? "top": postfix.c_str() );
6592 while ( !groupNames.insert( name ).second ) // name exists
6593 name = SMESH_Comment( oldGroupDS->GetStoreName() ) << "_" << suffix << "_" << nb++;
6598 newGroupDS->SetStoreName( name.c_str() );
6600 // make a SMESH_Groups
6601 mesh->AddGroup( newGroupDS );
6603 topGrouIds.push_back( newGroupDS->GetID() );
6605 newGroupIDs->push_back( newGroupDS->GetID() );
6609 newGroupIDs->splice( newGroupIDs->end(), topGrouIds );
6614 //================================================================================
6616 * \brief Return list of group of nodes close to each other within theTolerance
6617 * Search among theNodes or in the whole mesh if theNodes is empty using
6618 * an Octree algorithm
6620 //================================================================================
6622 void SMESH_MeshEditor::FindCoincidentNodes (TIDSortedNodeSet & theNodes,
6623 const double theTolerance,
6624 TListOfListOfNodes & theGroupsOfNodes)
6626 myLastCreatedElems.Clear();
6627 myLastCreatedNodes.Clear();
6629 if ( theNodes.empty() )
6630 { // get all nodes in the mesh
6631 SMDS_NodeIteratorPtr nIt = GetMeshDS()->nodesIterator(/*idInceasingOrder=*/true);
6632 while ( nIt->more() )
6633 theNodes.insert( theNodes.end(),nIt->next());
6636 SMESH_OctreeNode::FindCoincidentNodes ( theNodes, &theGroupsOfNodes, theTolerance);
6639 //=======================================================================
6640 //function : SimplifyFace
6642 //=======================================================================
6644 int SMESH_MeshEditor::SimplifyFace (const vector<const SMDS_MeshNode *>& faceNodes,
6645 vector<const SMDS_MeshNode *>& poly_nodes,
6646 vector<int>& quantities) const
6648 int nbNodes = faceNodes.size();
6653 set<const SMDS_MeshNode*> nodeSet;
6655 // get simple seq of nodes
6656 //const SMDS_MeshNode* simpleNodes[ nbNodes ];
6657 vector<const SMDS_MeshNode*> simpleNodes( nbNodes );
6658 int iSimple = 0, nbUnique = 0;
6660 simpleNodes[iSimple++] = faceNodes[0];
6662 for (int iCur = 1; iCur < nbNodes; iCur++) {
6663 if (faceNodes[iCur] != simpleNodes[iSimple - 1]) {
6664 simpleNodes[iSimple++] = faceNodes[iCur];
6665 if (nodeSet.insert( faceNodes[iCur] ).second)
6669 int nbSimple = iSimple;
6670 if (simpleNodes[nbSimple - 1] == simpleNodes[0]) {
6680 bool foundLoop = (nbSimple > nbUnique);
6683 set<const SMDS_MeshNode*> loopSet;
6684 for (iSimple = 0; iSimple < nbSimple && !foundLoop; iSimple++) {
6685 const SMDS_MeshNode* n = simpleNodes[iSimple];
6686 if (!loopSet.insert( n ).second) {
6690 int iC = 0, curLast = iSimple;
6691 for (; iC < curLast; iC++) {
6692 if (simpleNodes[iC] == n) break;
6694 int loopLen = curLast - iC;
6696 // create sub-element
6698 quantities.push_back(loopLen);
6699 for (; iC < curLast; iC++) {
6700 poly_nodes.push_back(simpleNodes[iC]);
6703 // shift the rest nodes (place from the first loop position)
6704 for (iC = curLast + 1; iC < nbSimple; iC++) {
6705 simpleNodes[iC - loopLen] = simpleNodes[iC];
6707 nbSimple -= loopLen;
6710 } // for (iSimple = 0; iSimple < nbSimple; iSimple++)
6711 } // while (foundLoop)
6715 quantities.push_back(iSimple);
6716 for (int i = 0; i < iSimple; i++)
6717 poly_nodes.push_back(simpleNodes[i]);
6723 //=======================================================================
6724 //function : MergeNodes
6725 //purpose : In each group, the cdr of nodes are substituted by the first one
6727 //=======================================================================
6729 void SMESH_MeshEditor::MergeNodes (TListOfListOfNodes & theGroupsOfNodes)
6731 MESSAGE("MergeNodes");
6732 myLastCreatedElems.Clear();
6733 myLastCreatedNodes.Clear();
6735 SMESHDS_Mesh* aMesh = GetMeshDS();
6737 TNodeNodeMap nodeNodeMap; // node to replace - new node
6738 set<const SMDS_MeshElement*> elems; // all elements with changed nodes
6739 list< int > rmElemIds, rmNodeIds;
6741 // Fill nodeNodeMap and elems
6743 TListOfListOfNodes::iterator grIt = theGroupsOfNodes.begin();
6744 for ( ; grIt != theGroupsOfNodes.end(); grIt++ ) {
6745 list<const SMDS_MeshNode*>& nodes = *grIt;
6746 list<const SMDS_MeshNode*>::iterator nIt = nodes.begin();
6747 const SMDS_MeshNode* nToKeep = *nIt;
6748 //MESSAGE("node to keep " << nToKeep->GetID());
6749 for ( ++nIt; nIt != nodes.end(); nIt++ ) {
6750 const SMDS_MeshNode* nToRemove = *nIt;
6751 nodeNodeMap.insert( TNodeNodeMap::value_type( nToRemove, nToKeep ));
6752 if ( nToRemove != nToKeep ) {
6753 //MESSAGE(" node to remove " << nToRemove->GetID());
6754 rmNodeIds.push_back( nToRemove->GetID() );
6755 AddToSameGroups( nToKeep, nToRemove, aMesh );
6756 // set _alwaysComputed to a sub-mesh of VERTEX to enable mesh computing
6757 // after MergeNodes() w/o creating node in place of merged ones.
6758 const SMDS_PositionPtr& pos = nToRemove->GetPosition();
6759 if ( pos && pos->GetTypeOfPosition() == SMDS_TOP_VERTEX )
6760 if ( SMESH_subMesh* sm = myMesh->GetSubMeshContaining( nToRemove->getshapeId() ))
6761 sm->SetIsAlwaysComputed( true );
6764 SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
6765 while ( invElemIt->more() ) {
6766 const SMDS_MeshElement* elem = invElemIt->next();
6771 // Change element nodes or remove an element
6773 set<const SMDS_MeshElement*>::iterator eIt = elems.begin();
6774 for ( ; eIt != elems.end(); eIt++ ) {
6775 const SMDS_MeshElement* elem = *eIt;
6776 //MESSAGE(" ---- inverse elem on node to remove " << elem->GetID());
6777 int nbNodes = elem->NbNodes();
6778 int aShapeId = FindShape( elem );
6780 set<const SMDS_MeshNode*> nodeSet;
6781 vector< const SMDS_MeshNode*> curNodes( nbNodes ), uniqueNodes( nbNodes );
6782 int iUnique = 0, iCur = 0, nbRepl = 0;
6783 vector<int> iRepl( nbNodes );
6785 // get new seq of nodes
6786 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6787 while ( itN->more() ) {
6788 const SMDS_MeshNode* n =
6789 static_cast<const SMDS_MeshNode*>( itN->next() );
6791 TNodeNodeMap::iterator nnIt = nodeNodeMap.find( n );
6792 if ( nnIt != nodeNodeMap.end() ) { // n sticks
6794 // BUG 0020185: begin
6796 bool stopRecur = false;
6797 set<const SMDS_MeshNode*> nodesRecur;
6798 nodesRecur.insert(n);
6799 while (!stopRecur) {
6800 TNodeNodeMap::iterator nnIt_i = nodeNodeMap.find( n );
6801 if ( nnIt_i != nodeNodeMap.end() ) { // n sticks
6802 n = (*nnIt_i).second;
6803 if (!nodesRecur.insert(n).second) {
6804 // error: recursive dependancy
6814 curNodes[ iCur ] = n;
6815 bool isUnique = nodeSet.insert( n ).second;
6817 uniqueNodes[ iUnique++ ] = n;
6819 iRepl[ nbRepl++ ] = iCur;
6823 // Analyse element topology after replacement
6826 int nbUniqueNodes = nodeSet.size();
6827 //MESSAGE("nbNodes nbUniqueNodes " << nbNodes << " " << nbUniqueNodes);
6828 if ( nbNodes != nbUniqueNodes ) { // some nodes stick
6829 // Polygons and Polyhedral volumes
6830 if (elem->IsPoly()) {
6832 if (elem->GetType() == SMDSAbs_Face) {
6834 vector<const SMDS_MeshNode *> face_nodes (nbNodes);
6836 for (; inode < nbNodes; inode++) {
6837 face_nodes[inode] = curNodes[inode];
6840 vector<const SMDS_MeshNode *> polygons_nodes;
6841 vector<int> quantities;
6842 int nbNew = SimplifyFace(face_nodes, polygons_nodes, quantities);
6845 for (int iface = 0; iface < nbNew; iface++) {
6846 int nbNodes = quantities[iface];
6847 vector<const SMDS_MeshNode *> poly_nodes (nbNodes);
6848 for (int ii = 0; ii < nbNodes; ii++, inode++) {
6849 poly_nodes[ii] = polygons_nodes[inode];
6851 SMDS_MeshElement* newElem = aMesh->AddPolygonalFace(poly_nodes);
6852 myLastCreatedElems.Append(newElem);
6854 aMesh->SetMeshElementOnShape(newElem, aShapeId);
6857 MESSAGE("ChangeElementNodes MergeNodes Polygon");
6858 //aMesh->ChangeElementNodes(elem, &polygons_nodes[inode], quantities[nbNew - 1]);
6859 vector<const SMDS_MeshNode *> polynodes(polygons_nodes.begin()+inode,polygons_nodes.end());
6861 if (nbNew > 0) quid = nbNew - 1;
6862 vector<int> newquant(quantities.begin()+quid, quantities.end());
6863 const SMDS_MeshElement* newElem = 0;
6864 newElem = aMesh->AddPolyhedralVolume(polynodes, newquant);
6865 myLastCreatedElems.Append(newElem);
6866 if ( aShapeId && newElem )
6867 aMesh->SetMeshElementOnShape( newElem, aShapeId );
6868 rmElemIds.push_back(elem->GetID());
6871 rmElemIds.push_back(elem->GetID());
6875 else if (elem->GetType() == SMDSAbs_Volume) {
6876 // Polyhedral volume
6877 if (nbUniqueNodes < 4) {
6878 rmElemIds.push_back(elem->GetID());
6881 // each face has to be analyzed in order to check volume validity
6882 const SMDS_VtkVolume* aPolyedre =
6883 dynamic_cast<const SMDS_VtkVolume*>( elem );
6885 int nbFaces = aPolyedre->NbFaces();
6887 vector<const SMDS_MeshNode *> poly_nodes;
6888 vector<int> quantities;
6890 for (int iface = 1; iface <= nbFaces; iface++) {
6891 int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
6892 vector<const SMDS_MeshNode *> faceNodes (nbFaceNodes);
6894 for (int inode = 1; inode <= nbFaceNodes; inode++) {
6895 const SMDS_MeshNode * faceNode = aPolyedre->GetFaceNode(iface, inode);
6896 TNodeNodeMap::iterator nnIt = nodeNodeMap.find(faceNode);
6897 if (nnIt != nodeNodeMap.end()) { // faceNode sticks
6898 faceNode = (*nnIt).second;
6900 faceNodes[inode - 1] = faceNode;
6903 SimplifyFace(faceNodes, poly_nodes, quantities);
6906 if (quantities.size() > 3) {
6907 // to be done: remove coincident faces
6910 if (quantities.size() > 3)
6912 MESSAGE("ChangeElementNodes MergeNodes Polyhedron");
6913 //aMesh->ChangePolyhedronNodes(elem, poly_nodes, quantities);
6914 const SMDS_MeshElement* newElem = 0;
6915 newElem = aMesh->AddPolyhedralVolume(poly_nodes, quantities);
6916 myLastCreatedElems.Append(newElem);
6917 if ( aShapeId && newElem )
6918 aMesh->SetMeshElementOnShape( newElem, aShapeId );
6919 rmElemIds.push_back(elem->GetID());
6923 rmElemIds.push_back(elem->GetID());
6934 // TODO not all the possible cases are solved. Find something more generic?
6935 switch ( nbNodes ) {
6936 case 2: ///////////////////////////////////// EDGE
6937 isOk = false; break;
6938 case 3: ///////////////////////////////////// TRIANGLE
6939 isOk = false; break;
6941 if ( elem->GetType() == SMDSAbs_Volume ) // TETRAHEDRON
6943 else { //////////////////////////////////// QUADRANGLE
6944 if ( nbUniqueNodes < 3 )
6946 else if ( nbRepl == 2 && iRepl[ 1 ] - iRepl[ 0 ] == 2 )
6947 isOk = false; // opposite nodes stick
6948 //MESSAGE("isOk " << isOk);
6951 case 6: ///////////////////////////////////// PENTAHEDRON
6952 if ( nbUniqueNodes == 4 ) {
6953 // ---------------------------------> tetrahedron
6955 iRepl[ 0 ] > 2 && iRepl[ 1 ] > 2 && iRepl[ 2 ] > 2 ) {
6956 // all top nodes stick: reverse a bottom
6957 uniqueNodes[ 0 ] = curNodes [ 1 ];
6958 uniqueNodes[ 1 ] = curNodes [ 0 ];
6960 else if (nbRepl == 3 &&
6961 iRepl[ 0 ] < 3 && iRepl[ 1 ] < 3 && iRepl[ 2 ] < 3 ) {
6962 // all bottom nodes stick: set a top before
6963 uniqueNodes[ 3 ] = uniqueNodes [ 0 ];
6964 uniqueNodes[ 0 ] = curNodes [ 3 ];
6965 uniqueNodes[ 1 ] = curNodes [ 4 ];
6966 uniqueNodes[ 2 ] = curNodes [ 5 ];
6968 else if (nbRepl == 4 &&
6969 iRepl[ 2 ] - iRepl [ 0 ] == 3 && iRepl[ 3 ] - iRepl [ 1 ] == 3 ) {
6970 // a lateral face turns into a line: reverse a bottom
6971 uniqueNodes[ 0 ] = curNodes [ 1 ];
6972 uniqueNodes[ 1 ] = curNodes [ 0 ];
6977 else if ( nbUniqueNodes == 5 ) {
6978 // PENTAHEDRON --------------------> 2 tetrahedrons
6979 if ( nbRepl == 2 && iRepl[ 1 ] - iRepl [ 0 ] == 3 ) {
6980 // a bottom node sticks with a linked top one
6982 SMDS_MeshElement* newElem =
6983 aMesh->AddVolume(curNodes[ 3 ],
6986 curNodes[ iRepl[ 0 ] == 2 ? 1 : 2 ]);
6987 myLastCreatedElems.Append(newElem);
6989 aMesh->SetMeshElementOnShape( newElem, aShapeId );
6990 // 2. : reverse a bottom
6991 uniqueNodes[ 0 ] = curNodes [ 1 ];
6992 uniqueNodes[ 1 ] = curNodes [ 0 ];
7002 if(elem->IsQuadratic()) { // Quadratic quadrangle
7014 MESSAGE("nbRepl=2: " << iRepl[0] << " " << iRepl[1]);
7017 MESSAGE("nbRepl=3: " << iRepl[0] << " " << iRepl[1] << " " << iRepl[2]);
7019 if( iRepl[0]==0 && iRepl[1]==1 && iRepl[2]==4 ) {
7020 uniqueNodes[0] = curNodes[0];
7021 uniqueNodes[1] = curNodes[2];
7022 uniqueNodes[2] = curNodes[3];
7023 uniqueNodes[3] = curNodes[5];
7024 uniqueNodes[4] = curNodes[6];
7025 uniqueNodes[5] = curNodes[7];
7028 if( iRepl[0]==0 && iRepl[1]==3 && iRepl[2]==7 ) {
7029 uniqueNodes[0] = curNodes[0];
7030 uniqueNodes[1] = curNodes[1];
7031 uniqueNodes[2] = curNodes[2];
7032 uniqueNodes[3] = curNodes[4];
7033 uniqueNodes[4] = curNodes[5];
7034 uniqueNodes[5] = curNodes[6];
7037 if( iRepl[0]==0 && iRepl[1]==4 && iRepl[2]==7 ) {
7038 uniqueNodes[0] = curNodes[1];
7039 uniqueNodes[1] = curNodes[2];
7040 uniqueNodes[2] = curNodes[3];
7041 uniqueNodes[3] = curNodes[5];
7042 uniqueNodes[4] = curNodes[6];
7043 uniqueNodes[5] = curNodes[0];
7046 if( iRepl[0]==1 && iRepl[1]==2 && iRepl[2]==5 ) {
7047 uniqueNodes[0] = curNodes[0];
7048 uniqueNodes[1] = curNodes[1];
7049 uniqueNodes[2] = curNodes[3];
7050 uniqueNodes[3] = curNodes[4];
7051 uniqueNodes[4] = curNodes[6];
7052 uniqueNodes[5] = curNodes[7];
7055 if( iRepl[0]==1 && iRepl[1]==4 && iRepl[2]==5 ) {
7056 uniqueNodes[0] = curNodes[0];
7057 uniqueNodes[1] = curNodes[2];
7058 uniqueNodes[2] = curNodes[3];
7059 uniqueNodes[3] = curNodes[1];
7060 uniqueNodes[4] = curNodes[6];
7061 uniqueNodes[5] = curNodes[7];
7064 if( iRepl[0]==2 && iRepl[1]==3 && iRepl[2]==6 ) {
7065 uniqueNodes[0] = curNodes[0];
7066 uniqueNodes[1] = curNodes[1];
7067 uniqueNodes[2] = curNodes[2];
7068 uniqueNodes[3] = curNodes[4];
7069 uniqueNodes[4] = curNodes[5];
7070 uniqueNodes[5] = curNodes[7];
7073 if( iRepl[0]==2 && iRepl[1]==5 && iRepl[2]==6 ) {
7074 uniqueNodes[0] = curNodes[0];
7075 uniqueNodes[1] = curNodes[1];
7076 uniqueNodes[2] = curNodes[3];
7077 uniqueNodes[3] = curNodes[4];
7078 uniqueNodes[4] = curNodes[2];
7079 uniqueNodes[5] = curNodes[7];
7082 if( iRepl[0]==3 && iRepl[1]==6 && iRepl[2]==7 ) {
7083 uniqueNodes[0] = curNodes[0];
7084 uniqueNodes[1] = curNodes[1];
7085 uniqueNodes[2] = curNodes[2];
7086 uniqueNodes[3] = curNodes[4];
7087 uniqueNodes[4] = curNodes[5];
7088 uniqueNodes[5] = curNodes[3];
7093 MESSAGE("nbRepl=4: " << iRepl[0] << " " << iRepl[1] << " " << iRepl[2] << " " << iRepl[3]);
7096 MESSAGE("nbRepl=5: " << iRepl[0] << " " << iRepl[1] << " " << iRepl[2] << " " << iRepl[3] << " " << iRepl[4]);
7100 //////////////////////////////////// HEXAHEDRON
7102 SMDS_VolumeTool hexa (elem);
7103 hexa.SetExternalNormal();
7104 if ( nbUniqueNodes == 4 && nbRepl == 4 ) {
7105 //////////////////////// HEX ---> 1 tetrahedron
7106 for ( int iFace = 0; iFace < 6; iFace++ ) {
7107 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7108 if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7109 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7110 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7111 // one face turns into a point ...
7112 int iOppFace = hexa.GetOppFaceIndex( iFace );
7113 ind = hexa.GetFaceNodesIndices( iOppFace );
7115 for ( iCur = 0; iCur < 4 && nbStick < 2; iCur++ ) {
7116 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7119 if ( nbStick == 1 ) {
7120 // ... and the opposite one - into a triangle.
7122 ind = hexa.GetFaceNodesIndices( iFace );
7123 uniqueNodes[ 3 ] = curNodes[ind[ 0 ]];
7130 else if ( nbUniqueNodes == 6 && nbRepl == 2 ) {
7131 //////////////////////// HEX ---> 1 prism
7132 int nbTria = 0, iTria[3];
7133 const int *ind; // indices of face nodes
7134 // look for triangular faces
7135 for ( int iFace = 0; iFace < 6 && nbTria < 3; iFace++ ) {
7136 ind = hexa.GetFaceNodesIndices( iFace );
7137 TIDSortedNodeSet faceNodes;
7138 for ( iCur = 0; iCur < 4; iCur++ )
7139 faceNodes.insert( curNodes[ind[iCur]] );
7140 if ( faceNodes.size() == 3 )
7141 iTria[ nbTria++ ] = iFace;
7143 // check if triangles are opposite
7144 if ( nbTria == 2 && iTria[0] == hexa.GetOppFaceIndex( iTria[1] ))
7147 // set nodes of the bottom triangle
7148 ind = hexa.GetFaceNodesIndices( iTria[ 0 ]);
7150 for ( iCur = 0; iCur < 4; iCur++ )
7151 if ( ind[iCur] != iRepl[0] && ind[iCur] != iRepl[1])
7152 indB.push_back( ind[iCur] );
7153 if ( !hexa.IsForward() )
7154 std::swap( indB[0], indB[2] );
7155 for ( iCur = 0; iCur < 3; iCur++ )
7156 uniqueNodes[ iCur ] = curNodes[indB[iCur]];
7157 // set nodes of the top triangle
7158 const int *indT = hexa.GetFaceNodesIndices( iTria[ 1 ]);
7159 for ( iCur = 0; iCur < 3; ++iCur )
7160 for ( int j = 0; j < 4; ++j )
7161 if ( hexa.IsLinked( indB[ iCur ], indT[ j ] ))
7163 uniqueNodes[ iCur + 3 ] = curNodes[ indT[ j ]];
7169 else if (nbUniqueNodes == 5 && nbRepl == 4 ) {
7170 //////////////////// HEXAHEDRON ---> 2 tetrahedrons
7171 for ( int iFace = 0; iFace < 6; iFace++ ) {
7172 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7173 if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
7174 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
7175 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
7176 // one face turns into a point ...
7177 int iOppFace = hexa.GetOppFaceIndex( iFace );
7178 ind = hexa.GetFaceNodesIndices( iOppFace );
7180 iUnique = 2; // reverse a tetrahedron 1 bottom
7181 for ( iCur = 0; iCur < 4 && nbStick == 0; iCur++ ) {
7182 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
7184 else if ( iUnique >= 0 )
7185 uniqueNodes[ iUnique-- ] = curNodes[ind[ iCur ]];
7187 if ( nbStick == 0 ) {
7188 // ... and the opposite one is a quadrangle
7190 const int* indTop = hexa.GetFaceNodesIndices( iFace );
7191 uniqueNodes[ 3 ] = curNodes[indTop[ 0 ]];
7194 SMDS_MeshElement* newElem =
7195 aMesh->AddVolume(curNodes[ind[ 0 ]],
7198 curNodes[indTop[ 0 ]]);
7199 myLastCreatedElems.Append(newElem);
7201 aMesh->SetMeshElementOnShape( newElem, aShapeId );
7208 else if ( nbUniqueNodes == 6 && nbRepl == 4 ) {
7209 ////////////////// HEXAHEDRON ---> 2 tetrahedrons or 1 prism
7210 // find indices of quad and tri faces
7211 int iQuadFace[ 6 ], iTriFace[ 6 ], nbQuad = 0, nbTri = 0, iFace;
7212 for ( iFace = 0; iFace < 6; iFace++ ) {
7213 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
7215 for ( iCur = 0; iCur < 4; iCur++ )
7216 nodeSet.insert( curNodes[ind[ iCur ]] );
7217 nbUniqueNodes = nodeSet.size();
7218 if ( nbUniqueNodes == 3 )
7219 iTriFace[ nbTri++ ] = iFace;
7220 else if ( nbUniqueNodes == 4 )
7221 iQuadFace[ nbQuad++ ] = iFace;
7223 if (nbQuad == 2 && nbTri == 4 &&
7224 hexa.GetOppFaceIndex( iQuadFace[ 0 ] ) == iQuadFace[ 1 ]) {
7225 // 2 opposite quadrangles stuck with a diagonal;
7226 // sample groups of merged indices: (0-4)(2-6)
7227 // --------------------------------------------> 2 tetrahedrons
7228 const int *ind1 = hexa.GetFaceNodesIndices( iQuadFace[ 0 ]); // indices of quad1 nodes
7229 const int *ind2 = hexa.GetFaceNodesIndices( iQuadFace[ 1 ]);
7230 int i0, i1d, i2, i3d, i0t, i2t; // d-daigonal, t-top
7231 if (curNodes[ind1[ 0 ]] == curNodes[ind2[ 0 ]] &&
7232 curNodes[ind1[ 2 ]] == curNodes[ind2[ 2 ]]) {
7233 // stuck with 0-2 diagonal
7241 else if (curNodes[ind1[ 1 ]] == curNodes[ind2[ 3 ]] &&
7242 curNodes[ind1[ 3 ]] == curNodes[ind2[ 1 ]]) {
7243 // stuck with 1-3 diagonal
7255 uniqueNodes[ 0 ] = curNodes [ i0 ];
7256 uniqueNodes[ 1 ] = curNodes [ i1d ];
7257 uniqueNodes[ 2 ] = curNodes [ i3d ];
7258 uniqueNodes[ 3 ] = curNodes [ i0t ];
7261 SMDS_MeshElement* newElem = aMesh->AddVolume(curNodes[ i1d ],
7265 myLastCreatedElems.Append(newElem);
7267 aMesh->SetMeshElementOnShape( newElem, aShapeId );
7270 else if (( nbTri == 2 && nbQuad == 3 ) || // merged (0-4)(1-5)
7271 ( nbTri == 4 && nbQuad == 2 )) { // merged (7-4)(1-5)
7272 // --------------------------------------------> prism
7273 // find 2 opposite triangles
7275 for ( iFace = 0; iFace + 1 < nbTri; iFace++ ) {
7276 if ( hexa.GetOppFaceIndex( iTriFace[ iFace ] ) == iTriFace[ iFace + 1 ]) {
7277 // find indices of kept and replaced nodes
7278 // and fill unique nodes of 2 opposite triangles
7279 const int *ind1 = hexa.GetFaceNodesIndices( iTriFace[ iFace ]);
7280 const int *ind2 = hexa.GetFaceNodesIndices( iTriFace[ iFace + 1 ]);
7281 const SMDS_MeshNode** hexanodes = hexa.GetNodes();
7282 // fill unique nodes
7285 for ( iCur = 0; iCur < 4 && isOk; iCur++ ) {
7286 const SMDS_MeshNode* n = curNodes[ind1[ iCur ]];
7287 const SMDS_MeshNode* nInit = hexanodes[ind1[ iCur ]];
7289 // iCur of a linked node of the opposite face (make normals co-directed):
7290 int iCurOpp = ( iCur == 1 || iCur == 3 ) ? 4 - iCur : iCur;
7291 // check that correspondent corners of triangles are linked
7292 if ( !hexa.IsLinked( ind1[ iCur ], ind2[ iCurOpp ] ))
7295 uniqueNodes[ iUnique ] = n;
7296 uniqueNodes[ iUnique + 3 ] = curNodes[ind2[ iCurOpp ]];
7305 } // if ( nbUniqueNodes == 6 && nbRepl == 4 )
7308 MESSAGE("MergeNodes() removes hexahedron "<< elem);
7315 } // switch ( nbNodes )
7317 } // if ( nbNodes != nbUniqueNodes ) // some nodes stick
7319 if ( isOk ) { // the elem remains valid after sticking nodes
7320 if (elem->IsPoly() && elem->GetType() == SMDSAbs_Volume)
7322 // Change nodes of polyedre
7323 const SMDS_VtkVolume* aPolyedre =
7324 dynamic_cast<const SMDS_VtkVolume*>( elem );
7326 int nbFaces = aPolyedre->NbFaces();
7328 vector<const SMDS_MeshNode *> poly_nodes;
7329 vector<int> quantities (nbFaces);
7331 for (int iface = 1; iface <= nbFaces; iface++) {
7332 int inode, nbFaceNodes = aPolyedre->NbFaceNodes(iface);
7333 quantities[iface - 1] = nbFaceNodes;
7335 for (inode = 1; inode <= nbFaceNodes; inode++) {
7336 const SMDS_MeshNode* curNode = aPolyedre->GetFaceNode(iface, inode);
7338 TNodeNodeMap::iterator nnIt = nodeNodeMap.find( curNode );
7339 if (nnIt != nodeNodeMap.end()) { // curNode sticks
7340 curNode = (*nnIt).second;
7342 poly_nodes.push_back(curNode);
7345 aMesh->ChangePolyhedronNodes( elem, poly_nodes, quantities );
7348 else // replace non-polyhedron elements
7350 const SMDSAbs_ElementType etyp = elem->GetType();
7351 const int elemId = elem->GetID();
7352 const bool isPoly = (elem->GetEntityType() == SMDSEntity_Polygon);
7353 uniqueNodes.resize(nbUniqueNodes);
7355 SMESHDS_SubMesh * sm = aShapeId > 0 ? aMesh->MeshElements(aShapeId) : 0;
7357 aMesh->RemoveFreeElement(elem, sm, /*fromGroups=*/false);
7358 SMDS_MeshElement* newElem = this->AddElement(uniqueNodes, etyp, isPoly, elemId);
7359 if ( sm && newElem )
7360 sm->AddElement( newElem );
7361 if ( elem != newElem )
7362 ReplaceElemInGroups( elem, newElem, aMesh );
7366 // Remove invalid regular element or invalid polygon
7367 rmElemIds.push_back( elem->GetID() );
7370 } // loop on elements
7372 // Remove bad elements, then equal nodes (order important)
7374 Remove( rmElemIds, false );
7375 Remove( rmNodeIds, true );
7380 // ========================================================
7381 // class : SortableElement
7382 // purpose : allow sorting elements basing on their nodes
7383 // ========================================================
7384 class SortableElement : public set <const SMDS_MeshElement*>
7388 SortableElement( const SMDS_MeshElement* theElem )
7391 SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator();
7392 while ( nodeIt->more() )
7393 this->insert( nodeIt->next() );
7396 const SMDS_MeshElement* Get() const
7399 void Set(const SMDS_MeshElement* e) const
7404 mutable const SMDS_MeshElement* myElem;
7407 //=======================================================================
7408 //function : FindEqualElements
7409 //purpose : Return list of group of elements built on the same nodes.
7410 // Search among theElements or in the whole mesh if theElements is empty
7411 //=======================================================================
7413 void SMESH_MeshEditor::FindEqualElements(TIDSortedElemSet & theElements,
7414 TListOfListOfElementsID & theGroupsOfElementsID)
7416 myLastCreatedElems.Clear();
7417 myLastCreatedNodes.Clear();
7419 typedef map< SortableElement, int > TMapOfNodeSet;
7420 typedef list<int> TGroupOfElems;
7422 if ( theElements.empty() )
7423 { // get all elements in the mesh
7424 SMDS_ElemIteratorPtr eIt = GetMeshDS()->elementsIterator();
7425 while ( eIt->more() )
7426 theElements.insert( theElements.end(), eIt->next());
7429 vector< TGroupOfElems > arrayOfGroups;
7430 TGroupOfElems groupOfElems;
7431 TMapOfNodeSet mapOfNodeSet;
7433 TIDSortedElemSet::iterator elemIt = theElements.begin();
7434 for ( int i = 0, j=0; elemIt != theElements.end(); ++elemIt, ++j ) {
7435 const SMDS_MeshElement* curElem = *elemIt;
7436 SortableElement SE(curElem);
7439 pair< TMapOfNodeSet::iterator, bool> pp = mapOfNodeSet.insert(make_pair(SE, i));
7440 if( !(pp.second) ) {
7441 TMapOfNodeSet::iterator& itSE = pp.first;
7442 ind = (*itSE).second;
7443 arrayOfGroups[ind].push_back(curElem->GetID());
7446 groupOfElems.clear();
7447 groupOfElems.push_back(curElem->GetID());
7448 arrayOfGroups.push_back(groupOfElems);
7453 vector< TGroupOfElems >::iterator groupIt = arrayOfGroups.begin();
7454 for ( ; groupIt != arrayOfGroups.end(); ++groupIt ) {
7455 groupOfElems = *groupIt;
7456 if ( groupOfElems.size() > 1 ) {
7457 groupOfElems.sort();
7458 theGroupsOfElementsID.push_back(groupOfElems);
7463 //=======================================================================
7464 //function : MergeElements
7465 //purpose : In each given group, substitute all elements by the first one.
7466 //=======================================================================
7468 void SMESH_MeshEditor::MergeElements(TListOfListOfElementsID & theGroupsOfElementsID)
7470 myLastCreatedElems.Clear();
7471 myLastCreatedNodes.Clear();
7473 typedef list<int> TListOfIDs;
7474 TListOfIDs rmElemIds; // IDs of elems to remove
7476 SMESHDS_Mesh* aMesh = GetMeshDS();
7478 TListOfListOfElementsID::iterator groupsIt = theGroupsOfElementsID.begin();
7479 while ( groupsIt != theGroupsOfElementsID.end() ) {
7480 TListOfIDs& aGroupOfElemID = *groupsIt;
7481 aGroupOfElemID.sort();
7482 int elemIDToKeep = aGroupOfElemID.front();
7483 const SMDS_MeshElement* elemToKeep = aMesh->FindElement(elemIDToKeep);
7484 aGroupOfElemID.pop_front();
7485 TListOfIDs::iterator idIt = aGroupOfElemID.begin();
7486 while ( idIt != aGroupOfElemID.end() ) {
7487 int elemIDToRemove = *idIt;
7488 const SMDS_MeshElement* elemToRemove = aMesh->FindElement(elemIDToRemove);
7489 // add the kept element in groups of removed one (PAL15188)
7490 AddToSameGroups( elemToKeep, elemToRemove, aMesh );
7491 rmElemIds.push_back( elemIDToRemove );
7497 Remove( rmElemIds, false );
7500 //=======================================================================
7501 //function : MergeEqualElements
7502 //purpose : Remove all but one of elements built on the same nodes.
7503 //=======================================================================
7505 void SMESH_MeshEditor::MergeEqualElements()
7507 TIDSortedElemSet aMeshElements; /* empty input ==
7508 to merge equal elements in the whole mesh */
7509 TListOfListOfElementsID aGroupsOfElementsID;
7510 FindEqualElements(aMeshElements, aGroupsOfElementsID);
7511 MergeElements(aGroupsOfElementsID);
7514 //=======================================================================
7515 //function : findAdjacentFace
7517 //=======================================================================
7519 static const SMDS_MeshElement* findAdjacentFace(const SMDS_MeshNode* n1,
7520 const SMDS_MeshNode* n2,
7521 const SMDS_MeshElement* elem)
7523 TIDSortedElemSet elemSet, avoidSet;
7525 avoidSet.insert ( elem );
7526 return SMESH_MeshAlgos::FindFaceInSet( n1, n2, elemSet, avoidSet );
7529 //=======================================================================
7530 //function : FindFreeBorder
7532 //=======================================================================
7534 #define ControlFreeBorder SMESH::Controls::FreeEdges::IsFreeEdge
7536 bool SMESH_MeshEditor::FindFreeBorder (const SMDS_MeshNode* theFirstNode,
7537 const SMDS_MeshNode* theSecondNode,
7538 const SMDS_MeshNode* theLastNode,
7539 list< const SMDS_MeshNode* > & theNodes,
7540 list< const SMDS_MeshElement* >& theFaces)
7542 if ( !theFirstNode || !theSecondNode )
7544 // find border face between theFirstNode and theSecondNode
7545 const SMDS_MeshElement* curElem = findAdjacentFace( theFirstNode, theSecondNode, 0 );
7549 theFaces.push_back( curElem );
7550 theNodes.push_back( theFirstNode );
7551 theNodes.push_back( theSecondNode );
7553 //vector<const SMDS_MeshNode*> nodes;
7554 const SMDS_MeshNode *nIgnore = theFirstNode, *nStart = theSecondNode;
7555 TIDSortedElemSet foundElems;
7556 bool needTheLast = ( theLastNode != 0 );
7558 while ( nStart != theLastNode ) {
7559 if ( nStart == theFirstNode )
7560 return !needTheLast;
7562 // find all free border faces sharing form nStart
7564 list< const SMDS_MeshElement* > curElemList;
7565 list< const SMDS_MeshNode* > nStartList;
7566 SMDS_ElemIteratorPtr invElemIt = nStart->GetInverseElementIterator(SMDSAbs_Face);
7567 while ( invElemIt->more() ) {
7568 const SMDS_MeshElement* e = invElemIt->next();
7569 if ( e == curElem || foundElems.insert( e ).second ) {
7571 int iNode = 0, nbNodes = e->NbNodes();
7572 //const SMDS_MeshNode* nodes[nbNodes+1];
7573 vector<const SMDS_MeshNode*> nodes(nbNodes+1);
7575 if(e->IsQuadratic()) {
7576 const SMDS_VtkFace* F =
7577 dynamic_cast<const SMDS_VtkFace*>(e);
7578 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
7579 // use special nodes iterator
7580 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
7581 while( anIter->more() ) {
7582 nodes[ iNode++ ] = cast2Node(anIter->next());
7586 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
7587 while ( nIt->more() )
7588 nodes[ iNode++ ] = static_cast<const SMDS_MeshNode*>( nIt->next() );
7590 nodes[ iNode ] = nodes[ 0 ];
7592 for ( iNode = 0; iNode < nbNodes; iNode++ )
7593 if (((nodes[ iNode ] == nStart && nodes[ iNode + 1] != nIgnore ) ||
7594 (nodes[ iNode + 1] == nStart && nodes[ iNode ] != nIgnore )) &&
7595 ControlFreeBorder( &nodes[ iNode ], e->GetID() ))
7597 nStartList.push_back( nodes[ iNode + ( nodes[ iNode ] == nStart ? 1 : 0 )]);
7598 curElemList.push_back( e );
7602 // analyse the found
7604 int nbNewBorders = curElemList.size();
7605 if ( nbNewBorders == 0 ) {
7606 // no free border furthermore
7607 return !needTheLast;
7609 else if ( nbNewBorders == 1 ) {
7610 // one more element found
7612 nStart = nStartList.front();
7613 curElem = curElemList.front();
7614 theFaces.push_back( curElem );
7615 theNodes.push_back( nStart );
7618 // several continuations found
7619 list< const SMDS_MeshElement* >::iterator curElemIt;
7620 list< const SMDS_MeshNode* >::iterator nStartIt;
7621 // check if one of them reached the last node
7622 if ( needTheLast ) {
7623 for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
7624 curElemIt!= curElemList.end();
7625 curElemIt++, nStartIt++ )
7626 if ( *nStartIt == theLastNode ) {
7627 theFaces.push_back( *curElemIt );
7628 theNodes.push_back( *nStartIt );
7632 // find the best free border by the continuations
7633 list<const SMDS_MeshNode*> contNodes[ 2 ], *cNL;
7634 list<const SMDS_MeshElement*> contFaces[ 2 ], *cFL;
7635 for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
7636 curElemIt!= curElemList.end();
7637 curElemIt++, nStartIt++ )
7639 cNL = & contNodes[ contNodes[0].empty() ? 0 : 1 ];
7640 cFL = & contFaces[ contFaces[0].empty() ? 0 : 1 ];
7641 // find one more free border
7642 if ( ! SMESH_MeshEditor::FindFreeBorder( nStart, *nStartIt, theLastNode, *cNL, *cFL )) {
7646 else if ( !contNodes[0].empty() && !contNodes[1].empty() ) {
7647 // choice: clear a worse one
7648 int iLongest = ( contNodes[0].size() < contNodes[1].size() ? 1 : 0 );
7649 int iWorse = ( needTheLast ? 1 - iLongest : iLongest );
7650 contNodes[ iWorse ].clear();
7651 contFaces[ iWorse ].clear();
7654 if ( contNodes[0].empty() && contNodes[1].empty() )
7657 // append the best free border
7658 cNL = & contNodes[ contNodes[0].empty() ? 1 : 0 ];
7659 cFL = & contFaces[ contFaces[0].empty() ? 1 : 0 ];
7660 theNodes.pop_back(); // remove nIgnore
7661 theNodes.pop_back(); // remove nStart
7662 theFaces.pop_back(); // remove curElem
7663 list< const SMDS_MeshNode* >::iterator nIt = cNL->begin();
7664 list< const SMDS_MeshElement* >::iterator fIt = cFL->begin();
7665 for ( ; nIt != cNL->end(); nIt++ ) theNodes.push_back( *nIt );
7666 for ( ; fIt != cFL->end(); fIt++ ) theFaces.push_back( *fIt );
7669 } // several continuations found
7670 } // while ( nStart != theLastNode )
7675 //=======================================================================
7676 //function : CheckFreeBorderNodes
7677 //purpose : Return true if the tree nodes are on a free border
7678 //=======================================================================
7680 bool SMESH_MeshEditor::CheckFreeBorderNodes(const SMDS_MeshNode* theNode1,
7681 const SMDS_MeshNode* theNode2,
7682 const SMDS_MeshNode* theNode3)
7684 list< const SMDS_MeshNode* > nodes;
7685 list< const SMDS_MeshElement* > faces;
7686 return FindFreeBorder( theNode1, theNode2, theNode3, nodes, faces);
7689 //=======================================================================
7690 //function : SewFreeBorder
7692 //=======================================================================
7694 SMESH_MeshEditor::Sew_Error
7695 SMESH_MeshEditor::SewFreeBorder (const SMDS_MeshNode* theBordFirstNode,
7696 const SMDS_MeshNode* theBordSecondNode,
7697 const SMDS_MeshNode* theBordLastNode,
7698 const SMDS_MeshNode* theSideFirstNode,
7699 const SMDS_MeshNode* theSideSecondNode,
7700 const SMDS_MeshNode* theSideThirdNode,
7701 const bool theSideIsFreeBorder,
7702 const bool toCreatePolygons,
7703 const bool toCreatePolyedrs)
7705 myLastCreatedElems.Clear();
7706 myLastCreatedNodes.Clear();
7708 MESSAGE("::SewFreeBorder()");
7709 Sew_Error aResult = SEW_OK;
7711 // ====================================
7712 // find side nodes and elements
7713 // ====================================
7715 list< const SMDS_MeshNode* > nSide[ 2 ];
7716 list< const SMDS_MeshElement* > eSide[ 2 ];
7717 list< const SMDS_MeshNode* >::iterator nIt[ 2 ];
7718 list< const SMDS_MeshElement* >::iterator eIt[ 2 ];
7722 if (!FindFreeBorder(theBordFirstNode,theBordSecondNode,theBordLastNode,
7723 nSide[0], eSide[0])) {
7724 MESSAGE(" Free Border 1 not found " );
7725 aResult = SEW_BORDER1_NOT_FOUND;
7727 if (theSideIsFreeBorder) {
7730 if (!FindFreeBorder(theSideFirstNode, theSideSecondNode, theSideThirdNode,
7731 nSide[1], eSide[1])) {
7732 MESSAGE(" Free Border 2 not found " );
7733 aResult = ( aResult != SEW_OK ? SEW_BOTH_BORDERS_NOT_FOUND : SEW_BORDER2_NOT_FOUND );
7736 if ( aResult != SEW_OK )
7739 if (!theSideIsFreeBorder) {
7743 // -------------------------------------------------------------------------
7745 // 1. If nodes to merge are not coincident, move nodes of the free border
7746 // from the coord sys defined by the direction from the first to last
7747 // nodes of the border to the correspondent sys of the side 2
7748 // 2. On the side 2, find the links most co-directed with the correspondent
7749 // links of the free border
7750 // -------------------------------------------------------------------------
7752 // 1. Since sewing may break if there are volumes to split on the side 2,
7753 // we wont move nodes but just compute new coordinates for them
7754 typedef map<const SMDS_MeshNode*, gp_XYZ> TNodeXYZMap;
7755 TNodeXYZMap nBordXYZ;
7756 list< const SMDS_MeshNode* >& bordNodes = nSide[ 0 ];
7757 list< const SMDS_MeshNode* >::iterator nBordIt;
7759 gp_XYZ Pb1( theBordFirstNode->X(), theBordFirstNode->Y(), theBordFirstNode->Z() );
7760 gp_XYZ Pb2( theBordLastNode->X(), theBordLastNode->Y(), theBordLastNode->Z() );
7761 gp_XYZ Ps1( theSideFirstNode->X(), theSideFirstNode->Y(), theSideFirstNode->Z() );
7762 gp_XYZ Ps2( theSideSecondNode->X(), theSideSecondNode->Y(), theSideSecondNode->Z() );
7763 double tol2 = 1.e-8;
7764 gp_Vec Vbs1( Pb1 - Ps1 ),Vbs2( Pb2 - Ps2 );
7765 if ( Vbs1.SquareMagnitude() > tol2 || Vbs2.SquareMagnitude() > tol2 ) {
7766 // Need node movement.
7768 // find X and Z axes to create trsf
7769 gp_Vec Zb( Pb1 - Pb2 ), Zs( Ps1 - Ps2 );
7771 if ( X.SquareMagnitude() <= gp::Resolution() * gp::Resolution() )
7773 X = gp_Ax2( gp::Origin(), Zb ).XDirection();
7776 gp_Ax3 toBordAx( Pb1, Zb, X );
7777 gp_Ax3 fromSideAx( Ps1, Zs, X );
7778 gp_Ax3 toGlobalAx( gp::Origin(), gp::DZ(), gp::DX() );
7780 gp_Trsf toBordSys, fromSide2Sys;
7781 toBordSys.SetTransformation( toBordAx );
7782 fromSide2Sys.SetTransformation( fromSideAx, toGlobalAx );
7783 fromSide2Sys.SetScaleFactor( Zs.Magnitude() / Zb.Magnitude() );
7786 for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
7787 const SMDS_MeshNode* n = *nBordIt;
7788 gp_XYZ xyz( n->X(),n->Y(),n->Z() );
7789 toBordSys.Transforms( xyz );
7790 fromSide2Sys.Transforms( xyz );
7791 nBordXYZ.insert( TNodeXYZMap::value_type( n, xyz ));
7795 // just insert nodes XYZ in the nBordXYZ map
7796 for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
7797 const SMDS_MeshNode* n = *nBordIt;
7798 nBordXYZ.insert( TNodeXYZMap::value_type( n, gp_XYZ( n->X(),n->Y(),n->Z() )));
7802 // 2. On the side 2, find the links most co-directed with the correspondent
7803 // links of the free border
7805 list< const SMDS_MeshElement* >& sideElems = eSide[ 1 ];
7806 list< const SMDS_MeshNode* >& sideNodes = nSide[ 1 ];
7807 sideNodes.push_back( theSideFirstNode );
7809 bool hasVolumes = false;
7810 LinkID_Gen aLinkID_Gen( GetMeshDS() );
7811 set<long> foundSideLinkIDs, checkedLinkIDs;
7812 SMDS_VolumeTool volume;
7813 //const SMDS_MeshNode* faceNodes[ 4 ];
7815 const SMDS_MeshNode* sideNode;
7816 const SMDS_MeshElement* sideElem;
7817 const SMDS_MeshNode* prevSideNode = theSideFirstNode;
7818 const SMDS_MeshNode* prevBordNode = theBordFirstNode;
7819 nBordIt = bordNodes.begin();
7821 // border node position and border link direction to compare with
7822 gp_XYZ bordPos = nBordXYZ[ *nBordIt ];
7823 gp_XYZ bordDir = bordPos - nBordXYZ[ prevBordNode ];
7824 // choose next side node by link direction or by closeness to
7825 // the current border node:
7826 bool searchByDir = ( *nBordIt != theBordLastNode );
7828 // find the next node on the Side 2
7830 double maxDot = -DBL_MAX, minDist = DBL_MAX;
7832 checkedLinkIDs.clear();
7833 gp_XYZ prevXYZ( prevSideNode->X(), prevSideNode->Y(), prevSideNode->Z() );
7835 // loop on inverse elements of current node (prevSideNode) on the Side 2
7836 SMDS_ElemIteratorPtr invElemIt = prevSideNode->GetInverseElementIterator();
7837 while ( invElemIt->more() )
7839 const SMDS_MeshElement* elem = invElemIt->next();
7840 // prepare data for a loop on links coming to prevSideNode, of a face or a volume
7841 int iPrevNode, iNode = 0, nbNodes = elem->NbNodes();
7842 vector< const SMDS_MeshNode* > faceNodes( nbNodes, (const SMDS_MeshNode*)0 );
7843 bool isVolume = volume.Set( elem );
7844 const SMDS_MeshNode** nodes = isVolume ? volume.GetNodes() : & faceNodes[0];
7845 if ( isVolume ) // --volume
7847 else if ( elem->GetType()==SMDSAbs_Face ) { // --face
7848 // retrieve all face nodes and find iPrevNode - an index of the prevSideNode
7849 if(elem->IsQuadratic()) {
7850 const SMDS_VtkFace* F =
7851 dynamic_cast<const SMDS_VtkFace*>(elem);
7852 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
7853 // use special nodes iterator
7854 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
7855 while( anIter->more() ) {
7856 nodes[ iNode ] = cast2Node(anIter->next());
7857 if ( nodes[ iNode++ ] == prevSideNode )
7858 iPrevNode = iNode - 1;
7862 SMDS_ElemIteratorPtr nIt = elem->nodesIterator();
7863 while ( nIt->more() ) {
7864 nodes[ iNode ] = cast2Node( nIt->next() );
7865 if ( nodes[ iNode++ ] == prevSideNode )
7866 iPrevNode = iNode - 1;
7869 // there are 2 links to check
7874 // loop on links, to be precise, on the second node of links
7875 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
7876 const SMDS_MeshNode* n = nodes[ iNode ];
7878 if ( !volume.IsLinked( n, prevSideNode ))
7882 if ( iNode ) // a node before prevSideNode
7883 n = nodes[ iPrevNode == 0 ? elem->NbNodes() - 1 : iPrevNode - 1 ];
7884 else // a node after prevSideNode
7885 n = nodes[ iPrevNode + 1 == elem->NbNodes() ? 0 : iPrevNode + 1 ];
7887 // check if this link was already used
7888 long iLink = aLinkID_Gen.GetLinkID( prevSideNode, n );
7889 bool isJustChecked = !checkedLinkIDs.insert( iLink ).second;
7890 if (!isJustChecked &&
7891 foundSideLinkIDs.find( iLink ) == foundSideLinkIDs.end() )
7893 // test a link geometrically
7894 gp_XYZ nextXYZ ( n->X(), n->Y(), n->Z() );
7895 bool linkIsBetter = false;
7896 double dot = 0.0, dist = 0.0;
7897 if ( searchByDir ) { // choose most co-directed link
7898 dot = bordDir * ( nextXYZ - prevXYZ ).Normalized();
7899 linkIsBetter = ( dot > maxDot );
7901 else { // choose link with the node closest to bordPos
7902 dist = ( nextXYZ - bordPos ).SquareModulus();
7903 linkIsBetter = ( dist < minDist );
7905 if ( linkIsBetter ) {
7914 } // loop on inverse elements of prevSideNode
7917 MESSAGE(" Cant find path by links of the Side 2 ");
7918 return SEW_BAD_SIDE_NODES;
7920 sideNodes.push_back( sideNode );
7921 sideElems.push_back( sideElem );
7922 foundSideLinkIDs.insert ( linkID );
7923 prevSideNode = sideNode;
7925 if ( *nBordIt == theBordLastNode )
7926 searchByDir = false;
7928 // find the next border link to compare with
7929 gp_XYZ sidePos( sideNode->X(), sideNode->Y(), sideNode->Z() );
7930 searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
7931 // move to next border node if sideNode is before forward border node (bordPos)
7932 while ( *nBordIt != theBordLastNode && !searchByDir ) {
7933 prevBordNode = *nBordIt;
7935 bordPos = nBordXYZ[ *nBordIt ];
7936 bordDir = bordPos - nBordXYZ[ prevBordNode ];
7937 searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
7941 while ( sideNode != theSideSecondNode );
7943 if ( hasVolumes && sideNodes.size () != bordNodes.size() && !toCreatePolyedrs) {
7944 MESSAGE("VOLUME SPLITTING IS FORBIDDEN");
7945 return SEW_VOLUMES_TO_SPLIT; // volume splitting is forbidden
7947 } // end nodes search on the side 2
7949 // ============================
7950 // sew the border to the side 2
7951 // ============================
7953 int nbNodes[] = { nSide[0].size(), nSide[1].size() };
7954 int maxNbNodes = Max( nbNodes[0], nbNodes[1] );
7956 TListOfListOfNodes nodeGroupsToMerge;
7957 if ( nbNodes[0] == nbNodes[1] ||
7958 ( theSideIsFreeBorder && !theSideThirdNode)) {
7960 // all nodes are to be merged
7962 for (nIt[0] = nSide[0].begin(), nIt[1] = nSide[1].begin();
7963 nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end();
7964 nIt[0]++, nIt[1]++ )
7966 nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
7967 nodeGroupsToMerge.back().push_back( *nIt[1] ); // to keep
7968 nodeGroupsToMerge.back().push_back( *nIt[0] ); // to remove
7973 // insert new nodes into the border and the side to get equal nb of segments
7975 // get normalized parameters of nodes on the borders
7976 //double param[ 2 ][ maxNbNodes ];
7978 param[0] = new double [ maxNbNodes ];
7979 param[1] = new double [ maxNbNodes ];
7981 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
7982 list< const SMDS_MeshNode* >& nodes = nSide[ iBord ];
7983 list< const SMDS_MeshNode* >::iterator nIt = nodes.begin();
7984 const SMDS_MeshNode* nPrev = *nIt;
7985 double bordLength = 0;
7986 for ( iNode = 0; nIt != nodes.end(); nIt++, iNode++ ) { // loop on border nodes
7987 const SMDS_MeshNode* nCur = *nIt;
7988 gp_XYZ segment (nCur->X() - nPrev->X(),
7989 nCur->Y() - nPrev->Y(),
7990 nCur->Z() - nPrev->Z());
7991 double segmentLen = segment.Modulus();
7992 bordLength += segmentLen;
7993 param[ iBord ][ iNode ] = bordLength;
7996 // normalize within [0,1]
7997 for ( iNode = 0; iNode < nbNodes[ iBord ]; iNode++ ) {
7998 param[ iBord ][ iNode ] /= bordLength;
8002 // loop on border segments
8003 const SMDS_MeshNode *nPrev[ 2 ] = { 0, 0 };
8004 int i[ 2 ] = { 0, 0 };
8005 nIt[0] = nSide[0].begin(); eIt[0] = eSide[0].begin();
8006 nIt[1] = nSide[1].begin(); eIt[1] = eSide[1].begin();
8008 TElemOfNodeListMap insertMap;
8009 TElemOfNodeListMap::iterator insertMapIt;
8011 // key: elem to insert nodes into
8012 // value: 2 nodes to insert between + nodes to be inserted
8014 bool next[ 2 ] = { false, false };
8016 // find min adjacent segment length after sewing
8017 double nextParam = 10., prevParam = 0;
8018 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8019 if ( i[ iBord ] + 1 < nbNodes[ iBord ])
8020 nextParam = Min( nextParam, param[iBord][ i[iBord] + 1 ]);
8021 if ( i[ iBord ] > 0 )
8022 prevParam = Max( prevParam, param[iBord][ i[iBord] - 1 ]);
8024 double minParam = Min( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8025 double maxParam = Max( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
8026 double minSegLen = Min( nextParam - minParam, maxParam - prevParam );
8028 // choose to insert or to merge nodes
8029 double du = param[ 1 ][ i[1] ] - param[ 0 ][ i[0] ];
8030 if ( Abs( du ) <= minSegLen * 0.2 ) {
8033 nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
8034 const SMDS_MeshNode* n0 = *nIt[0];
8035 const SMDS_MeshNode* n1 = *nIt[1];
8036 nodeGroupsToMerge.back().push_back( n1 );
8037 nodeGroupsToMerge.back().push_back( n0 );
8038 // position of node of the border changes due to merge
8039 param[ 0 ][ i[0] ] += du;
8040 // move n1 for the sake of elem shape evaluation during insertion.
8041 // n1 will be removed by MergeNodes() anyway
8042 const_cast<SMDS_MeshNode*>( n0 )->setXYZ( n1->X(), n1->Y(), n1->Z() );
8043 next[0] = next[1] = true;
8048 int intoBord = ( du < 0 ) ? 0 : 1;
8049 const SMDS_MeshElement* elem = *eIt[ intoBord ];
8050 const SMDS_MeshNode* n1 = nPrev[ intoBord ];
8051 const SMDS_MeshNode* n2 = *nIt[ intoBord ];
8052 const SMDS_MeshNode* nIns = *nIt[ 1 - intoBord ];
8053 if ( intoBord == 1 ) {
8054 // move node of the border to be on a link of elem of the side
8055 gp_XYZ p1 (n1->X(), n1->Y(), n1->Z());
8056 gp_XYZ p2 (n2->X(), n2->Y(), n2->Z());
8057 double ratio = du / ( param[ 1 ][ i[1] ] - param[ 1 ][ i[1]-1 ]);
8058 gp_XYZ p = p2 * ( 1 - ratio ) + p1 * ratio;
8059 GetMeshDS()->MoveNode( nIns, p.X(), p.Y(), p.Z() );
8061 insertMapIt = insertMap.find( elem );
8062 bool notFound = ( insertMapIt == insertMap.end() );
8063 bool otherLink = ( !notFound && (*insertMapIt).second.front() != n1 );
8065 // insert into another link of the same element:
8066 // 1. perform insertion into the other link of the elem
8067 list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8068 const SMDS_MeshNode* n12 = nodeList.front(); nodeList.pop_front();
8069 const SMDS_MeshNode* n22 = nodeList.front(); nodeList.pop_front();
8070 InsertNodesIntoLink( elem, n12, n22, nodeList, toCreatePolygons );
8071 // 2. perform insertion into the link of adjacent faces
8073 const SMDS_MeshElement* adjElem = findAdjacentFace( n12, n22, elem );
8075 InsertNodesIntoLink( adjElem, n12, n22, nodeList, toCreatePolygons );
8079 if (toCreatePolyedrs) {
8080 // perform insertion into the links of adjacent volumes
8081 UpdateVolumes(n12, n22, nodeList);
8083 // 3. find an element appeared on n1 and n2 after the insertion
8084 insertMap.erase( elem );
8085 elem = findAdjacentFace( n1, n2, 0 );
8087 if ( notFound || otherLink ) {
8088 // add element and nodes of the side into the insertMap
8089 insertMapIt = insertMap.insert
8090 ( TElemOfNodeListMap::value_type( elem, list<const SMDS_MeshNode*>() )).first;
8091 (*insertMapIt).second.push_back( n1 );
8092 (*insertMapIt).second.push_back( n2 );
8094 // add node to be inserted into elem
8095 (*insertMapIt).second.push_back( nIns );
8096 next[ 1 - intoBord ] = true;
8099 // go to the next segment
8100 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
8101 if ( next[ iBord ] ) {
8102 if ( i[ iBord ] != 0 && eIt[ iBord ] != eSide[ iBord ].end())
8104 nPrev[ iBord ] = *nIt[ iBord ];
8105 nIt[ iBord ]++; i[ iBord ]++;
8109 while ( nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end());
8111 // perform insertion of nodes into elements
8113 for (insertMapIt = insertMap.begin();
8114 insertMapIt != insertMap.end();
8117 const SMDS_MeshElement* elem = (*insertMapIt).first;
8118 list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
8119 const SMDS_MeshNode* n1 = nodeList.front(); nodeList.pop_front();
8120 const SMDS_MeshNode* n2 = nodeList.front(); nodeList.pop_front();
8122 InsertNodesIntoLink( elem, n1, n2, nodeList, toCreatePolygons );
8124 if ( !theSideIsFreeBorder ) {
8125 // look for and insert nodes into the faces adjacent to elem
8127 const SMDS_MeshElement* adjElem = findAdjacentFace( n1, n2, elem );
8129 InsertNodesIntoLink( adjElem, n1, n2, nodeList, toCreatePolygons );
8134 if (toCreatePolyedrs) {
8135 // perform insertion into the links of adjacent volumes
8136 UpdateVolumes(n1, n2, nodeList);
8142 } // end: insert new nodes
8144 MergeNodes ( nodeGroupsToMerge );
8149 //=======================================================================
8150 //function : InsertNodesIntoLink
8151 //purpose : insert theNodesToInsert into theFace between theBetweenNode1
8152 // and theBetweenNode2 and split theElement
8153 //=======================================================================
8155 void SMESH_MeshEditor::InsertNodesIntoLink(const SMDS_MeshElement* theFace,
8156 const SMDS_MeshNode* theBetweenNode1,
8157 const SMDS_MeshNode* theBetweenNode2,
8158 list<const SMDS_MeshNode*>& theNodesToInsert,
8159 const bool toCreatePoly)
8161 if ( theFace->GetType() != SMDSAbs_Face ) return;
8163 // find indices of 2 link nodes and of the rest nodes
8164 int iNode = 0, il1, il2, i3, i4;
8165 il1 = il2 = i3 = i4 = -1;
8166 //const SMDS_MeshNode* nodes[ theFace->NbNodes() ];
8167 vector<const SMDS_MeshNode*> nodes( theFace->NbNodes() );
8169 if(theFace->IsQuadratic()) {
8170 const SMDS_VtkFace* F =
8171 dynamic_cast<const SMDS_VtkFace*>(theFace);
8172 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8173 // use special nodes iterator
8174 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8175 while( anIter->more() ) {
8176 const SMDS_MeshNode* n = cast2Node(anIter->next());
8177 if ( n == theBetweenNode1 )
8179 else if ( n == theBetweenNode2 )
8185 nodes[ iNode++ ] = n;
8189 SMDS_ElemIteratorPtr nodeIt = theFace->nodesIterator();
8190 while ( nodeIt->more() ) {
8191 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8192 if ( n == theBetweenNode1 )
8194 else if ( n == theBetweenNode2 )
8200 nodes[ iNode++ ] = n;
8203 if ( il1 < 0 || il2 < 0 || i3 < 0 )
8206 // arrange link nodes to go one after another regarding the face orientation
8207 bool reverse = ( Abs( il2 - il1 ) == 1 ? il2 < il1 : il1 < il2 );
8208 list<const SMDS_MeshNode *> aNodesToInsert = theNodesToInsert;
8213 aNodesToInsert.reverse();
8215 // check that not link nodes of a quadrangles are in good order
8216 int nbFaceNodes = theFace->NbNodes();
8217 if ( nbFaceNodes == 4 && i4 - i3 != 1 ) {
8223 if (toCreatePoly || theFace->IsPoly()) {
8226 vector<const SMDS_MeshNode *> poly_nodes (nbFaceNodes + aNodesToInsert.size());
8228 // add nodes of face up to first node of link
8231 if(theFace->IsQuadratic()) {
8232 const SMDS_VtkFace* F =
8233 dynamic_cast<const SMDS_VtkFace*>(theFace);
8234 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
8235 // use special nodes iterator
8236 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
8237 while( anIter->more() && !isFLN ) {
8238 const SMDS_MeshNode* n = cast2Node(anIter->next());
8239 poly_nodes[iNode++] = n;
8240 if (n == nodes[il1]) {
8244 // add nodes to insert
8245 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8246 for (; nIt != aNodesToInsert.end(); nIt++) {
8247 poly_nodes[iNode++] = *nIt;
8249 // add nodes of face starting from last node of link
8250 while ( anIter->more() ) {
8251 poly_nodes[iNode++] = cast2Node(anIter->next());
8255 SMDS_ElemIteratorPtr nodeIt = theFace->nodesIterator();
8256 while ( nodeIt->more() && !isFLN ) {
8257 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8258 poly_nodes[iNode++] = n;
8259 if (n == nodes[il1]) {
8263 // add nodes to insert
8264 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8265 for (; nIt != aNodesToInsert.end(); nIt++) {
8266 poly_nodes[iNode++] = *nIt;
8268 // add nodes of face starting from last node of link
8269 while ( nodeIt->more() ) {
8270 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8271 poly_nodes[iNode++] = n;
8275 // edit or replace the face
8276 SMESHDS_Mesh *aMesh = GetMeshDS();
8278 if (theFace->IsPoly()) {
8279 aMesh->ChangePolygonNodes(theFace, poly_nodes);
8282 int aShapeId = FindShape( theFace );
8284 SMDS_MeshElement* newElem = aMesh->AddPolygonalFace(poly_nodes);
8285 myLastCreatedElems.Append(newElem);
8286 if ( aShapeId && newElem )
8287 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8289 aMesh->RemoveElement(theFace);
8294 SMESHDS_Mesh *aMesh = GetMeshDS();
8295 if( !theFace->IsQuadratic() ) {
8297 // put aNodesToInsert between theBetweenNode1 and theBetweenNode2
8298 int nbLinkNodes = 2 + aNodesToInsert.size();
8299 //const SMDS_MeshNode* linkNodes[ nbLinkNodes ];
8300 vector<const SMDS_MeshNode*> linkNodes( nbLinkNodes );
8301 linkNodes[ 0 ] = nodes[ il1 ];
8302 linkNodes[ nbLinkNodes - 1 ] = nodes[ il2 ];
8303 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8304 for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
8305 linkNodes[ iNode++ ] = *nIt;
8307 // decide how to split a quadrangle: compare possible variants
8308 // and choose which of splits to be a quadrangle
8309 int i1, i2, iSplit, nbSplits = nbLinkNodes - 1, iBestQuad;
8310 if ( nbFaceNodes == 3 ) {
8311 iBestQuad = nbSplits;
8314 else if ( nbFaceNodes == 4 ) {
8315 SMESH::Controls::NumericalFunctorPtr aCrit( new SMESH::Controls::AspectRatio);
8316 double aBestRate = DBL_MAX;
8317 for ( int iQuad = 0; iQuad < nbSplits; iQuad++ ) {
8319 double aBadRate = 0;
8320 // evaluate elements quality
8321 for ( iSplit = 0; iSplit < nbSplits; iSplit++ ) {
8322 if ( iSplit == iQuad ) {
8323 SMDS_FaceOfNodes quad (linkNodes[ i1++ ],
8327 aBadRate += getBadRate( &quad, aCrit );
8330 SMDS_FaceOfNodes tria (linkNodes[ i1++ ],
8332 nodes[ iSplit < iQuad ? i4 : i3 ]);
8333 aBadRate += getBadRate( &tria, aCrit );
8337 if ( aBadRate < aBestRate ) {
8339 aBestRate = aBadRate;
8344 // create new elements
8345 int aShapeId = FindShape( theFace );
8348 for ( iSplit = 0; iSplit < nbSplits - 1; iSplit++ ) {
8349 SMDS_MeshElement* newElem = 0;
8350 if ( iSplit == iBestQuad )
8351 newElem = aMesh->AddFace (linkNodes[ i1++ ],
8356 newElem = aMesh->AddFace (linkNodes[ i1++ ],
8358 nodes[ iSplit < iBestQuad ? i4 : i3 ]);
8359 myLastCreatedElems.Append(newElem);
8360 if ( aShapeId && newElem )
8361 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8364 // change nodes of theFace
8365 const SMDS_MeshNode* newNodes[ 4 ];
8366 newNodes[ 0 ] = linkNodes[ i1 ];
8367 newNodes[ 1 ] = linkNodes[ i2 ];
8368 newNodes[ 2 ] = nodes[ iSplit >= iBestQuad ? i3 : i4 ];
8369 newNodes[ 3 ] = nodes[ i4 ];
8370 //aMesh->ChangeElementNodes( theFace, newNodes, iSplit == iBestQuad ? 4 : 3 );
8371 const SMDS_MeshElement* newElem = 0;
8372 if (iSplit == iBestQuad)
8373 newElem = aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2], newNodes[3] );
8375 newElem = aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2] );
8376 myLastCreatedElems.Append(newElem);
8377 if ( aShapeId && newElem )
8378 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8379 } // end if(!theFace->IsQuadratic())
8380 else { // theFace is quadratic
8381 // we have to split theFace on simple triangles and one simple quadrangle
8383 int nbshift = tmp*2;
8384 // shift nodes in nodes[] by nbshift
8386 for(i=0; i<nbshift; i++) {
8387 const SMDS_MeshNode* n = nodes[0];
8388 for(j=0; j<nbFaceNodes-1; j++) {
8389 nodes[j] = nodes[j+1];
8391 nodes[nbFaceNodes-1] = n;
8393 il1 = il1 - nbshift;
8394 // now have to insert nodes between n0 and n1 or n1 and n2 (see below)
8395 // n0 n1 n2 n0 n1 n2
8396 // +-----+-----+ +-----+-----+
8405 // create new elements
8406 int aShapeId = FindShape( theFace );
8409 if(nbFaceNodes==6) { // quadratic triangle
8410 SMDS_MeshElement* newElem =
8411 aMesh->AddFace(nodes[3],nodes[4],nodes[5]);
8412 myLastCreatedElems.Append(newElem);
8413 if ( aShapeId && newElem )
8414 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8415 if(theFace->IsMediumNode(nodes[il1])) {
8416 // create quadrangle
8417 newElem = aMesh->AddFace(nodes[0],nodes[1],nodes[3],nodes[5]);
8418 myLastCreatedElems.Append(newElem);
8419 if ( aShapeId && newElem )
8420 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8426 // create quadrangle
8427 newElem = aMesh->AddFace(nodes[1],nodes[2],nodes[3],nodes[5]);
8428 myLastCreatedElems.Append(newElem);
8429 if ( aShapeId && newElem )
8430 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8436 else { // nbFaceNodes==8 - quadratic quadrangle
8437 SMDS_MeshElement* newElem =
8438 aMesh->AddFace(nodes[3],nodes[4],nodes[5]);
8439 myLastCreatedElems.Append(newElem);
8440 if ( aShapeId && newElem )
8441 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8442 newElem = aMesh->AddFace(nodes[5],nodes[6],nodes[7]);
8443 myLastCreatedElems.Append(newElem);
8444 if ( aShapeId && newElem )
8445 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8446 newElem = aMesh->AddFace(nodes[5],nodes[7],nodes[3]);
8447 myLastCreatedElems.Append(newElem);
8448 if ( aShapeId && newElem )
8449 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8450 if(theFace->IsMediumNode(nodes[il1])) {
8451 // create quadrangle
8452 newElem = aMesh->AddFace(nodes[0],nodes[1],nodes[3],nodes[7]);
8453 myLastCreatedElems.Append(newElem);
8454 if ( aShapeId && newElem )
8455 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8461 // create quadrangle
8462 newElem = aMesh->AddFace(nodes[1],nodes[2],nodes[3],nodes[7]);
8463 myLastCreatedElems.Append(newElem);
8464 if ( aShapeId && newElem )
8465 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8471 // create needed triangles using n1,n2,n3 and inserted nodes
8472 int nbn = 2 + aNodesToInsert.size();
8473 //const SMDS_MeshNode* aNodes[nbn];
8474 vector<const SMDS_MeshNode*> aNodes(nbn);
8475 aNodes[0] = nodes[n1];
8476 aNodes[nbn-1] = nodes[n2];
8477 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8478 for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
8479 aNodes[iNode++] = *nIt;
8481 for(i=1; i<nbn; i++) {
8482 SMDS_MeshElement* newElem =
8483 aMesh->AddFace(aNodes[i-1],aNodes[i],nodes[n3]);
8484 myLastCreatedElems.Append(newElem);
8485 if ( aShapeId && newElem )
8486 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8490 aMesh->RemoveElement(theFace);
8493 //=======================================================================
8494 //function : UpdateVolumes
8496 //=======================================================================
8497 void SMESH_MeshEditor::UpdateVolumes (const SMDS_MeshNode* theBetweenNode1,
8498 const SMDS_MeshNode* theBetweenNode2,
8499 list<const SMDS_MeshNode*>& theNodesToInsert)
8501 myLastCreatedElems.Clear();
8502 myLastCreatedNodes.Clear();
8504 SMDS_ElemIteratorPtr invElemIt = theBetweenNode1->GetInverseElementIterator(SMDSAbs_Volume);
8505 while (invElemIt->more()) { // loop on inverse elements of theBetweenNode1
8506 const SMDS_MeshElement* elem = invElemIt->next();
8508 // check, if current volume has link theBetweenNode1 - theBetweenNode2
8509 SMDS_VolumeTool aVolume (elem);
8510 if (!aVolume.IsLinked(theBetweenNode1, theBetweenNode2))
8513 // insert new nodes in all faces of the volume, sharing link theBetweenNode1 - theBetweenNode2
8514 int iface, nbFaces = aVolume.NbFaces();
8515 vector<const SMDS_MeshNode *> poly_nodes;
8516 vector<int> quantities (nbFaces);
8518 for (iface = 0; iface < nbFaces; iface++) {
8519 int nbFaceNodes = aVolume.NbFaceNodes(iface), nbInserted = 0;
8520 // faceNodes will contain (nbFaceNodes + 1) nodes, last = first
8521 const SMDS_MeshNode** faceNodes = aVolume.GetFaceNodes(iface);
8523 for (int inode = 0; inode < nbFaceNodes; inode++) {
8524 poly_nodes.push_back(faceNodes[inode]);
8526 if (nbInserted == 0) {
8527 if (faceNodes[inode] == theBetweenNode1) {
8528 if (faceNodes[inode + 1] == theBetweenNode2) {
8529 nbInserted = theNodesToInsert.size();
8531 // add nodes to insert
8532 list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.begin();
8533 for (; nIt != theNodesToInsert.end(); nIt++) {
8534 poly_nodes.push_back(*nIt);
8538 else if (faceNodes[inode] == theBetweenNode2) {
8539 if (faceNodes[inode + 1] == theBetweenNode1) {
8540 nbInserted = theNodesToInsert.size();
8542 // add nodes to insert in reversed order
8543 list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.end();
8545 for (; nIt != theNodesToInsert.begin(); nIt--) {
8546 poly_nodes.push_back(*nIt);
8548 poly_nodes.push_back(*nIt);
8555 quantities[iface] = nbFaceNodes + nbInserted;
8558 // Replace or update the volume
8559 SMESHDS_Mesh *aMesh = GetMeshDS();
8561 if (elem->IsPoly()) {
8562 aMesh->ChangePolyhedronNodes(elem, poly_nodes, quantities);
8566 int aShapeId = FindShape( elem );
8568 SMDS_MeshElement* newElem =
8569 aMesh->AddPolyhedralVolume(poly_nodes, quantities);
8570 myLastCreatedElems.Append(newElem);
8571 if (aShapeId && newElem)
8572 aMesh->SetMeshElementOnShape(newElem, aShapeId);
8574 aMesh->RemoveElement(elem);
8581 //================================================================================
8583 * \brief Transform any volume into data of SMDSEntity_Polyhedra
8585 //================================================================================
8587 void volumeToPolyhedron( const SMDS_MeshElement* elem,
8588 vector<const SMDS_MeshNode *> & nodes,
8589 vector<int> & nbNodeInFaces )
8592 nbNodeInFaces.clear();
8593 SMDS_VolumeTool vTool ( elem );
8594 for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
8596 const SMDS_MeshNode** fNodes = vTool.GetFaceNodes( iF );
8597 nodes.insert( nodes.end(), fNodes, fNodes + vTool.NbFaceNodes( iF ));
8598 nbNodeInFaces.push_back( vTool.NbFaceNodes( iF ));
8603 //=======================================================================
8605 * \brief Convert elements contained in a submesh to quadratic
8606 * \return int - nb of checked elements
8608 //=======================================================================
8610 int SMESH_MeshEditor::convertElemToQuadratic(SMESHDS_SubMesh * theSm,
8611 SMESH_MesherHelper& theHelper,
8612 const bool theForce3d)
8615 if( !theSm ) return nbElem;
8617 vector<int> nbNodeInFaces;
8618 vector<const SMDS_MeshNode *> nodes;
8619 SMDS_ElemIteratorPtr ElemItr = theSm->GetElements();
8620 while(ElemItr->more())
8623 const SMDS_MeshElement* elem = ElemItr->next();
8624 if( !elem ) continue;
8626 // analyse a necessity of conversion
8627 const SMDSAbs_ElementType aType = elem->GetType();
8628 if ( aType < SMDSAbs_Edge || aType > SMDSAbs_Volume )
8630 const SMDSAbs_EntityType aGeomType = elem->GetEntityType();
8631 bool hasCentralNodes = false;
8632 if ( elem->IsQuadratic() )
8635 switch ( aGeomType ) {
8636 case SMDSEntity_Quad_Triangle:
8637 case SMDSEntity_Quad_Quadrangle:
8638 case SMDSEntity_Quad_Hexa:
8639 alreadyOK = !theHelper.GetIsBiQuadratic(); break;
8641 case SMDSEntity_BiQuad_Triangle:
8642 case SMDSEntity_BiQuad_Quadrangle:
8643 case SMDSEntity_TriQuad_Hexa:
8644 alreadyOK = theHelper.GetIsBiQuadratic();
8645 hasCentralNodes = true;
8650 // take into account already present modium nodes
8652 case SMDSAbs_Volume:
8653 theHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( elem )); break;
8655 theHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( elem )); break;
8657 theHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( elem )); break;
8663 // get elem data needed to re-create it
8665 const int id = elem->GetID();
8666 const int nbNodes = elem->NbCornerNodes();
8667 nodes.assign(elem->begin_nodes(), elem->end_nodes());
8668 if ( aGeomType == SMDSEntity_Polyhedra )
8669 nbNodeInFaces = static_cast<const SMDS_VtkVolume* >( elem )->GetQuantities();
8670 else if ( aGeomType == SMDSEntity_Hexagonal_Prism )
8671 volumeToPolyhedron( elem, nodes, nbNodeInFaces );
8673 // remove a linear element
8674 GetMeshDS()->RemoveFreeElement(elem, theSm, /*fromGroups=*/false);
8676 // remove central nodes of biquadratic elements (biquad->quad convertion)
8677 if ( hasCentralNodes )
8678 for ( size_t i = nbNodes * 2; i < nodes.size(); ++i )
8679 if ( nodes[i]->NbInverseElements() == 0 )
8680 GetMeshDS()->RemoveFreeNode( nodes[i], theSm, /*fromGroups=*/true );
8682 const SMDS_MeshElement* NewElem = 0;
8688 NewElem = theHelper.AddEdge(nodes[0], nodes[1], id, theForce3d);
8696 NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
8699 NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
8702 NewElem = theHelper.AddPolygonalFace(nodes, id, theForce3d);
8706 case SMDSAbs_Volume :
8710 case SMDSEntity_Tetra:
8711 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
8713 case SMDSEntity_Pyramid:
8714 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], id, theForce3d);
8716 case SMDSEntity_Penta:
8717 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], nodes[5], id, theForce3d);
8719 case SMDSEntity_Hexa:
8720 case SMDSEntity_Quad_Hexa:
8721 case SMDSEntity_TriQuad_Hexa:
8722 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
8723 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
8725 case SMDSEntity_Hexagonal_Prism:
8727 NewElem = theHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
8734 ReplaceElemInGroups( elem, NewElem, GetMeshDS());
8735 if( NewElem && NewElem->getshapeId() < 1 )
8736 theSm->AddElement( NewElem );
8740 //=======================================================================
8741 //function : ConvertToQuadratic
8743 //=======================================================================
8745 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d, const bool theToBiQuad)
8747 SMESHDS_Mesh* meshDS = GetMeshDS();
8749 SMESH_MesherHelper aHelper(*myMesh);
8751 aHelper.SetIsQuadratic( true );
8752 aHelper.SetIsBiQuadratic( theToBiQuad );
8753 aHelper.SetElementsOnShape(true);
8755 // convert elements assigned to sub-meshes
8756 int nbCheckedElems = 0;
8757 if ( myMesh->HasShapeToMesh() )
8759 if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
8761 SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
8762 while ( smIt->more() ) {
8763 SMESH_subMesh* sm = smIt->next();
8764 if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() ) {
8765 aHelper.SetSubShape( sm->GetSubShape() );
8766 nbCheckedElems += convertElemToQuadratic(smDS, aHelper, theForce3d);
8772 // convert elements NOT assigned to sub-meshes
8773 int totalNbElems = meshDS->NbEdges() + meshDS->NbFaces() + meshDS->NbVolumes();
8774 if ( nbCheckedElems < totalNbElems ) // not all elements are in sub-meshes
8776 aHelper.SetElementsOnShape(false);
8777 SMESHDS_SubMesh *smDS = 0;
8780 SMDS_EdgeIteratorPtr aEdgeItr = meshDS->edgesIterator();
8781 while( aEdgeItr->more() )
8783 const SMDS_MeshEdge* edge = aEdgeItr->next();
8784 if ( !edge->IsQuadratic() )
8786 int id = edge->GetID();
8787 const SMDS_MeshNode* n1 = edge->GetNode(0);
8788 const SMDS_MeshNode* n2 = edge->GetNode(1);
8790 meshDS->RemoveFreeElement(edge, smDS, /*fromGroups=*/false);
8792 const SMDS_MeshEdge* NewEdge = aHelper.AddEdge(n1, n2, id, theForce3d);
8793 ReplaceElemInGroups( edge, NewEdge, GetMeshDS());
8797 aHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( edge ));
8802 SMDS_FaceIteratorPtr aFaceItr = meshDS->facesIterator();
8803 while( aFaceItr->more() )
8805 const SMDS_MeshFace* face = aFaceItr->next();
8806 if ( !face ) continue;
8808 const SMDSAbs_EntityType type = face->GetEntityType();
8812 case SMDSEntity_Quad_Triangle:
8813 case SMDSEntity_Quad_Quadrangle:
8814 alreadyOK = !theToBiQuad;
8815 aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
8817 case SMDSEntity_BiQuad_Triangle:
8818 case SMDSEntity_BiQuad_Quadrangle:
8819 alreadyOK = theToBiQuad;
8820 aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
8822 default: alreadyOK = false;
8827 const int id = face->GetID();
8828 vector<const SMDS_MeshNode *> nodes ( face->begin_nodes(), face->end_nodes());
8830 meshDS->RemoveFreeElement(face, smDS, /*fromGroups=*/false);
8832 SMDS_MeshFace * NewFace = 0;
8835 case SMDSEntity_Triangle:
8836 case SMDSEntity_Quad_Triangle:
8837 case SMDSEntity_BiQuad_Triangle:
8838 NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
8839 if ( nodes.size() == 7 && nodes[6]->NbInverseElements() == 0 ) // rm a central node
8840 GetMeshDS()->RemoveFreeNode( nodes[6], /*sm=*/0, /*fromGroups=*/true );
8843 case SMDSEntity_Quadrangle:
8844 case SMDSEntity_Quad_Quadrangle:
8845 case SMDSEntity_BiQuad_Quadrangle:
8846 NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
8847 if ( nodes.size() == 9 && nodes[8]->NbInverseElements() == 0 ) // rm a central node
8848 GetMeshDS()->RemoveFreeNode( nodes[8], /*sm=*/0, /*fromGroups=*/true );
8852 NewFace = aHelper.AddPolygonalFace(nodes, id, theForce3d);
8854 ReplaceElemInGroups( face, NewFace, GetMeshDS());
8858 vector<int> nbNodeInFaces;
8859 SMDS_VolumeIteratorPtr aVolumeItr = meshDS->volumesIterator();
8860 while(aVolumeItr->more())
8862 const SMDS_MeshVolume* volume = aVolumeItr->next();
8863 if ( !volume ) continue;
8865 const SMDSAbs_EntityType type = volume->GetEntityType();
8866 if ( volume->IsQuadratic() )
8871 case SMDSEntity_Quad_Hexa: alreadyOK = !theToBiQuad; break;
8872 case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; break;
8873 default: alreadyOK = true;
8877 aHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( volume ));
8881 const int id = volume->GetID();
8882 vector<const SMDS_MeshNode *> nodes (volume->begin_nodes(), volume->end_nodes());
8883 if ( type == SMDSEntity_Polyhedra )
8884 nbNodeInFaces = static_cast<const SMDS_VtkVolume* >(volume)->GetQuantities();
8885 else if ( type == SMDSEntity_Hexagonal_Prism )
8886 volumeToPolyhedron( volume, nodes, nbNodeInFaces );
8888 meshDS->RemoveFreeElement(volume, smDS, /*fromGroups=*/false);
8890 SMDS_MeshVolume * NewVolume = 0;
8893 case SMDSEntity_Tetra:
8894 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d );
8896 case SMDSEntity_Hexa:
8897 case SMDSEntity_Quad_Hexa:
8898 case SMDSEntity_TriQuad_Hexa:
8899 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
8900 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
8901 for ( size_t i = 20; i < nodes.size(); ++i ) // rm central nodes
8902 if ( nodes[i]->NbInverseElements() == 0 )
8903 GetMeshDS()->RemoveFreeNode( nodes[i], /*sm=*/0, /*fromGroups=*/true );
8905 case SMDSEntity_Pyramid:
8906 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
8907 nodes[3], nodes[4], id, theForce3d);
8909 case SMDSEntity_Penta:
8910 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
8911 nodes[3], nodes[4], nodes[5], id, theForce3d);
8913 case SMDSEntity_Hexagonal_Prism:
8915 NewVolume = aHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
8917 ReplaceElemInGroups(volume, NewVolume, meshDS);
8922 { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
8923 // aHelper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
8924 // aHelper.FixQuadraticElements(myError);
8925 SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
8929 //================================================================================
8931 * \brief Makes given elements quadratic
8932 * \param theForce3d - if true, the medium nodes will be placed in the middle of link
8933 * \param theElements - elements to make quadratic
8935 //================================================================================
8937 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d,
8938 TIDSortedElemSet& theElements,
8939 const bool theToBiQuad)
8941 if ( theElements.empty() ) return;
8943 // we believe that all theElements are of the same type
8944 const SMDSAbs_ElementType elemType = (*theElements.begin())->GetType();
8946 // get all nodes shared by theElements
8947 TIDSortedNodeSet allNodes;
8948 TIDSortedElemSet::iterator eIt = theElements.begin();
8949 for ( ; eIt != theElements.end(); ++eIt )
8950 allNodes.insert( (*eIt)->begin_nodes(), (*eIt)->end_nodes() );
8952 // complete theElements with elements of lower dim whose all nodes are in allNodes
8954 TIDSortedElemSet quadAdjacentElems [ SMDSAbs_NbElementTypes ]; // quadratic adjacent elements
8955 TIDSortedElemSet checkedAdjacentElems [ SMDSAbs_NbElementTypes ];
8956 TIDSortedNodeSet::iterator nIt = allNodes.begin();
8957 for ( ; nIt != allNodes.end(); ++nIt )
8959 const SMDS_MeshNode* n = *nIt;
8960 SMDS_ElemIteratorPtr invIt = n->GetInverseElementIterator();
8961 while ( invIt->more() )
8963 const SMDS_MeshElement* e = invIt->next();
8964 const SMDSAbs_ElementType type = e->GetType();
8965 if ( e->IsQuadratic() )
8967 quadAdjacentElems[ type ].insert( e );
8970 switch ( e->GetEntityType() ) {
8971 case SMDSEntity_Quad_Triangle:
8972 case SMDSEntity_Quad_Quadrangle:
8973 case SMDSEntity_Quad_Hexa: alreadyOK = !theToBiQuad; break;
8974 case SMDSEntity_BiQuad_Triangle:
8975 case SMDSEntity_BiQuad_Quadrangle:
8976 case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; break;
8977 default: alreadyOK = true;
8982 if ( type >= elemType )
8983 continue; // same type or more complex linear element
8985 if ( !checkedAdjacentElems[ type ].insert( e ).second )
8986 continue; // e is already checked
8990 SMDS_NodeIteratorPtr nodeIt = e->nodeIterator();
8991 while ( nodeIt->more() && allIn )
8992 allIn = allNodes.count( nodeIt->next() );
8994 theElements.insert(e );
8998 SMESH_MesherHelper helper(*myMesh);
8999 helper.SetIsQuadratic( true );
9000 helper.SetIsBiQuadratic( theToBiQuad );
9002 // add links of quadratic adjacent elements to the helper
9004 if ( !quadAdjacentElems[SMDSAbs_Edge].empty() )
9005 for ( eIt = quadAdjacentElems[SMDSAbs_Edge].begin();
9006 eIt != quadAdjacentElems[SMDSAbs_Edge].end(); ++eIt )
9008 helper.AddTLinks( static_cast< const SMDS_MeshEdge*> (*eIt) );
9010 if ( !quadAdjacentElems[SMDSAbs_Face].empty() )
9011 for ( eIt = quadAdjacentElems[SMDSAbs_Face].begin();
9012 eIt != quadAdjacentElems[SMDSAbs_Face].end(); ++eIt )
9014 helper.AddTLinks( static_cast< const SMDS_MeshFace*> (*eIt) );
9016 if ( !quadAdjacentElems[SMDSAbs_Volume].empty() )
9017 for ( eIt = quadAdjacentElems[SMDSAbs_Volume].begin();
9018 eIt != quadAdjacentElems[SMDSAbs_Volume].end(); ++eIt )
9020 helper.AddTLinks( static_cast< const SMDS_MeshVolume*> (*eIt) );
9023 // make quadratic (or bi-tri-quadratic) elements instead of linear ones
9025 SMESHDS_Mesh* meshDS = GetMeshDS();
9026 SMESHDS_SubMesh* smDS = 0;
9027 for ( eIt = theElements.begin(); eIt != theElements.end(); ++eIt )
9029 const SMDS_MeshElement* elem = *eIt;
9032 int nbCentralNodes = 0;
9033 switch ( elem->GetEntityType() ) {
9034 // linear convertible
9035 case SMDSEntity_Edge:
9036 case SMDSEntity_Triangle:
9037 case SMDSEntity_Quadrangle:
9038 case SMDSEntity_Tetra:
9039 case SMDSEntity_Pyramid:
9040 case SMDSEntity_Hexa:
9041 case SMDSEntity_Penta: alreadyOK = false; nbCentralNodes = 0; break;
9042 // quadratic that can become bi-quadratic
9043 case SMDSEntity_Quad_Triangle:
9044 case SMDSEntity_Quad_Quadrangle:
9045 case SMDSEntity_Quad_Hexa: alreadyOK =!theToBiQuad; nbCentralNodes = 0; break;
9047 case SMDSEntity_BiQuad_Triangle:
9048 case SMDSEntity_BiQuad_Quadrangle: alreadyOK = theToBiQuad; nbCentralNodes = 1; break;
9049 case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; nbCentralNodes = 7; break;
9051 default: alreadyOK = true;
9053 if ( alreadyOK ) continue;
9055 const SMDSAbs_ElementType type = elem->GetType();
9056 const int id = elem->GetID();
9057 const int nbNodes = elem->NbCornerNodes();
9058 vector<const SMDS_MeshNode *> nodes ( elem->begin_nodes(), elem->end_nodes());
9060 helper.SetSubShape( elem->getshapeId() );
9062 if ( !smDS || !smDS->Contains( elem ))
9063 smDS = meshDS->MeshElements( elem->getshapeId() );
9064 meshDS->RemoveFreeElement(elem, smDS, /*fromGroups=*/false);
9066 SMDS_MeshElement * newElem = 0;
9069 case 4: // cases for most frequently used element types go first (for optimization)
9070 if ( type == SMDSAbs_Volume )
9071 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9073 newElem = helper.AddFace (nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
9076 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9077 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
9080 newElem = helper.AddFace (nodes[0], nodes[1], nodes[2], id, theForce3d);
9083 newElem = helper.AddEdge(nodes[0], nodes[1], id, theForce3d);
9086 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9087 nodes[4], id, theForce3d);
9090 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
9091 nodes[4], nodes[5], id, theForce3d);
9095 ReplaceElemInGroups( elem, newElem, meshDS);
9096 if( newElem && smDS )
9097 smDS->AddElement( newElem );
9099 // remove central nodes
9100 for ( size_t i = nodes.size() - nbCentralNodes; i < nodes.size(); ++i )
9101 if ( nodes[i]->NbInverseElements() == 0 )
9102 meshDS->RemoveFreeNode( nodes[i], smDS, /*fromGroups=*/true );
9104 } // loop on theElements
9107 { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
9108 // helper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
9109 // helper.FixQuadraticElements( myError );
9110 SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
9114 //=======================================================================
9116 * \brief Convert quadratic elements to linear ones and remove quadratic nodes
9117 * \return int - nb of checked elements
9119 //=======================================================================
9121 int SMESH_MeshEditor::removeQuadElem(SMESHDS_SubMesh * theSm,
9122 SMDS_ElemIteratorPtr theItr,
9123 const int theShapeID)
9126 SMESHDS_Mesh* meshDS = GetMeshDS();
9128 while( theItr->more() )
9130 const SMDS_MeshElement* elem = theItr->next();
9132 if( elem && elem->IsQuadratic())
9134 int id = elem->GetID();
9135 int nbCornerNodes = elem->NbCornerNodes();
9136 SMDSAbs_ElementType aType = elem->GetType();
9138 vector<const SMDS_MeshNode *> nodes( elem->begin_nodes(), elem->end_nodes() );
9140 //remove a quadratic element
9141 if ( !theSm || !theSm->Contains( elem ))
9142 theSm = meshDS->MeshElements( elem->getshapeId() );
9143 meshDS->RemoveFreeElement( elem, theSm, /*fromGroups=*/false );
9145 // remove medium nodes
9146 for ( unsigned i = nbCornerNodes; i < nodes.size(); ++i )
9147 if ( nodes[i]->NbInverseElements() == 0 )
9148 meshDS->RemoveFreeNode( nodes[i], theSm );
9150 // add a linear element
9151 nodes.resize( nbCornerNodes );
9152 SMDS_MeshElement * newElem = AddElement( nodes, aType, false, id );
9153 ReplaceElemInGroups(elem, newElem, meshDS);
9154 if( theSm && newElem )
9155 theSm->AddElement( newElem );
9161 //=======================================================================
9162 //function : ConvertFromQuadratic
9164 //=======================================================================
9166 bool SMESH_MeshEditor::ConvertFromQuadratic()
9168 int nbCheckedElems = 0;
9169 if ( myMesh->HasShapeToMesh() )
9171 if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
9173 SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
9174 while ( smIt->more() ) {
9175 SMESH_subMesh* sm = smIt->next();
9176 if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() )
9177 nbCheckedElems += removeQuadElem( smDS, smDS->GetElements(), sm->GetId() );
9183 GetMeshDS()->NbEdges() + GetMeshDS()->NbFaces() + GetMeshDS()->NbVolumes();
9184 if ( nbCheckedElems < totalNbElems ) // not all elements are in submeshes
9186 SMESHDS_SubMesh *aSM = 0;
9187 removeQuadElem( aSM, GetMeshDS()->elementsIterator(), 0 );
9195 //================================================================================
9197 * \brief Return true if all medium nodes of the element are in the node set
9199 //================================================================================
9201 bool allMediumNodesIn(const SMDS_MeshElement* elem, TIDSortedNodeSet& nodeSet )
9203 for ( int i = elem->NbCornerNodes(); i < elem->NbNodes(); ++i )
9204 if ( !nodeSet.count( elem->GetNode(i) ))
9210 //================================================================================
9212 * \brief Makes given elements linear
9214 //================================================================================
9216 void SMESH_MeshEditor::ConvertFromQuadratic(TIDSortedElemSet& theElements)
9218 if ( theElements.empty() ) return;
9220 // collect IDs of medium nodes of theElements; some of these nodes will be removed
9221 set<int> mediumNodeIDs;
9222 TIDSortedElemSet::iterator eIt = theElements.begin();
9223 for ( ; eIt != theElements.end(); ++eIt )
9225 const SMDS_MeshElement* e = *eIt;
9226 for ( int i = e->NbCornerNodes(); i < e->NbNodes(); ++i )
9227 mediumNodeIDs.insert( e->GetNode(i)->GetID() );
9230 // replace given elements by linear ones
9231 SMDS_ElemIteratorPtr elemIt = elemSetIterator( theElements );
9232 removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9234 // we need to convert remaining elements whose all medium nodes are in mediumNodeIDs
9235 // except those elements sharing medium nodes of quadratic element whose medium nodes
9236 // are not all in mediumNodeIDs
9238 // get remaining medium nodes
9239 TIDSortedNodeSet mediumNodes;
9240 set<int>::iterator nIdsIt = mediumNodeIDs.begin();
9241 for ( ; nIdsIt != mediumNodeIDs.end(); ++nIdsIt )
9242 if ( const SMDS_MeshNode* n = GetMeshDS()->FindNode( *nIdsIt ))
9243 mediumNodes.insert( mediumNodes.end(), n );
9245 // find more quadratic elements to convert
9246 TIDSortedElemSet moreElemsToConvert;
9247 TIDSortedNodeSet::iterator nIt = mediumNodes.begin();
9248 for ( ; nIt != mediumNodes.end(); ++nIt )
9250 SMDS_ElemIteratorPtr invIt = (*nIt)->GetInverseElementIterator();
9251 while ( invIt->more() )
9253 const SMDS_MeshElement* e = invIt->next();
9254 if ( e->IsQuadratic() && allMediumNodesIn( e, mediumNodes ))
9256 // find a more complex element including e and
9257 // whose medium nodes are not in mediumNodes
9258 bool complexFound = false;
9259 for ( int type = e->GetType() + 1; type < SMDSAbs_0DElement; ++type )
9261 SMDS_ElemIteratorPtr invIt2 =
9262 (*nIt)->GetInverseElementIterator( SMDSAbs_ElementType( type ));
9263 while ( invIt2->more() )
9265 const SMDS_MeshElement* eComplex = invIt2->next();
9266 if ( eComplex->IsQuadratic() && !allMediumNodesIn( eComplex, mediumNodes))
9268 int nbCommonNodes = SMESH_MeshAlgos::GetCommonNodes( e, eComplex ).size();
9269 if ( nbCommonNodes == e->NbNodes())
9271 complexFound = true;
9272 type = SMDSAbs_NbElementTypes; // to quit from the outer loop
9278 if ( !complexFound )
9279 moreElemsToConvert.insert( e );
9283 elemIt = elemSetIterator( moreElemsToConvert );
9284 removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
9287 //=======================================================================
9288 //function : SewSideElements
9290 //=======================================================================
9292 SMESH_MeshEditor::Sew_Error
9293 SMESH_MeshEditor::SewSideElements (TIDSortedElemSet& theSide1,
9294 TIDSortedElemSet& theSide2,
9295 const SMDS_MeshNode* theFirstNode1,
9296 const SMDS_MeshNode* theFirstNode2,
9297 const SMDS_MeshNode* theSecondNode1,
9298 const SMDS_MeshNode* theSecondNode2)
9300 myLastCreatedElems.Clear();
9301 myLastCreatedNodes.Clear();
9303 MESSAGE ("::::SewSideElements()");
9304 if ( theSide1.size() != theSide2.size() )
9305 return SEW_DIFF_NB_OF_ELEMENTS;
9307 Sew_Error aResult = SEW_OK;
9309 // 1. Build set of faces representing each side
9310 // 2. Find which nodes of the side 1 to merge with ones on the side 2
9311 // 3. Replace nodes in elements of the side 1 and remove replaced nodes
9313 // =======================================================================
9314 // 1. Build set of faces representing each side:
9315 // =======================================================================
9316 // a. build set of nodes belonging to faces
9317 // b. complete set of faces: find missing faces whose nodes are in set of nodes
9318 // c. create temporary faces representing side of volumes if correspondent
9319 // face does not exist
9321 SMESHDS_Mesh* aMesh = GetMeshDS();
9322 // TODO algoritm not OK with vtkUnstructuredGrid: 2 meshes can't share nodes
9323 //SMDS_Mesh aTmpFacesMesh; // try to use the same mesh
9324 TIDSortedElemSet faceSet1, faceSet2;
9325 set<const SMDS_MeshElement*> volSet1, volSet2;
9326 set<const SMDS_MeshNode*> nodeSet1, nodeSet2;
9327 TIDSortedElemSet * faceSetPtr[] = { &faceSet1, &faceSet2 };
9328 set<const SMDS_MeshElement*> * volSetPtr[] = { &volSet1, &volSet2 };
9329 set<const SMDS_MeshNode*> * nodeSetPtr[] = { &nodeSet1, &nodeSet2 };
9330 TIDSortedElemSet * elemSetPtr[] = { &theSide1, &theSide2 };
9331 int iSide, iFace, iNode;
9333 list<const SMDS_MeshElement* > tempFaceList;
9334 for ( iSide = 0; iSide < 2; iSide++ ) {
9335 set<const SMDS_MeshNode*> * nodeSet = nodeSetPtr[ iSide ];
9336 TIDSortedElemSet * elemSet = elemSetPtr[ iSide ];
9337 TIDSortedElemSet * faceSet = faceSetPtr[ iSide ];
9338 set<const SMDS_MeshElement*> * volSet = volSetPtr [ iSide ];
9339 set<const SMDS_MeshElement*>::iterator vIt;
9340 TIDSortedElemSet::iterator eIt;
9341 set<const SMDS_MeshNode*>::iterator nIt;
9343 // check that given nodes belong to given elements
9344 const SMDS_MeshNode* n1 = ( iSide == 0 ) ? theFirstNode1 : theFirstNode2;
9345 const SMDS_MeshNode* n2 = ( iSide == 0 ) ? theSecondNode1 : theSecondNode2;
9346 int firstIndex = -1, secondIndex = -1;
9347 for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9348 const SMDS_MeshElement* elem = *eIt;
9349 if ( firstIndex < 0 ) firstIndex = elem->GetNodeIndex( n1 );
9350 if ( secondIndex < 0 ) secondIndex = elem->GetNodeIndex( n2 );
9351 if ( firstIndex > -1 && secondIndex > -1 ) break;
9353 if ( firstIndex < 0 || secondIndex < 0 ) {
9354 // we can simply return until temporary faces created
9355 return (iSide == 0 ) ? SEW_BAD_SIDE1_NODES : SEW_BAD_SIDE2_NODES;
9358 // -----------------------------------------------------------
9359 // 1a. Collect nodes of existing faces
9360 // and build set of face nodes in order to detect missing
9361 // faces corresponding to sides of volumes
9362 // -----------------------------------------------------------
9364 set< set <const SMDS_MeshNode*> > setOfFaceNodeSet;
9366 // loop on the given element of a side
9367 for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
9368 //const SMDS_MeshElement* elem = *eIt;
9369 const SMDS_MeshElement* elem = *eIt;
9370 if ( elem->GetType() == SMDSAbs_Face ) {
9371 faceSet->insert( elem );
9372 set <const SMDS_MeshNode*> faceNodeSet;
9373 SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
9374 while ( nodeIt->more() ) {
9375 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9376 nodeSet->insert( n );
9377 faceNodeSet.insert( n );
9379 setOfFaceNodeSet.insert( faceNodeSet );
9381 else if ( elem->GetType() == SMDSAbs_Volume )
9382 volSet->insert( elem );
9384 // ------------------------------------------------------------------------------
9385 // 1b. Complete set of faces: find missing faces whose nodes are in set of nodes
9386 // ------------------------------------------------------------------------------
9388 for ( nIt = nodeSet->begin(); nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
9389 SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
9390 while ( fIt->more() ) { // loop on faces sharing a node
9391 const SMDS_MeshElement* f = fIt->next();
9392 if ( faceSet->find( f ) == faceSet->end() ) {
9393 // check if all nodes are in nodeSet and
9394 // complete setOfFaceNodeSet if they are
9395 set <const SMDS_MeshNode*> faceNodeSet;
9396 SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
9397 bool allInSet = true;
9398 while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
9399 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9400 if ( nodeSet->find( n ) == nodeSet->end() )
9403 faceNodeSet.insert( n );
9406 faceSet->insert( f );
9407 setOfFaceNodeSet.insert( faceNodeSet );
9413 // -------------------------------------------------------------------------
9414 // 1c. Create temporary faces representing sides of volumes if correspondent
9415 // face does not exist
9416 // -------------------------------------------------------------------------
9418 if ( !volSet->empty() ) {
9419 //int nodeSetSize = nodeSet->size();
9421 // loop on given volumes
9422 for ( vIt = volSet->begin(); vIt != volSet->end(); vIt++ ) {
9423 SMDS_VolumeTool vol (*vIt);
9424 // loop on volume faces: find free faces
9425 // --------------------------------------
9426 list<const SMDS_MeshElement* > freeFaceList;
9427 for ( iFace = 0; iFace < vol.NbFaces(); iFace++ ) {
9428 if ( !vol.IsFreeFace( iFace ))
9430 // check if there is already a face with same nodes in a face set
9431 const SMDS_MeshElement* aFreeFace = 0;
9432 const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iFace );
9433 int nbNodes = vol.NbFaceNodes( iFace );
9434 set <const SMDS_MeshNode*> faceNodeSet;
9435 vol.GetFaceNodes( iFace, faceNodeSet );
9436 bool isNewFace = setOfFaceNodeSet.insert( faceNodeSet ).second;
9438 // no such a face is given but it still can exist, check it
9439 vector<const SMDS_MeshNode *> nodes ( fNodes, fNodes + nbNodes);
9440 aFreeFace = aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false );
9443 // create a temporary face
9444 if ( nbNodes == 3 ) {
9445 //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2] );
9446 aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2] );
9448 else if ( nbNodes == 4 ) {
9449 //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
9450 aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
9453 vector<const SMDS_MeshNode *> poly_nodes ( fNodes, & fNodes[nbNodes]);
9454 //aFreeFace = aTmpFacesMesh.AddPolygonalFace(poly_nodes);
9455 aFreeFace = aMesh->AddPolygonalFace(poly_nodes);
9458 tempFaceList.push_back( aFreeFace );
9462 freeFaceList.push_back( aFreeFace );
9464 } // loop on faces of a volume
9466 // choose one of several free faces of a volume
9467 // --------------------------------------------
9468 if ( freeFaceList.size() > 1 ) {
9469 // choose a face having max nb of nodes shared by other elems of a side
9470 int maxNbNodes = -1;
9471 list<const SMDS_MeshElement* >::iterator fIt = freeFaceList.begin();
9472 while ( fIt != freeFaceList.end() ) { // loop on free faces
9473 int nbSharedNodes = 0;
9474 SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
9475 while ( nodeIt->more() ) { // loop on free face nodes
9476 const SMDS_MeshNode* n =
9477 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9478 SMDS_ElemIteratorPtr invElemIt = n->GetInverseElementIterator();
9479 while ( invElemIt->more() ) {
9480 const SMDS_MeshElement* e = invElemIt->next();
9481 nbSharedNodes += faceSet->count( e );
9482 nbSharedNodes += elemSet->count( e );
9485 if ( nbSharedNodes > maxNbNodes ) {
9486 maxNbNodes = nbSharedNodes;
9487 freeFaceList.erase( freeFaceList.begin(), fIt++ );
9489 else if ( nbSharedNodes == maxNbNodes ) {
9493 freeFaceList.erase( fIt++ ); // here fIt++ occurs before erase
9496 if ( freeFaceList.size() > 1 )
9498 // could not choose one face, use another way
9499 // choose a face most close to the bary center of the opposite side
9500 gp_XYZ aBC( 0., 0., 0. );
9501 set <const SMDS_MeshNode*> addedNodes;
9502 TIDSortedElemSet * elemSet2 = elemSetPtr[ 1 - iSide ];
9503 eIt = elemSet2->begin();
9504 for ( eIt = elemSet2->begin(); eIt != elemSet2->end(); eIt++ ) {
9505 SMDS_ElemIteratorPtr nodeIt = (*eIt)->nodesIterator();
9506 while ( nodeIt->more() ) { // loop on free face nodes
9507 const SMDS_MeshNode* n =
9508 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9509 if ( addedNodes.insert( n ).second )
9510 aBC += gp_XYZ( n->X(),n->Y(),n->Z() );
9513 aBC /= addedNodes.size();
9514 double minDist = DBL_MAX;
9515 fIt = freeFaceList.begin();
9516 while ( fIt != freeFaceList.end() ) { // loop on free faces
9518 SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
9519 while ( nodeIt->more() ) { // loop on free face nodes
9520 const SMDS_MeshNode* n =
9521 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9522 gp_XYZ p( n->X(),n->Y(),n->Z() );
9523 dist += ( aBC - p ).SquareModulus();
9525 if ( dist < minDist ) {
9527 freeFaceList.erase( freeFaceList.begin(), fIt++ );
9530 fIt = freeFaceList.erase( fIt++ );
9533 } // choose one of several free faces of a volume
9535 if ( freeFaceList.size() == 1 ) {
9536 const SMDS_MeshElement* aFreeFace = freeFaceList.front();
9537 faceSet->insert( aFreeFace );
9538 // complete a node set with nodes of a found free face
9539 // for ( iNode = 0; iNode < ; iNode++ )
9540 // nodeSet->insert( fNodes[ iNode ] );
9543 } // loop on volumes of a side
9545 // // complete a set of faces if new nodes in a nodeSet appeared
9546 // // ----------------------------------------------------------
9547 // if ( nodeSetSize != nodeSet->size() ) {
9548 // for ( ; nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
9549 // SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
9550 // while ( fIt->more() ) { // loop on faces sharing a node
9551 // const SMDS_MeshElement* f = fIt->next();
9552 // if ( faceSet->find( f ) == faceSet->end() ) {
9553 // // check if all nodes are in nodeSet and
9554 // // complete setOfFaceNodeSet if they are
9555 // set <const SMDS_MeshNode*> faceNodeSet;
9556 // SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
9557 // bool allInSet = true;
9558 // while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
9559 // const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9560 // if ( nodeSet->find( n ) == nodeSet->end() )
9561 // allInSet = false;
9563 // faceNodeSet.insert( n );
9565 // if ( allInSet ) {
9566 // faceSet->insert( f );
9567 // setOfFaceNodeSet.insert( faceNodeSet );
9573 } // Create temporary faces, if there are volumes given
9576 if ( faceSet1.size() != faceSet2.size() ) {
9577 // delete temporary faces: they are in reverseElements of actual nodes
9578 // SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
9579 // while ( tmpFaceIt->more() )
9580 // aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
9581 // list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
9582 // for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
9583 // aMesh->RemoveElement(*tmpFaceIt);
9584 MESSAGE("Diff nb of faces");
9585 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9588 // ============================================================
9589 // 2. Find nodes to merge:
9590 // bind a node to remove to a node to put instead
9591 // ============================================================
9593 TNodeNodeMap nReplaceMap; // bind a node to remove to a node to put instead
9594 if ( theFirstNode1 != theFirstNode2 )
9595 nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
9596 if ( theSecondNode1 != theSecondNode2 )
9597 nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
9599 LinkID_Gen aLinkID_Gen( GetMeshDS() );
9600 set< long > linkIdSet; // links to process
9601 linkIdSet.insert( aLinkID_Gen.GetLinkID( theFirstNode1, theSecondNode1 ));
9603 typedef pair< const SMDS_MeshNode*, const SMDS_MeshNode* > NLink;
9604 list< NLink > linkList[2];
9605 linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
9606 linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
9607 // loop on links in linkList; find faces by links and append links
9608 // of the found faces to linkList
9609 list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
9610 for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ )
9612 NLink link[] = { *linkIt[0], *linkIt[1] };
9613 long linkID = aLinkID_Gen.GetLinkID( link[0].first, link[0].second );
9614 if ( !linkIdSet.count( linkID ) )
9617 // by links, find faces in the face sets,
9618 // and find indices of link nodes in the found faces;
9619 // in a face set, there is only one or no face sharing a link
9620 // ---------------------------------------------------------------
9622 const SMDS_MeshElement* face[] = { 0, 0 };
9623 vector<const SMDS_MeshNode*> fnodes[2];
9624 int iLinkNode[2][2];
9625 TIDSortedElemSet avoidSet;
9626 for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
9627 const SMDS_MeshNode* n1 = link[iSide].first;
9628 const SMDS_MeshNode* n2 = link[iSide].second;
9629 //cout << "Side " << iSide << " ";
9630 //cout << "L( " << n1->GetID() << ", " << n2->GetID() << " ) " << endl;
9631 // find a face by two link nodes
9632 face[ iSide ] = SMESH_MeshAlgos::FindFaceInSet( n1, n2,
9633 *faceSetPtr[ iSide ], avoidSet,
9634 &iLinkNode[iSide][0],
9635 &iLinkNode[iSide][1] );
9638 //cout << " F " << face[ iSide]->GetID() <<endl;
9639 faceSetPtr[ iSide ]->erase( face[ iSide ]);
9640 // put face nodes to fnodes
9641 if ( face[ iSide ]->IsQuadratic() )
9643 // use interlaced nodes iterator
9644 const SMDS_VtkFace* F = dynamic_cast<const SMDS_VtkFace*>( face[ iSide ]);
9645 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
9646 SMDS_ElemIteratorPtr nIter = F->interlacedNodesElemIterator();
9647 while ( nIter->more() )
9648 fnodes[ iSide ].push_back( cast2Node( nIter->next() ));
9652 fnodes[ iSide ].assign( face[ iSide ]->begin_nodes(),
9653 face[ iSide ]->end_nodes() );
9655 fnodes[ iSide ].push_back( fnodes[ iSide ].front());
9659 // check similarity of elements of the sides
9660 if (aResult == SEW_OK && (( face[0] && !face[1] ) || ( !face[0] && face[1] ))) {
9661 MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
9662 if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
9663 aResult = ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
9666 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9668 break; // do not return because it's necessary to remove tmp faces
9671 // set nodes to merge
9672 // -------------------
9674 if ( face[0] && face[1] ) {
9675 const int nbNodes = face[0]->NbNodes();
9676 if ( nbNodes != face[1]->NbNodes() ) {
9677 MESSAGE("Diff nb of face nodes");
9678 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9679 break; // do not return because it s necessary to remove tmp faces
9681 bool reverse[] = { false, false }; // order of nodes in the link
9682 for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
9683 // analyse link orientation in faces
9684 int i1 = iLinkNode[ iSide ][ 0 ];
9685 int i2 = iLinkNode[ iSide ][ 1 ];
9686 reverse[ iSide ] = Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1;
9688 int di1 = reverse[0] ? -1 : +1, i1 = iLinkNode[0][1] + di1;
9689 int di2 = reverse[1] ? -1 : +1, i2 = iLinkNode[1][1] + di2;
9690 for ( int i = nbNodes - 2; i > 0; --i, i1 += di1, i2 += di2 )
9692 nReplaceMap.insert ( make_pair ( fnodes[0][ ( i1 + nbNodes ) % nbNodes ],
9693 fnodes[1][ ( i2 + nbNodes ) % nbNodes ]));
9696 // add other links of the faces to linkList
9697 // -----------------------------------------
9699 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
9700 linkID = aLinkID_Gen.GetLinkID( fnodes[0][iNode], fnodes[0][iNode+1] );
9701 pair< set<long>::iterator, bool > iter_isnew = linkIdSet.insert( linkID );
9702 if ( !iter_isnew.second ) { // already in a set: no need to process
9703 linkIdSet.erase( iter_isnew.first );
9705 else // new in set == encountered for the first time: add
9707 const SMDS_MeshNode* n1 = fnodes[0][ iNode ];
9708 const SMDS_MeshNode* n2 = fnodes[0][ iNode + 1];
9709 linkList[0].push_back ( NLink( n1, n2 ));
9710 linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
9715 if ( faceSetPtr[0]->empty() || faceSetPtr[1]->empty() )
9718 } // loop on link lists
9720 if ( aResult == SEW_OK &&
9721 ( //linkIt[0] != linkList[0].end() ||
9722 !faceSetPtr[0]->empty() || !faceSetPtr[1]->empty() )) {
9723 MESSAGE( (linkIt[0] != linkList[0].end()) <<" "<< (faceSetPtr[0]->empty()) <<
9724 " " << (faceSetPtr[1]->empty()));
9725 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9728 // ====================================================================
9729 // 3. Replace nodes in elements of the side 1 and remove replaced nodes
9730 // ====================================================================
9732 // delete temporary faces
9733 // SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
9734 // while ( tmpFaceIt->more() )
9735 // aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
9736 list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
9737 for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
9738 aMesh->RemoveElement(*tmpFaceIt);
9740 if ( aResult != SEW_OK)
9743 list< int > nodeIDsToRemove/*, elemIDsToRemove*/;
9744 // loop on nodes replacement map
9745 TNodeNodeMap::iterator nReplaceMapIt = nReplaceMap.begin(), nnIt;
9746 for ( ; nReplaceMapIt != nReplaceMap.end(); nReplaceMapIt++ )
9747 if ( (*nReplaceMapIt).first != (*nReplaceMapIt).second ) {
9748 const SMDS_MeshNode* nToRemove = (*nReplaceMapIt).first;
9749 nodeIDsToRemove.push_back( nToRemove->GetID() );
9750 // loop on elements sharing nToRemove
9751 SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
9752 while ( invElemIt->more() ) {
9753 const SMDS_MeshElement* e = invElemIt->next();
9754 // get a new suite of nodes: make replacement
9755 int nbReplaced = 0, i = 0, nbNodes = e->NbNodes();
9756 vector< const SMDS_MeshNode*> nodes( nbNodes );
9757 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
9758 while ( nIt->more() ) {
9759 const SMDS_MeshNode* n =
9760 static_cast<const SMDS_MeshNode*>( nIt->next() );
9761 nnIt = nReplaceMap.find( n );
9762 if ( nnIt != nReplaceMap.end() ) {
9768 // if ( nbReplaced == nbNodes && e->GetType() == SMDSAbs_Face )
9769 // elemIDsToRemove.push_back( e->GetID() );
9773 SMDSAbs_ElementType etyp = e->GetType();
9774 SMDS_MeshElement* newElem = this->AddElement(nodes, etyp, false);
9777 myLastCreatedElems.Append(newElem);
9778 AddToSameGroups(newElem, e, aMesh);
9779 int aShapeId = e->getshapeId();
9782 aMesh->SetMeshElementOnShape( newElem, aShapeId );
9785 aMesh->RemoveElement(e);
9790 Remove( nodeIDsToRemove, true );
9795 //================================================================================
9797 * \brief Find corresponding nodes in two sets of faces
9798 * \param theSide1 - first face set
9799 * \param theSide2 - second first face
9800 * \param theFirstNode1 - a boundary node of set 1
9801 * \param theFirstNode2 - a node of set 2 corresponding to theFirstNode1
9802 * \param theSecondNode1 - a boundary node of set 1 linked with theFirstNode1
9803 * \param theSecondNode2 - a node of set 2 corresponding to theSecondNode1
9804 * \param nReplaceMap - output map of corresponding nodes
9805 * \return bool - is a success or not
9807 //================================================================================
9810 //#define DEBUG_MATCHING_NODES
9813 SMESH_MeshEditor::Sew_Error
9814 SMESH_MeshEditor::FindMatchingNodes(set<const SMDS_MeshElement*>& theSide1,
9815 set<const SMDS_MeshElement*>& theSide2,
9816 const SMDS_MeshNode* theFirstNode1,
9817 const SMDS_MeshNode* theFirstNode2,
9818 const SMDS_MeshNode* theSecondNode1,
9819 const SMDS_MeshNode* theSecondNode2,
9820 TNodeNodeMap & nReplaceMap)
9822 set<const SMDS_MeshElement*> * faceSetPtr[] = { &theSide1, &theSide2 };
9824 nReplaceMap.clear();
9825 if ( theFirstNode1 != theFirstNode2 )
9826 nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
9827 if ( theSecondNode1 != theSecondNode2 )
9828 nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
9830 set< SMESH_TLink > linkSet; // set of nodes where order of nodes is ignored
9831 linkSet.insert( SMESH_TLink( theFirstNode1, theSecondNode1 ));
9833 list< NLink > linkList[2];
9834 linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
9835 linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
9837 // loop on links in linkList; find faces by links and append links
9838 // of the found faces to linkList
9839 list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
9840 for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ ) {
9841 NLink link[] = { *linkIt[0], *linkIt[1] };
9842 if ( linkSet.find( link[0] ) == linkSet.end() )
9845 // by links, find faces in the face sets,
9846 // and find indices of link nodes in the found faces;
9847 // in a face set, there is only one or no face sharing a link
9848 // ---------------------------------------------------------------
9850 const SMDS_MeshElement* face[] = { 0, 0 };
9851 list<const SMDS_MeshNode*> notLinkNodes[2];
9852 //bool reverse[] = { false, false }; // order of notLinkNodes
9854 for ( int iSide = 0; iSide < 2; iSide++ ) // loop on 2 sides
9856 const SMDS_MeshNode* n1 = link[iSide].first;
9857 const SMDS_MeshNode* n2 = link[iSide].second;
9858 set<const SMDS_MeshElement*> * faceSet = faceSetPtr[ iSide ];
9859 set< const SMDS_MeshElement* > facesOfNode1;
9860 for ( int iNode = 0; iNode < 2; iNode++ ) // loop on 2 nodes of a link
9862 // during a loop of the first node, we find all faces around n1,
9863 // during a loop of the second node, we find one face sharing both n1 and n2
9864 const SMDS_MeshNode* n = iNode ? n1 : n2; // a node of a link
9865 SMDS_ElemIteratorPtr fIt = n->GetInverseElementIterator(SMDSAbs_Face);
9866 while ( fIt->more() ) { // loop on faces sharing a node
9867 const SMDS_MeshElement* f = fIt->next();
9868 if (faceSet->find( f ) != faceSet->end() && // f is in face set
9869 ! facesOfNode1.insert( f ).second ) // f encounters twice
9871 if ( face[ iSide ] ) {
9872 MESSAGE( "2 faces per link " );
9873 return ( iSide ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
9876 faceSet->erase( f );
9878 // get not link nodes
9879 int nbN = f->NbNodes();
9880 if ( f->IsQuadratic() )
9882 nbNodes[ iSide ] = nbN;
9883 list< const SMDS_MeshNode* > & nodes = notLinkNodes[ iSide ];
9884 int i1 = f->GetNodeIndex( n1 );
9885 int i2 = f->GetNodeIndex( n2 );
9886 int iEnd = nbN, iBeg = -1, iDelta = 1;
9887 bool reverse = ( Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1 );
9889 std::swap( iEnd, iBeg ); iDelta = -1;
9894 if ( i == iEnd ) i = iBeg + iDelta;
9895 if ( i == i1 ) break;
9896 nodes.push_back ( f->GetNode( i ) );
9902 // check similarity of elements of the sides
9903 if (( face[0] && !face[1] ) || ( !face[0] && face[1] )) {
9904 MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
9905 if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
9906 return ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
9909 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9913 // set nodes to merge
9914 // -------------------
9916 if ( face[0] && face[1] ) {
9917 if ( nbNodes[0] != nbNodes[1] ) {
9918 MESSAGE("Diff nb of face nodes");
9919 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9921 #ifdef DEBUG_MATCHING_NODES
9922 MESSAGE ( " Link 1: " << link[0].first->GetID() <<" "<< link[0].second->GetID()
9923 << " F 1: " << face[0] << "| Link 2: " << link[1].first->GetID() <<" "
9924 << link[1].second->GetID() << " F 2: " << face[1] << " | Bind: " ) ;
9926 int nbN = nbNodes[0];
9928 list<const SMDS_MeshNode*>::iterator n1 = notLinkNodes[0].begin();
9929 list<const SMDS_MeshNode*>::iterator n2 = notLinkNodes[1].begin();
9930 for ( int i = 0 ; i < nbN - 2; ++i ) {
9931 #ifdef DEBUG_MATCHING_NODES
9932 MESSAGE ( (*n1)->GetID() << " to " << (*n2)->GetID() );
9934 nReplaceMap.insert( make_pair( *(n1++), *(n2++) ));
9938 // add other links of the face 1 to linkList
9939 // -----------------------------------------
9941 const SMDS_MeshElement* f0 = face[0];
9942 const SMDS_MeshNode* n1 = f0->GetNode( nbN - 1 );
9943 for ( int i = 0; i < nbN; i++ )
9945 const SMDS_MeshNode* n2 = f0->GetNode( i );
9946 pair< set< SMESH_TLink >::iterator, bool > iter_isnew =
9947 linkSet.insert( SMESH_TLink( n1, n2 ));
9948 if ( !iter_isnew.second ) { // already in a set: no need to process
9949 linkSet.erase( iter_isnew.first );
9951 else // new in set == encountered for the first time: add
9953 #ifdef DEBUG_MATCHING_NODES
9954 MESSAGE ( "Add link 1: " << n1->GetID() << " " << n2->GetID() << " "
9955 << " | link 2: " << nReplaceMap[n1]->GetID() << " " << nReplaceMap[n2]->GetID() << " " );
9957 linkList[0].push_back ( NLink( n1, n2 ));
9958 linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
9963 } // loop on link lists
9968 //================================================================================
9970 * \brief Create elements equal (on same nodes) to given ones
9971 * \param [in] theElements - a set of elems to duplicate. If it is empty, all
9972 * elements of the uppest dimension are duplicated.
9974 //================================================================================
9976 void SMESH_MeshEditor::DoubleElements( const TIDSortedElemSet& theElements )
9979 SMESHDS_Mesh* mesh = GetMeshDS();
9981 // get an element type and an iterator over elements
9983 SMDSAbs_ElementType type;
9984 SMDS_ElemIteratorPtr elemIt;
9985 vector< const SMDS_MeshElement* > allElems;
9986 if ( theElements.empty() )
9988 if ( mesh->NbNodes() == 0 )
9990 // get most complex type
9991 SMDSAbs_ElementType types[SMDSAbs_NbElementTypes] = {
9992 SMDSAbs_Volume, SMDSAbs_Face, SMDSAbs_Edge,
9993 SMDSAbs_0DElement, SMDSAbs_Ball, SMDSAbs_Node
9995 for ( int i = 0; i < SMDSAbs_NbElementTypes; ++i )
9996 if ( mesh->GetMeshInfo().NbElements( types[i] ))
10001 // put all elements in the vector <allElems>
10002 allElems.reserve( mesh->GetMeshInfo().NbElements( type ));
10003 elemIt = mesh->elementsIterator( type );
10004 while ( elemIt->more() )
10005 allElems.push_back( elemIt->next());
10006 elemIt = elemSetIterator( allElems );
10010 type = (*theElements.begin())->GetType();
10011 elemIt = elemSetIterator( theElements );
10014 // duplicate elements
10016 if ( type == SMDSAbs_Ball )
10018 SMDS_UnstructuredGrid* vtkGrid = mesh->getGrid();
10019 while ( elemIt->more() )
10021 const SMDS_MeshElement* elem = elemIt->next();
10022 if ( elem->GetType() != SMDSAbs_Ball )
10024 if (( elem = mesh->AddBall( elem->GetNode(0),
10025 vtkGrid->GetBallDiameter( elem->getVtkId() ))))
10026 myLastCreatedElems.Append( elem );
10031 vector< const SMDS_MeshNode* > nodes;
10032 while ( elemIt->more() )
10034 const SMDS_MeshElement* elem = elemIt->next();
10035 if ( elem->GetType() != type )
10038 nodes.assign( elem->begin_nodes(), elem->end_nodes() );
10040 if ( type == SMDSAbs_Volume && elem->GetVtkType() == VTK_POLYHEDRON )
10042 std::vector<int> quantities =
10043 static_cast< const SMDS_VtkVolume* >( elem )->GetQuantities();
10044 elem = mesh->AddPolyhedralVolume( nodes, quantities );
10048 AddElement( nodes, type, elem->IsPoly() );
10049 elem = 0; // myLastCreatedElems is already filled
10052 myLastCreatedElems.Append( elem );
10057 //================================================================================
10059 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10060 \param theElems - the list of elements (edges or faces) to be replicated
10061 The nodes for duplication could be found from these elements
10062 \param theNodesNot - list of nodes to NOT replicate
10063 \param theAffectedElems - the list of elements (cells and edges) to which the
10064 replicated nodes should be associated to.
10065 \return TRUE if operation has been completed successfully, FALSE otherwise
10067 //================================================================================
10069 bool SMESH_MeshEditor::DoubleNodes( const TIDSortedElemSet& theElems,
10070 const TIDSortedElemSet& theNodesNot,
10071 const TIDSortedElemSet& theAffectedElems )
10073 myLastCreatedElems.Clear();
10074 myLastCreatedNodes.Clear();
10076 if ( theElems.size() == 0 )
10079 SMESHDS_Mesh* aMeshDS = GetMeshDS();
10084 std::map< const SMDS_MeshNode*, const SMDS_MeshNode* > anOldNodeToNewNode;
10085 // duplicate elements and nodes
10086 res = doubleNodes( aMeshDS, theElems, theNodesNot, anOldNodeToNewNode, true );
10087 // replce nodes by duplications
10088 res = doubleNodes( aMeshDS, theAffectedElems, theNodesNot, anOldNodeToNewNode, false );
10092 //================================================================================
10094 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10095 \param theMeshDS - mesh instance
10096 \param theElems - the elements replicated or modified (nodes should be changed)
10097 \param theNodesNot - nodes to NOT replicate
10098 \param theNodeNodeMap - relation of old node to new created node
10099 \param theIsDoubleElem - flag os to replicate element or modify
10100 \return TRUE if operation has been completed successfully, FALSE otherwise
10102 //================================================================================
10104 bool SMESH_MeshEditor::doubleNodes( SMESHDS_Mesh* theMeshDS,
10105 const TIDSortedElemSet& theElems,
10106 const TIDSortedElemSet& theNodesNot,
10107 std::map< const SMDS_MeshNode*,
10108 const SMDS_MeshNode* >& theNodeNodeMap,
10109 const bool theIsDoubleElem )
10111 MESSAGE("doubleNodes");
10112 // iterate on through element and duplicate them (by nodes duplication)
10114 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10115 for ( ; elemItr != theElems.end(); ++elemItr )
10117 const SMDS_MeshElement* anElem = *elemItr;
10121 bool isDuplicate = false;
10122 // duplicate nodes to duplicate element
10123 std::vector<const SMDS_MeshNode*> newNodes( anElem->NbNodes() );
10124 SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
10126 while ( anIter->more() )
10129 SMDS_MeshNode* aCurrNode = (SMDS_MeshNode*)anIter->next();
10130 SMDS_MeshNode* aNewNode = aCurrNode;
10131 if ( theNodeNodeMap.find( aCurrNode ) != theNodeNodeMap.end() )
10132 aNewNode = (SMDS_MeshNode*)theNodeNodeMap[ aCurrNode ];
10133 else if ( theIsDoubleElem && theNodesNot.find( aCurrNode ) == theNodesNot.end() )
10136 aNewNode = theMeshDS->AddNode( aCurrNode->X(), aCurrNode->Y(), aCurrNode->Z() );
10137 theNodeNodeMap[ aCurrNode ] = aNewNode;
10138 myLastCreatedNodes.Append( aNewNode );
10140 isDuplicate |= (aCurrNode != aNewNode);
10141 newNodes[ ind++ ] = aNewNode;
10143 if ( !isDuplicate )
10146 if ( theIsDoubleElem )
10147 AddElement(newNodes, anElem->GetType(), anElem->IsPoly());
10150 MESSAGE("ChangeElementNodes");
10151 theMeshDS->ChangeElementNodes( anElem, &newNodes[ 0 ], anElem->NbNodes() );
10158 //================================================================================
10160 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10161 \param theNodes - identifiers of nodes to be doubled
10162 \param theModifiedElems - identifiers of elements to be updated by the new (doubled)
10163 nodes. If list of element identifiers is empty then nodes are doubled but
10164 they not assigned to elements
10165 \return TRUE if operation has been completed successfully, FALSE otherwise
10167 //================================================================================
10169 bool SMESH_MeshEditor::DoubleNodes( const std::list< int >& theListOfNodes,
10170 const std::list< int >& theListOfModifiedElems )
10172 MESSAGE("DoubleNodes");
10173 myLastCreatedElems.Clear();
10174 myLastCreatedNodes.Clear();
10176 if ( theListOfNodes.size() == 0 )
10179 SMESHDS_Mesh* aMeshDS = GetMeshDS();
10183 // iterate through nodes and duplicate them
10185 std::map< const SMDS_MeshNode*, const SMDS_MeshNode* > anOldNodeToNewNode;
10187 std::list< int >::const_iterator aNodeIter;
10188 for ( aNodeIter = theListOfNodes.begin(); aNodeIter != theListOfNodes.end(); ++aNodeIter )
10190 int aCurr = *aNodeIter;
10191 SMDS_MeshNode* aNode = (SMDS_MeshNode*)aMeshDS->FindNode( aCurr );
10197 const SMDS_MeshNode* aNewNode = aMeshDS->AddNode( aNode->X(), aNode->Y(), aNode->Z() );
10200 anOldNodeToNewNode[ aNode ] = aNewNode;
10201 myLastCreatedNodes.Append( aNewNode );
10205 // Create map of new nodes for modified elements
10207 std::map< SMDS_MeshElement*, vector<const SMDS_MeshNode*> > anElemToNodes;
10209 std::list< int >::const_iterator anElemIter;
10210 for ( anElemIter = theListOfModifiedElems.begin();
10211 anElemIter != theListOfModifiedElems.end(); ++anElemIter )
10213 int aCurr = *anElemIter;
10214 SMDS_MeshElement* anElem = (SMDS_MeshElement*)aMeshDS->FindElement( aCurr );
10218 vector<const SMDS_MeshNode*> aNodeArr( anElem->NbNodes() );
10220 SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
10222 while ( anIter->more() )
10224 SMDS_MeshNode* aCurrNode = (SMDS_MeshNode*)anIter->next();
10225 if ( aCurr && anOldNodeToNewNode.find( aCurrNode ) != anOldNodeToNewNode.end() )
10227 const SMDS_MeshNode* aNewNode = anOldNodeToNewNode[ aCurrNode ];
10228 aNodeArr[ ind++ ] = aNewNode;
10231 aNodeArr[ ind++ ] = aCurrNode;
10233 anElemToNodes[ anElem ] = aNodeArr;
10236 // Change nodes of elements
10238 std::map< SMDS_MeshElement*, vector<const SMDS_MeshNode*> >::iterator
10239 anElemToNodesIter = anElemToNodes.begin();
10240 for ( ; anElemToNodesIter != anElemToNodes.end(); ++anElemToNodesIter )
10242 const SMDS_MeshElement* anElem = anElemToNodesIter->first;
10243 vector<const SMDS_MeshNode*> aNodeArr = anElemToNodesIter->second;
10246 MESSAGE("ChangeElementNodes");
10247 aMeshDS->ChangeElementNodes( anElem, &aNodeArr[ 0 ], anElem->NbNodes() );
10256 //================================================================================
10258 \brief Check if element located inside shape
10259 \return TRUE if IN or ON shape, FALSE otherwise
10261 //================================================================================
10263 template<class Classifier>
10264 bool isInside(const SMDS_MeshElement* theElem,
10265 Classifier& theClassifier,
10266 const double theTol)
10268 gp_XYZ centerXYZ (0, 0, 0);
10269 SMDS_ElemIteratorPtr aNodeItr = theElem->nodesIterator();
10270 while (aNodeItr->more())
10271 centerXYZ += SMESH_TNodeXYZ(cast2Node( aNodeItr->next()));
10273 gp_Pnt aPnt = centerXYZ / theElem->NbNodes();
10274 theClassifier.Perform(aPnt, theTol);
10275 TopAbs_State aState = theClassifier.State();
10276 return (aState == TopAbs_IN || aState == TopAbs_ON );
10279 //================================================================================
10281 * \brief Classifier of the 3D point on the TopoDS_Face
10282 * with interaface suitable for isInside()
10284 //================================================================================
10286 struct _FaceClassifier
10288 Extrema_ExtPS _extremum;
10289 BRepAdaptor_Surface _surface;
10290 TopAbs_State _state;
10292 _FaceClassifier(const TopoDS_Face& face):_extremum(),_surface(face),_state(TopAbs_OUT)
10294 _extremum.Initialize( _surface,
10295 _surface.FirstUParameter(), _surface.LastUParameter(),
10296 _surface.FirstVParameter(), _surface.LastVParameter(),
10297 _surface.Tolerance(), _surface.Tolerance() );
10299 void Perform(const gp_Pnt& aPnt, double theTol)
10301 _state = TopAbs_OUT;
10302 _extremum.Perform(aPnt);
10303 if ( _extremum.IsDone() )
10304 for ( int iSol = 1; iSol <= _extremum.NbExt() && _state == TopAbs_OUT; ++iSol)
10305 #if OCC_VERSION_LARGE > 0x06040000 // Porting to OCCT6.5.1
10306 _state = ( _extremum.SquareDistance(iSol) <= theTol ? TopAbs_IN : TopAbs_OUT );
10308 _state = ( _extremum.Value(iSol) <= theTol ? TopAbs_IN : TopAbs_OUT );
10311 TopAbs_State State() const
10318 //================================================================================
10320 \brief Identify the elements that will be affected by node duplication (actual duplication is not performed).
10321 This method is the first step of DoubleNodeElemGroupsInRegion.
10322 \param theElems - list of groups of elements (edges or faces) to be replicated
10323 \param theNodesNot - list of groups of nodes not to replicated
10324 \param theShape - shape to detect affected elements (element which geometric center
10325 located on or inside shape). If the shape is null, detection is done on faces orientations
10326 (select elements with a gravity center on the side given by faces normals).
10327 This mode (null shape) is faster, but works only when theElems are faces, with coherents orientations.
10328 The replicated nodes should be associated to affected elements.
10329 \return groups of affected elements
10330 \sa DoubleNodeElemGroupsInRegion()
10332 //================================================================================
10334 bool SMESH_MeshEditor::AffectedElemGroupsInRegion( const TIDSortedElemSet& theElems,
10335 const TIDSortedElemSet& theNodesNot,
10336 const TopoDS_Shape& theShape,
10337 TIDSortedElemSet& theAffectedElems)
10339 if ( theShape.IsNull() )
10341 std::set<const SMDS_MeshNode*> alreadyCheckedNodes;
10342 std::set<const SMDS_MeshElement*> alreadyCheckedElems;
10343 std::set<const SMDS_MeshElement*> edgesToCheck;
10344 alreadyCheckedNodes.clear();
10345 alreadyCheckedElems.clear();
10346 edgesToCheck.clear();
10348 // --- iterates on elements to be replicated and get elements by back references from their nodes
10350 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10352 for ( ielem=1; elemItr != theElems.end(); ++elemItr )
10354 SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
10355 if (!anElem || (anElem->GetType() != SMDSAbs_Face))
10358 SMESH_MeshAlgos::FaceNormal( anElem, normal, /*normalized=*/true );
10359 MESSAGE("element " << ielem++ << " normal " << normal.X() << " " << normal.Y() << " " << normal.Z());
10360 std::set<const SMDS_MeshNode*> nodesElem;
10362 SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
10363 while ( nodeItr->more() )
10365 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10366 nodesElem.insert(aNode);
10368 std::set<const SMDS_MeshNode*>::iterator nodit = nodesElem.begin();
10369 for (; nodit != nodesElem.end(); nodit++)
10371 MESSAGE(" noeud ");
10372 const SMDS_MeshNode* aNode = *nodit;
10373 if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
10375 if (alreadyCheckedNodes.find(aNode) != alreadyCheckedNodes.end())
10377 alreadyCheckedNodes.insert(aNode);
10378 SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
10379 while ( backElemItr->more() )
10381 MESSAGE(" backelem ");
10382 const SMDS_MeshElement* curElem = backElemItr->next();
10383 if (alreadyCheckedElems.find(curElem) != alreadyCheckedElems.end())
10385 if (theElems.find(curElem) != theElems.end())
10387 alreadyCheckedElems.insert(curElem);
10388 double x=0, y=0, z=0;
10390 SMDS_ElemIteratorPtr nodeItr2 = curElem->nodesIterator();
10391 while ( nodeItr2->more() )
10393 const SMDS_MeshNode* anotherNode = cast2Node(nodeItr2->next());
10394 x += anotherNode->X();
10395 y += anotherNode->Y();
10396 z += anotherNode->Z();
10400 p.SetCoord( x/nb -aNode->X(),
10402 z/nb -aNode->Z() );
10403 MESSAGE(" check " << p.X() << " " << p.Y() << " " << p.Z());
10406 MESSAGE(" --- inserted")
10407 theAffectedElems.insert( curElem );
10409 else if (curElem->GetType() == SMDSAbs_Edge)
10410 edgesToCheck.insert(curElem);
10414 // --- add also edges lying on the set of faces (all nodes in alreadyCheckedNodes)
10415 std::set<const SMDS_MeshElement*>::iterator eit = edgesToCheck.begin();
10416 for( ; eit != edgesToCheck.end(); eit++)
10418 bool onside = true;
10419 const SMDS_MeshElement* anEdge = *eit;
10420 SMDS_ElemIteratorPtr nodeItr = anEdge->nodesIterator();
10421 while ( nodeItr->more() )
10423 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10424 if (alreadyCheckedNodes.find(aNode) == alreadyCheckedNodes.end())
10432 MESSAGE(" --- edge onside inserted")
10433 theAffectedElems.insert(anEdge);
10439 const double aTol = Precision::Confusion();
10440 auto_ptr< BRepClass3d_SolidClassifier> bsc3d;
10441 auto_ptr<_FaceClassifier> aFaceClassifier;
10442 if ( theShape.ShapeType() == TopAbs_SOLID )
10444 bsc3d.reset( new BRepClass3d_SolidClassifier(theShape));;
10445 bsc3d->PerformInfinitePoint(aTol);
10447 else if (theShape.ShapeType() == TopAbs_FACE )
10449 aFaceClassifier.reset( new _FaceClassifier(TopoDS::Face(theShape)));
10452 // iterates on indicated elements and get elements by back references from their nodes
10453 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10455 for ( ielem = 1; elemItr != theElems.end(); ++elemItr )
10457 MESSAGE("element " << ielem++);
10458 SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
10461 SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
10462 while ( nodeItr->more() )
10464 MESSAGE(" noeud ");
10465 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10466 if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
10468 SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
10469 while ( backElemItr->more() )
10471 MESSAGE(" backelem ");
10472 const SMDS_MeshElement* curElem = backElemItr->next();
10473 if ( curElem && theElems.find(curElem) == theElems.end() &&
10475 isInside( curElem, *bsc3d, aTol ) :
10476 isInside( curElem, *aFaceClassifier, aTol )))
10477 theAffectedElems.insert( curElem );
10485 //================================================================================
10487 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
10488 \param theElems - group of of elements (edges or faces) to be replicated
10489 \param theNodesNot - group of nodes not to replicate
10490 \param theShape - shape to detect affected elements (element which geometric center
10491 located on or inside shape).
10492 The replicated nodes should be associated to affected elements.
10493 \return TRUE if operation has been completed successfully, FALSE otherwise
10495 //================================================================================
10497 bool SMESH_MeshEditor::DoubleNodesInRegion( const TIDSortedElemSet& theElems,
10498 const TIDSortedElemSet& theNodesNot,
10499 const TopoDS_Shape& theShape )
10501 if ( theShape.IsNull() )
10504 const double aTol = Precision::Confusion();
10505 auto_ptr< BRepClass3d_SolidClassifier> bsc3d;
10506 auto_ptr<_FaceClassifier> aFaceClassifier;
10507 if ( theShape.ShapeType() == TopAbs_SOLID )
10509 bsc3d.reset( new BRepClass3d_SolidClassifier(theShape));;
10510 bsc3d->PerformInfinitePoint(aTol);
10512 else if (theShape.ShapeType() == TopAbs_FACE )
10514 aFaceClassifier.reset( new _FaceClassifier(TopoDS::Face(theShape)));
10517 // iterates on indicated elements and get elements by back references from their nodes
10518 TIDSortedElemSet anAffected;
10519 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10520 for ( ; elemItr != theElems.end(); ++elemItr )
10522 SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
10526 SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
10527 while ( nodeItr->more() )
10529 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10530 if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
10532 SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
10533 while ( backElemItr->more() )
10535 const SMDS_MeshElement* curElem = backElemItr->next();
10536 if ( curElem && theElems.find(curElem) == theElems.end() &&
10538 isInside( curElem, *bsc3d, aTol ) :
10539 isInside( curElem, *aFaceClassifier, aTol )))
10540 anAffected.insert( curElem );
10544 return DoubleNodes( theElems, theNodesNot, anAffected );
10548 * \brief compute an oriented angle between two planes defined by four points.
10549 * The vector (p0,p1) defines the intersection of the 2 planes (p0,p1,g1) and (p0,p1,g2)
10550 * @param p0 base of the rotation axe
10551 * @param p1 extremity of the rotation axe
10552 * @param g1 belongs to the first plane
10553 * @param g2 belongs to the second plane
10555 double SMESH_MeshEditor::OrientedAngle(const gp_Pnt& p0, const gp_Pnt& p1, const gp_Pnt& g1, const gp_Pnt& g2)
10557 // MESSAGE(" p0: " << p0.X() << " " << p0.Y() << " " << p0.Z());
10558 // MESSAGE(" p1: " << p1.X() << " " << p1.Y() << " " << p1.Z());
10559 // MESSAGE(" g1: " << g1.X() << " " << g1.Y() << " " << g1.Z());
10560 // MESSAGE(" g2: " << g2.X() << " " << g2.Y() << " " << g2.Z());
10561 gp_Vec vref(p0, p1);
10564 gp_Vec n1 = vref.Crossed(v1);
10565 gp_Vec n2 = vref.Crossed(v2);
10567 return n2.AngleWithRef(n1, vref);
10569 catch ( Standard_Failure ) {
10571 return Max( v1.Magnitude(), v2.Magnitude() );
10575 * \brief Double nodes on shared faces between groups of volumes and create flat elements on demand.
10576 * The list of groups must contain at least two groups. The groups have to be disjoint: no common element into two different groups.
10577 * The nodes of the internal faces at the boundaries of the groups are doubled. Optionally, the internal faces are replaced by flat elements.
10578 * Triangles are transformed into prisms, and quadrangles into hexahedrons.
10579 * The flat elements are stored in groups of volumes. These groups are named according to the position of the group in the list:
10580 * 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.
10581 * 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.
10582 * All the flat elements are gathered into the group named "joints3D" (or "joints2D" in 2D situation).
10583 * The flat element of the multiple junctions between the simple junction are stored in a group named "jointsMultiples".
10584 * \param theElems - list of groups of volumes, where a group of volume is a set of
10585 * SMDS_MeshElements sorted by Id.
10586 * \param createJointElems - if TRUE, create the elements
10587 * \param onAllBoundaries - if TRUE, the nodes and elements are also created on
10588 * the boundary between \a theDomains and the rest mesh
10589 * \return TRUE if operation has been completed successfully, FALSE otherwise
10591 bool SMESH_MeshEditor::DoubleNodesOnGroupBoundaries( const std::vector<TIDSortedElemSet>& theElems,
10592 bool createJointElems,
10593 bool onAllBoundaries)
10595 MESSAGE("----------------------------------------------");
10596 MESSAGE("SMESH_MeshEditor::doubleNodesOnGroupBoundaries");
10597 MESSAGE("----------------------------------------------");
10599 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
10600 meshDS->BuildDownWardConnectivity(true);
10602 SMDS_UnstructuredGrid *grid = meshDS->getGrid();
10604 // --- build the list of faces shared by 2 domains (group of elements), with their domain and volume indexes
10605 // build the list of cells with only a node or an edge on the border, with their domain and volume indexes
10606 // build the list of nodes shared by 2 or more domains, with their domain indexes
10608 std::map<DownIdType, std::map<int,int>, DownIdCompare> faceDomains; // face --> (id domain --> id volume)
10609 std::map<int,int>celldom; // cell vtkId --> domain
10610 std::map<DownIdType, std::map<int,int>, DownIdCompare> cellDomains; // oldNode --> (id domain --> id cell)
10611 std::map<int, std::map<int,int> > nodeDomains; // oldId --> (domainId --> newId)
10612 faceDomains.clear();
10614 cellDomains.clear();
10615 nodeDomains.clear();
10616 std::map<int,int> emptyMap;
10617 std::set<int> emptySet;
10620 MESSAGE(".. Number of domains :"<<theElems.size());
10622 TIDSortedElemSet theRestDomElems;
10623 const int iRestDom = -1;
10624 const int idom0 = onAllBoundaries ? iRestDom : 0;
10625 const int nbDomains = theElems.size();
10627 // Check if the domains do not share an element
10628 for (int idom = 0; idom < nbDomains-1; idom++)
10630 // MESSAGE("... Check of domain #" << idom);
10631 const TIDSortedElemSet& domain = theElems[idom];
10632 TIDSortedElemSet::const_iterator elemItr = domain.begin();
10633 for (; elemItr != domain.end(); ++elemItr)
10635 const SMDS_MeshElement* anElem = *elemItr;
10636 int idombisdeb = idom + 1 ;
10637 for (int idombis = idombisdeb; idombis < theElems.size(); idombis++) // check if the element belongs to a domain further in the list
10639 const TIDSortedElemSet& domainbis = theElems[idombis];
10640 if ( domainbis.count(anElem) )
10642 MESSAGE(".... Domain #" << idom);
10643 MESSAGE(".... Domain #" << idombis);
10644 throw SALOME_Exception("The domains are not disjoint.");
10651 for (int idom = 0; idom < nbDomains; idom++)
10654 // --- build a map (face to duplicate --> volume to modify)
10655 // with all the faces shared by 2 domains (group of elements)
10656 // and corresponding volume of this domain, for each shared face.
10657 // a volume has a face shared by 2 domains if it has a neighbor which is not in his domain.
10659 MESSAGE("... Neighbors of domain #" << idom);
10660 const TIDSortedElemSet& domain = theElems[idom];
10661 TIDSortedElemSet::const_iterator elemItr = domain.begin();
10662 for (; elemItr != domain.end(); ++elemItr)
10664 const SMDS_MeshElement* anElem = *elemItr;
10667 int vtkId = anElem->getVtkId();
10668 //MESSAGE(" vtkId " << vtkId << " smdsId " << anElem->GetID());
10669 int neighborsVtkIds[NBMAXNEIGHBORS];
10670 int downIds[NBMAXNEIGHBORS];
10671 unsigned char downTypes[NBMAXNEIGHBORS];
10672 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
10673 for (int n = 0; n < nbNeighbors; n++)
10675 int smdsId = meshDS->fromVtkToSmds(neighborsVtkIds[n]);
10676 const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
10677 if (elem && ! domain.count(elem)) // neighbor is in another domain : face is shared
10680 for (int idombis = 0; idombis < theElems.size() && !ok; idombis++) // check if the neighbor belongs to another domain of the list
10682 // MESSAGE("Domain " << idombis);
10683 const TIDSortedElemSet& domainbis = theElems[idombis];
10684 if ( domainbis.count(elem)) ok = true ; // neighbor is in a correct domain : face is kept
10686 if ( ok || onAllBoundaries ) // the characteristics of the face is stored
10688 DownIdType face(downIds[n], downTypes[n]);
10689 if (!faceDomains[face].count(idom))
10691 faceDomains[face][idom] = vtkId; // volume associated to face in this domain
10692 celldom[vtkId] = idom;
10693 //MESSAGE(" cell with a border " << vtkId << " domain " << idom);
10697 theRestDomElems.insert( elem );
10698 faceDomains[face][iRestDom] = neighborsVtkIds[n];
10699 celldom[neighborsVtkIds[n]] = iRestDom;
10707 //MESSAGE("Number of shared faces " << faceDomains.size());
10708 std::map<DownIdType, std::map<int, int>, DownIdCompare>::iterator itface;
10710 // --- explore the shared faces domain by domain,
10711 // explore the nodes of the face and see if they belong to a cell in the domain,
10712 // which has only a node or an edge on the border (not a shared face)
10714 for (int idomain = idom0; idomain < nbDomains; idomain++)
10716 //MESSAGE("Domain " << idomain);
10717 const TIDSortedElemSet& domain = (idomain == iRestDom) ? theRestDomElems : theElems[idomain];
10718 itface = faceDomains.begin();
10719 for (; itface != faceDomains.end(); ++itface)
10721 const std::map<int, int>& domvol = itface->second;
10722 if (!domvol.count(idomain))
10724 DownIdType face = itface->first;
10725 //MESSAGE(" --- face " << face.cellId);
10726 std::set<int> oldNodes;
10728 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
10729 std::set<int>::iterator itn = oldNodes.begin();
10730 for (; itn != oldNodes.end(); ++itn)
10733 //MESSAGE(" node " << oldId);
10734 vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
10735 for (int i=0; i<l.ncells; i++)
10737 int vtkId = l.cells[i];
10738 const SMDS_MeshElement* anElem = GetMeshDS()->FindElement(GetMeshDS()->fromVtkToSmds(vtkId));
10739 if (!domain.count(anElem))
10741 int vtkType = grid->GetCellType(vtkId);
10742 int downId = grid->CellIdToDownId(vtkId);
10745 MESSAGE("doubleNodesOnGroupBoundaries: internal algorithm problem");
10746 continue; // not OK at this stage of the algorithm:
10747 //no cells created after BuildDownWardConnectivity
10749 DownIdType aCell(downId, vtkType);
10750 cellDomains[aCell][idomain] = vtkId;
10751 celldom[vtkId] = idomain;
10752 //MESSAGE(" cell " << vtkId << " domain " << idomain);
10758 // --- explore the shared faces domain by domain, to duplicate the nodes in a coherent way
10759 // for each shared face, get the nodes
10760 // for each node, for each domain of the face, create a clone of the node
10762 // --- edges at the intersection of 3 or 4 domains, with the order of domains to build
10763 // junction elements of type prism or hexa. the key is the pair of nodesId (lower first)
10764 // the value is the ordered domain ids. (more than 4 domains not taken into account)
10766 std::map<std::vector<int>, std::vector<int> > edgesMultiDomains; // nodes of edge --> ordered domains
10767 std::map<int, std::vector<int> > mutipleNodes; // nodes multi domains with domain order
10768 std::map<int, std::vector<int> > mutipleNodesToFace; // nodes multi domains with domain order to transform in Face (junction between 3 or more 2D domains)
10770 MESSAGE(".. Duplication of the nodes");
10771 for (int idomain = idom0; idomain < nbDomains; idomain++)
10773 itface = faceDomains.begin();
10774 for (; itface != faceDomains.end(); ++itface)
10776 const std::map<int, int>& domvol = itface->second;
10777 if (!domvol.count(idomain))
10779 DownIdType face = itface->first;
10780 //MESSAGE(" --- face " << face.cellId);
10781 std::set<int> oldNodes;
10783 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
10784 std::set<int>::iterator itn = oldNodes.begin();
10785 for (; itn != oldNodes.end(); ++itn)
10788 if (nodeDomains[oldId].empty())
10790 nodeDomains[oldId][idomain] = oldId; // keep the old node in the first domain
10791 //MESSAGE("-+-+-b oldNode " << oldId << " domain " << idomain);
10793 std::map<int, int>::const_iterator itdom = domvol.begin();
10794 for (; itdom != domvol.end(); ++itdom)
10796 int idom = itdom->first;
10797 //MESSAGE(" domain " << idom);
10798 if (!nodeDomains[oldId].count(idom)) // --- node to clone
10800 if (nodeDomains[oldId].size() >= 2) // a multiple node
10802 vector<int> orderedDoms;
10803 //MESSAGE("multiple node " << oldId);
10804 if (mutipleNodes.count(oldId))
10805 orderedDoms = mutipleNodes[oldId];
10808 map<int,int>::iterator it = nodeDomains[oldId].begin();
10809 for (; it != nodeDomains[oldId].end(); ++it)
10810 orderedDoms.push_back(it->first);
10812 orderedDoms.push_back(idom); // TODO order ==> push_front or back
10813 //stringstream txt;
10814 //for (int i=0; i<orderedDoms.size(); i++)
10815 // txt << orderedDoms[i] << " ";
10816 //MESSAGE("orderedDoms " << txt.str());
10817 mutipleNodes[oldId] = orderedDoms;
10819 double *coords = grid->GetPoint(oldId);
10820 SMDS_MeshNode *newNode = meshDS->AddNode(coords[0], coords[1], coords[2]);
10821 int newId = newNode->getVtkId();
10822 nodeDomains[oldId][idom] = newId; // cloned node for other domains
10823 //MESSAGE("-+-+-c oldNode " << oldId << " domain " << idomain << " newNode " << newId << " domain " << idom << " size=" <<nodeDomains[oldId].size());
10830 MESSAGE(".. Creation of elements");
10831 for (int idomain = idom0; idomain < nbDomains; idomain++)
10833 itface = faceDomains.begin();
10834 for (; itface != faceDomains.end(); ++itface)
10836 std::map<int, int> domvol = itface->second;
10837 if (!domvol.count(idomain))
10839 DownIdType face = itface->first;
10840 //MESSAGE(" --- face " << face.cellId);
10841 std::set<int> oldNodes;
10843 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
10844 int nbMultipleNodes = 0;
10845 std::set<int>::iterator itn = oldNodes.begin();
10846 for (; itn != oldNodes.end(); ++itn)
10849 if (mutipleNodes.count(oldId))
10852 if (nbMultipleNodes > 1) // check if an edge of the face is shared between 3 or more domains
10854 //MESSAGE("multiple Nodes detected on a shared face");
10855 int downId = itface->first.cellId;
10856 unsigned char cellType = itface->first.cellType;
10857 // --- shared edge or shared face ?
10858 if ((cellType == VTK_LINE) || (cellType == VTK_QUADRATIC_EDGE)) // shared edge (between two faces)
10861 int nbNodes = grid->getDownArray(cellType)->getNodes(downId, nodes);
10862 for (int i=0; i< nbNodes; i=i+nbNodes-1) // i=0 , i=nbNodes-1
10863 if (mutipleNodes.count(nodes[i]))
10864 if (!mutipleNodesToFace.count(nodes[i]))
10865 mutipleNodesToFace[nodes[i]] = mutipleNodes[nodes[i]];
10867 else // shared face (between two volumes)
10869 int nbEdges = grid->getDownArray(cellType)->getNumberOfDownCells(downId);
10870 const int* downEdgeIds = grid->getDownArray(cellType)->getDownCells(downId);
10871 const unsigned char* edgeType = grid->getDownArray(cellType)->getDownTypes(downId);
10872 for (int ie =0; ie < nbEdges; ie++)
10875 int nbNodes = grid->getDownArray(edgeType[ie])->getNodes(downEdgeIds[ie], nodes);
10876 if (mutipleNodes.count(nodes[0]) && mutipleNodes.count(nodes[nbNodes-1]))
10878 vector<int> vn0 = mutipleNodes[nodes[0]];
10879 vector<int> vn1 = mutipleNodes[nodes[nbNodes - 1]];
10881 for (int i0 = 0; i0 < vn0.size(); i0++)
10882 for (int i1 = 0; i1 < vn1.size(); i1++)
10883 if (vn0[i0] == vn1[i1])
10884 doms.push_back(vn0[i0]);
10885 if (doms.size() >2)
10887 //MESSAGE(" detect edgesMultiDomains " << nodes[0] << " " << nodes[nbNodes - 1]);
10888 double *coords = grid->GetPoint(nodes[0]);
10889 gp_Pnt p0(coords[0], coords[1], coords[2]);
10890 coords = grid->GetPoint(nodes[nbNodes - 1]);
10891 gp_Pnt p1(coords[0], coords[1], coords[2]);
10893 int vtkVolIds[1000]; // an edge can belong to a lot of volumes
10894 map<int, SMDS_VtkVolume*> domvol; // domain --> a volume with the edge
10895 map<int, double> angleDom; // oriented angles between planes defined by edge and volume centers
10896 int nbvol = grid->GetParentVolumes(vtkVolIds, downEdgeIds[ie], edgeType[ie]);
10897 for (int id=0; id < doms.size(); id++)
10899 int idom = doms[id];
10900 const TIDSortedElemSet& domain = (idom == iRestDom) ? theRestDomElems : theElems[idom];
10901 for (int ivol=0; ivol<nbvol; ivol++)
10903 int smdsId = meshDS->fromVtkToSmds(vtkVolIds[ivol]);
10904 SMDS_MeshElement* elem = (SMDS_MeshElement*)meshDS->FindElement(smdsId);
10905 if (domain.count(elem))
10907 SMDS_VtkVolume* svol = dynamic_cast<SMDS_VtkVolume*>(elem);
10908 domvol[idom] = svol;
10909 //MESSAGE(" domain " << idom << " volume " << elem->GetID());
10911 vtkIdType npts = 0;
10912 vtkIdType* pts = 0;
10913 grid->GetCellPoints(vtkVolIds[ivol], npts, pts);
10914 SMDS_VtkVolume::gravityCenter(grid, pts, npts, values);
10917 gref.SetXYZ(gp_XYZ(values[0], values[1], values[2]));
10918 angleDom[idom] = 0;
10922 gp_Pnt g(values[0], values[1], values[2]);
10923 angleDom[idom] = OrientedAngle(p0, p1, gref, g); // -pi<angle<+pi
10924 //MESSAGE(" angle=" << angleDom[idom]);
10930 map<double, int> sortedDom; // sort domains by angle
10931 for (map<int, double>::iterator ia = angleDom.begin(); ia != angleDom.end(); ++ia)
10932 sortedDom[ia->second] = ia->first;
10933 vector<int> vnodes;
10935 for (map<double, int>::iterator ib = sortedDom.begin(); ib != sortedDom.end(); ++ib)
10937 vdom.push_back(ib->second);
10938 //MESSAGE(" ordered domain " << ib->second << " angle " << ib->first);
10940 for (int ino = 0; ino < nbNodes; ino++)
10941 vnodes.push_back(nodes[ino]);
10942 edgesMultiDomains[vnodes] = vdom; // nodes vector --> ordered domains
10951 // --- iterate on shared faces (volumes to modify, face to extrude)
10952 // get node id's of the face (id SMDS = id VTK)
10953 // create flat element with old and new nodes if requested
10955 // --- new quad nodes on flat quad elements: oldId --> ((domain1 X domain2) --> newId)
10956 // (domain1 X domain2) = domain1 + MAXINT*domain2
10958 std::map<int, std::map<long,int> > nodeQuadDomains;
10959 std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
10961 MESSAGE(".. Creation of elements: simple junction");
10962 if (createJointElems)
10965 string joints2DName = "joints2D";
10966 mapOfJunctionGroups[joints2DName] = this->myMesh->AddGroup(SMDSAbs_Face, joints2DName.c_str(), idg);
10967 SMESHDS_Group *joints2DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints2DName]->GetGroupDS());
10968 string joints3DName = "joints3D";
10969 mapOfJunctionGroups[joints3DName] = this->myMesh->AddGroup(SMDSAbs_Volume, joints3DName.c_str(), idg);
10970 SMESHDS_Group *joints3DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints3DName]->GetGroupDS());
10972 itface = faceDomains.begin();
10973 for (; itface != faceDomains.end(); ++itface)
10975 DownIdType face = itface->first;
10976 std::set<int> oldNodes;
10977 std::set<int>::iterator itn;
10979 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
10981 std::map<int, int> domvol = itface->second;
10982 std::map<int, int>::iterator itdom = domvol.begin();
10983 int dom1 = itdom->first;
10984 int vtkVolId = itdom->second;
10986 int dom2 = itdom->first;
10987 SMDS_MeshCell *vol = grid->extrudeVolumeFromFace(vtkVolId, dom1, dom2, oldNodes, nodeDomains,
10989 stringstream grpname;
10992 grpname << dom1 << "_" << dom2;
10994 grpname << dom2 << "_" << dom1;
10995 string namegrp = grpname.str();
10996 if (!mapOfJunctionGroups.count(namegrp))
10997 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(vol->GetType(), namegrp.c_str(), idg);
10998 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11000 sgrp->Add(vol->GetID());
11001 if (vol->GetType() == SMDSAbs_Volume)
11002 joints3DGrp->Add(vol->GetID());
11003 else if (vol->GetType() == SMDSAbs_Face)
11004 joints2DGrp->Add(vol->GetID());
11008 // --- create volumes on multiple domain intersection if requested
11009 // iterate on mutipleNodesToFace
11010 // iterate on edgesMultiDomains
11012 MESSAGE(".. Creation of elements: multiple junction");
11013 if (createJointElems)
11015 // --- iterate on mutipleNodesToFace
11017 std::map<int, std::vector<int> >::iterator itn = mutipleNodesToFace.begin();
11018 for (; itn != mutipleNodesToFace.end(); ++itn)
11020 int node = itn->first;
11021 vector<int> orderDom = itn->second;
11022 vector<vtkIdType> orderedNodes;
11023 for (int idom = 0; idom <orderDom.size(); idom++)
11024 orderedNodes.push_back( nodeDomains[node][orderDom[idom]] );
11025 SMDS_MeshFace* face = this->GetMeshDS()->AddFaceFromVtkIds(orderedNodes);
11027 stringstream grpname;
11029 grpname << 0 << "_" << 0;
11031 string namegrp = grpname.str();
11032 if (!mapOfJunctionGroups.count(namegrp))
11033 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Face, namegrp.c_str(), idg);
11034 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11036 sgrp->Add(face->GetID());
11039 // --- iterate on edgesMultiDomains
11041 std::map<std::vector<int>, std::vector<int> >::iterator ite = edgesMultiDomains.begin();
11042 for (; ite != edgesMultiDomains.end(); ++ite)
11044 vector<int> nodes = ite->first;
11045 vector<int> orderDom = ite->second;
11046 vector<vtkIdType> orderedNodes;
11047 if (nodes.size() == 2)
11049 //MESSAGE(" use edgesMultiDomains " << nodes[0] << " " << nodes[1]);
11050 for (int ino=0; ino < nodes.size(); ino++)
11051 if (orderDom.size() == 3)
11052 for (int idom = 0; idom <orderDom.size(); idom++)
11053 orderedNodes.push_back( nodeDomains[nodes[ino]][orderDom[idom]] );
11055 for (int idom = orderDom.size()-1; idom >=0; idom--)
11056 orderedNodes.push_back( nodeDomains[nodes[ino]][orderDom[idom]] );
11057 SMDS_MeshVolume* vol = this->GetMeshDS()->AddVolumeFromVtkIds(orderedNodes);
11060 string namegrp = "jointsMultiples";
11061 if (!mapOfJunctionGroups.count(namegrp))
11062 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
11063 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11065 sgrp->Add(vol->GetID());
11069 INFOS("Quadratic multiple joints not implemented");
11070 // TODO quadratic nodes
11075 // --- list the explicit faces and edges of the mesh that need to be modified,
11076 // i.e. faces and edges built with one or more duplicated nodes.
11077 // associate these faces or edges to their corresponding domain.
11078 // only the first domain found is kept when a face or edge is shared
11080 std::map<DownIdType, std::map<int,int>, DownIdCompare> faceOrEdgeDom; // cellToModify --> (id domain --> id cell)
11081 std::map<int,int> feDom; // vtk id of cell to modify --> id domain
11082 faceOrEdgeDom.clear();
11085 MESSAGE(".. Modification of elements");
11086 for (int idomain = idom0; idomain < nbDomains; idomain++)
11088 std::map<int, std::map<int, int> >::const_iterator itnod = nodeDomains.begin();
11089 for (; itnod != nodeDomains.end(); ++itnod)
11091 int oldId = itnod->first;
11092 //MESSAGE(" node " << oldId);
11093 vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
11094 for (int i = 0; i < l.ncells; i++)
11096 int vtkId = l.cells[i];
11097 int vtkType = grid->GetCellType(vtkId);
11098 int downId = grid->CellIdToDownId(vtkId);
11100 continue; // new cells: not to be modified
11101 DownIdType aCell(downId, vtkType);
11102 int volParents[1000];
11103 int nbvol = grid->GetParentVolumes(volParents, vtkId);
11104 for (int j = 0; j < nbvol; j++)
11105 if (celldom.count(volParents[j]) && (celldom[volParents[j]] == idomain))
11106 if (!feDom.count(vtkId))
11108 feDom[vtkId] = idomain;
11109 faceOrEdgeDom[aCell] = emptyMap;
11110 faceOrEdgeDom[aCell][idomain] = vtkId; // affect face or edge to the first domain only
11111 //MESSAGE("affect cell " << this->GetMeshDS()->fromVtkToSmds(vtkId) << " domain " << idomain
11112 // << " type " << vtkType << " downId " << downId);
11118 // --- iterate on shared faces (volumes to modify, face to extrude)
11119 // get node id's of the face
11120 // replace old nodes by new nodes in volumes, and update inverse connectivity
11122 std::map<DownIdType, std::map<int,int>, DownIdCompare>* maps[3] = {&faceDomains, &cellDomains, &faceOrEdgeDom};
11123 for (int m=0; m<3; m++)
11125 std::map<DownIdType, std::map<int,int>, DownIdCompare>* amap = maps[m];
11126 itface = (*amap).begin();
11127 for (; itface != (*amap).end(); ++itface)
11129 DownIdType face = itface->first;
11130 std::set<int> oldNodes;
11131 std::set<int>::iterator itn;
11133 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
11134 //MESSAGE("examine cell, downId " << face.cellId << " type " << int(face.cellType));
11135 std::map<int, int> localClonedNodeIds;
11137 std::map<int, int> domvol = itface->second;
11138 std::map<int, int>::iterator itdom = domvol.begin();
11139 for (; itdom != domvol.end(); ++itdom)
11141 int idom = itdom->first;
11142 int vtkVolId = itdom->second;
11143 //MESSAGE("modify nodes of cell " << this->GetMeshDS()->fromVtkToSmds(vtkVolId) << " domain " << idom);
11144 localClonedNodeIds.clear();
11145 for (itn = oldNodes.begin(); itn != oldNodes.end(); ++itn)
11148 if (nodeDomains[oldId].count(idom))
11150 localClonedNodeIds[oldId] = nodeDomains[oldId][idom];
11151 //MESSAGE(" node " << oldId << " --> " << localClonedNodeIds[oldId]);
11154 meshDS->ModifyCellNodes(vtkVolId, localClonedNodeIds);
11159 meshDS->CleanDownWardConnectivity(); // Mesh has been modified, downward connectivity is no more usable, free memory
11160 grid->BuildLinks();
11168 * \brief Double nodes on some external faces and create flat elements.
11169 * Flat elements are mainly used by some types of mechanic calculations.
11171 * Each group of the list must be constituted of faces.
11172 * Triangles are transformed in prisms, and quadrangles in hexahedrons.
11173 * @param theElems - list of groups of faces, where a group of faces is a set of
11174 * SMDS_MeshElements sorted by Id.
11175 * @return TRUE if operation has been completed successfully, FALSE otherwise
11177 bool SMESH_MeshEditor::CreateFlatElementsOnFacesGroups(const std::vector<TIDSortedElemSet>& theElems)
11179 MESSAGE("-------------------------------------------------");
11180 MESSAGE("SMESH_MeshEditor::CreateFlatElementsOnFacesGroups");
11181 MESSAGE("-------------------------------------------------");
11183 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11185 // --- For each group of faces
11186 // duplicate the nodes, create a flat element based on the face
11187 // replace the nodes of the faces by their clones
11189 std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> clonedNodes;
11190 std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> intermediateNodes;
11191 clonedNodes.clear();
11192 intermediateNodes.clear();
11193 std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
11194 mapOfJunctionGroups.clear();
11196 for (int idom = 0; idom < theElems.size(); idom++)
11198 const TIDSortedElemSet& domain = theElems[idom];
11199 TIDSortedElemSet::const_iterator elemItr = domain.begin();
11200 for (; elemItr != domain.end(); ++elemItr)
11202 SMDS_MeshElement* anElem = (SMDS_MeshElement*) *elemItr;
11203 SMDS_MeshFace* aFace = dynamic_cast<SMDS_MeshFace*> (anElem);
11206 // MESSAGE("aFace=" << aFace->GetID());
11207 bool isQuad = aFace->IsQuadratic();
11208 vector<const SMDS_MeshNode*> ln0, ln1, ln2, ln3, ln4;
11210 // --- clone the nodes, create intermediate nodes for non medium nodes of a quad face
11212 SMDS_ElemIteratorPtr nodeIt = aFace->nodesIterator();
11213 while (nodeIt->more())
11215 const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*> (nodeIt->next());
11216 bool isMedium = isQuad && (aFace->IsMediumNode(node));
11218 ln2.push_back(node);
11220 ln0.push_back(node);
11222 const SMDS_MeshNode* clone = 0;
11223 if (!clonedNodes.count(node))
11225 clone = meshDS->AddNode(node->X(), node->Y(), node->Z());
11226 clonedNodes[node] = clone;
11229 clone = clonedNodes[node];
11232 ln3.push_back(clone);
11234 ln1.push_back(clone);
11236 const SMDS_MeshNode* inter = 0;
11237 if (isQuad && (!isMedium))
11239 if (!intermediateNodes.count(node))
11241 inter = meshDS->AddNode(node->X(), node->Y(), node->Z());
11242 intermediateNodes[node] = inter;
11245 inter = intermediateNodes[node];
11246 ln4.push_back(inter);
11250 // --- extrude the face
11252 vector<const SMDS_MeshNode*> ln;
11253 SMDS_MeshVolume* vol = 0;
11254 vtkIdType aType = aFace->GetVtkType();
11258 vol = meshDS->AddVolume(ln0[2], ln0[1], ln0[0], ln1[2], ln1[1], ln1[0]);
11259 // MESSAGE("vol prism " << vol->GetID());
11260 ln.push_back(ln1[0]);
11261 ln.push_back(ln1[1]);
11262 ln.push_back(ln1[2]);
11265 vol = meshDS->AddVolume(ln0[3], ln0[2], ln0[1], ln0[0], ln1[3], ln1[2], ln1[1], ln1[0]);
11266 // MESSAGE("vol hexa " << vol->GetID());
11267 ln.push_back(ln1[0]);
11268 ln.push_back(ln1[1]);
11269 ln.push_back(ln1[2]);
11270 ln.push_back(ln1[3]);
11272 case VTK_QUADRATIC_TRIANGLE:
11273 vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln0[0], ln0[1], ln0[2], ln3[0], ln3[1], ln3[2],
11274 ln2[0], ln2[1], ln2[2], ln4[0], ln4[1], ln4[2]);
11275 // MESSAGE("vol quad prism " << vol->GetID());
11276 ln.push_back(ln1[0]);
11277 ln.push_back(ln1[1]);
11278 ln.push_back(ln1[2]);
11279 ln.push_back(ln3[0]);
11280 ln.push_back(ln3[1]);
11281 ln.push_back(ln3[2]);
11283 case VTK_QUADRATIC_QUAD:
11284 // vol = meshDS->AddVolume(ln0[0], ln0[1], ln0[2], ln0[3], ln1[0], ln1[1], ln1[2], ln1[3],
11285 // ln2[0], ln2[1], ln2[2], ln2[3], ln3[0], ln3[1], ln3[2], ln3[3],
11286 // ln4[0], ln4[1], ln4[2], ln4[3]);
11287 vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln1[3], ln0[0], ln0[1], ln0[2], ln0[3],
11288 ln3[0], ln3[1], ln3[2], ln3[3], ln2[0], ln2[1], ln2[2], ln2[3],
11289 ln4[0], ln4[1], ln4[2], ln4[3]);
11290 // MESSAGE("vol quad hexa " << vol->GetID());
11291 ln.push_back(ln1[0]);
11292 ln.push_back(ln1[1]);
11293 ln.push_back(ln1[2]);
11294 ln.push_back(ln1[3]);
11295 ln.push_back(ln3[0]);
11296 ln.push_back(ln3[1]);
11297 ln.push_back(ln3[2]);
11298 ln.push_back(ln3[3]);
11308 stringstream grpname;
11312 string namegrp = grpname.str();
11313 if (!mapOfJunctionGroups.count(namegrp))
11314 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
11315 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
11317 sgrp->Add(vol->GetID());
11320 // --- modify the face
11322 aFace->ChangeNodes(&ln[0], ln.size());
11329 * \brief identify all the elements around a geom shape, get the faces delimiting the hole
11330 * Build groups of volume to remove, groups of faces to replace on the skin of the object,
11331 * groups of faces to remove inside the object, (idem edges).
11332 * Build ordered list of nodes at the border of each group of faces to replace (to be used to build a geom subshape)
11334 void SMESH_MeshEditor::CreateHoleSkin(double radius,
11335 const TopoDS_Shape& theShape,
11336 SMESH_NodeSearcher* theNodeSearcher,
11337 const char* groupName,
11338 std::vector<double>& nodesCoords,
11339 std::vector<std::vector<int> >& listOfListOfNodes)
11341 MESSAGE("--------------------------------");
11342 MESSAGE("SMESH_MeshEditor::CreateHoleSkin");
11343 MESSAGE("--------------------------------");
11345 // --- zone of volumes to remove is given :
11346 // 1 either by a geom shape (one or more vertices) and a radius,
11347 // 2 either by a group of nodes (representative of the shape)to use with the radius,
11348 // 3 either by a group of nodes where all the elements build on one of this nodes are to remove,
11349 // In the case 2, the group of nodes is an external group of nodes from another mesh,
11350 // In the case 3, the group of nodes is an internal group of the mesh (obtained for instance by a filter),
11351 // defined by it's name.
11353 SMESHDS_GroupBase* groupDS = 0;
11354 SMESH_Mesh::GroupIteratorPtr groupIt = this->myMesh->GetGroups();
11355 while ( groupIt->more() )
11358 SMESH_Group * group = groupIt->next();
11359 if ( !group ) continue;
11360 groupDS = group->GetGroupDS();
11361 if ( !groupDS || groupDS->IsEmpty() ) continue;
11362 std::string grpName = group->GetName();
11363 //MESSAGE("grpName=" << grpName);
11364 if (grpName == groupName)
11370 bool isNodeGroup = false;
11371 bool isNodeCoords = false;
11374 if (groupDS->GetType() != SMDSAbs_Node)
11376 isNodeGroup = true; // a group of nodes exists and it is in this mesh
11379 if (nodesCoords.size() > 0)
11380 isNodeCoords = true; // a list o nodes given by their coordinates
11381 //MESSAGE("---" << isNodeGroup << " " << isNodeCoords);
11383 // --- define groups to build
11385 int idg; // --- group of SMDS volumes
11386 string grpvName = groupName;
11387 grpvName += "_vol";
11388 SMESH_Group *grp = this->myMesh->AddGroup(SMDSAbs_Volume, grpvName.c_str(), idg);
11391 MESSAGE("group not created " << grpvName);
11394 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(grp->GetGroupDS());
11396 int idgs; // --- group of SMDS faces on the skin
11397 string grpsName = groupName;
11398 grpsName += "_skin";
11399 SMESH_Group *grps = this->myMesh->AddGroup(SMDSAbs_Face, grpsName.c_str(), idgs);
11402 MESSAGE("group not created " << grpsName);
11405 SMESHDS_Group *sgrps = dynamic_cast<SMESHDS_Group*>(grps->GetGroupDS());
11407 int idgi; // --- group of SMDS faces internal (several shapes)
11408 string grpiName = groupName;
11409 grpiName += "_internalFaces";
11410 SMESH_Group *grpi = this->myMesh->AddGroup(SMDSAbs_Face, grpiName.c_str(), idgi);
11413 MESSAGE("group not created " << grpiName);
11416 SMESHDS_Group *sgrpi = dynamic_cast<SMESHDS_Group*>(grpi->GetGroupDS());
11418 int idgei; // --- group of SMDS faces internal (several shapes)
11419 string grpeiName = groupName;
11420 grpeiName += "_internalEdges";
11421 SMESH_Group *grpei = this->myMesh->AddGroup(SMDSAbs_Edge, grpeiName.c_str(), idgei);
11424 MESSAGE("group not created " << grpeiName);
11427 SMESHDS_Group *sgrpei = dynamic_cast<SMESHDS_Group*>(grpei->GetGroupDS());
11429 // --- build downward connectivity
11431 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
11432 meshDS->BuildDownWardConnectivity(true);
11433 SMDS_UnstructuredGrid* grid = meshDS->getGrid();
11435 // --- set of volumes detected inside
11437 std::set<int> setOfInsideVol;
11438 std::set<int> setOfVolToCheck;
11440 std::vector<gp_Pnt> gpnts;
11443 if (isNodeGroup) // --- a group of nodes is provided : find all the volumes using one or more of this nodes
11445 MESSAGE("group of nodes provided");
11446 SMDS_ElemIteratorPtr elemIt = groupDS->GetElements();
11447 while ( elemIt->more() )
11449 const SMDS_MeshElement* elem = elemIt->next();
11452 const SMDS_MeshNode* node = dynamic_cast<const SMDS_MeshNode*>(elem);
11455 SMDS_MeshElement* vol = 0;
11456 SMDS_ElemIteratorPtr volItr = node->GetInverseElementIterator(SMDSAbs_Volume);
11457 while (volItr->more())
11459 vol = (SMDS_MeshElement*)volItr->next();
11460 setOfInsideVol.insert(vol->getVtkId());
11461 sgrp->Add(vol->GetID());
11465 else if (isNodeCoords)
11467 MESSAGE("list of nodes coordinates provided");
11470 while (i < nodesCoords.size()-2)
11472 double x = nodesCoords[i++];
11473 double y = nodesCoords[i++];
11474 double z = nodesCoords[i++];
11475 gp_Pnt p = gp_Pnt(x, y ,z);
11476 gpnts.push_back(p);
11477 MESSAGE("TopoDS_Vertex " << k << " " << p.X() << " " << p.Y() << " " << p.Z());
11481 else // --- no group, no coordinates : use the vertices of the geom shape provided, and radius
11483 MESSAGE("no group of nodes provided, using vertices from geom shape, and radius");
11484 TopTools_IndexedMapOfShape vertexMap;
11485 TopExp::MapShapes( theShape, TopAbs_VERTEX, vertexMap );
11486 gp_Pnt p = gp_Pnt(0,0,0);
11487 if (vertexMap.Extent() < 1)
11490 for ( int i = 1; i <= vertexMap.Extent(); ++i )
11492 const TopoDS_Vertex& vertex = TopoDS::Vertex( vertexMap( i ));
11493 p = BRep_Tool::Pnt(vertex);
11494 gpnts.push_back(p);
11495 MESSAGE("TopoDS_Vertex " << i << " " << p.X() << " " << p.Y() << " " << p.Z());
11499 if (gpnts.size() > 0)
11502 const SMDS_MeshNode* startNode = theNodeSearcher->FindClosestTo(gpnts[0]);
11504 nodeId = startNode->GetID();
11505 MESSAGE("nodeId " << nodeId);
11507 double radius2 = radius*radius;
11508 MESSAGE("radius2 " << radius2);
11510 // --- volumes on start node
11512 setOfVolToCheck.clear();
11513 SMDS_MeshElement* startVol = 0;
11514 SMDS_ElemIteratorPtr volItr = startNode->GetInverseElementIterator(SMDSAbs_Volume);
11515 while (volItr->more())
11517 startVol = (SMDS_MeshElement*)volItr->next();
11518 setOfVolToCheck.insert(startVol->getVtkId());
11520 if (setOfVolToCheck.empty())
11522 MESSAGE("No volumes found");
11526 // --- starting with central volumes then their neighbors, check if they are inside
11527 // or outside the domain, until no more new neighbor volume is inside.
11528 // Fill the group of inside volumes
11530 std::map<int, double> mapOfNodeDistance2;
11531 mapOfNodeDistance2.clear();
11532 std::set<int> setOfOutsideVol;
11533 while (!setOfVolToCheck.empty())
11535 std::set<int>::iterator it = setOfVolToCheck.begin();
11537 MESSAGE("volume to check, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
11538 bool volInside = false;
11539 vtkIdType npts = 0;
11540 vtkIdType* pts = 0;
11541 grid->GetCellPoints(vtkId, npts, pts);
11542 for (int i=0; i<npts; i++)
11544 double distance2 = 0;
11545 if (mapOfNodeDistance2.count(pts[i]))
11547 distance2 = mapOfNodeDistance2[pts[i]];
11548 MESSAGE("point " << pts[i] << " distance2 " << distance2);
11552 double *coords = grid->GetPoint(pts[i]);
11553 gp_Pnt aPoint = gp_Pnt(coords[0], coords[1], coords[2]);
11555 for (int j=0; j<gpnts.size(); j++)
11557 double d2 = aPoint.SquareDistance(gpnts[j]);
11558 if (d2 < distance2)
11561 if (distance2 < radius2)
11565 mapOfNodeDistance2[pts[i]] = distance2;
11566 MESSAGE(" point " << pts[i] << " distance2 " << distance2 << " coords " << coords[0] << " " << coords[1] << " " << coords[2]);
11568 if (distance2 < radius2)
11570 volInside = true; // one or more nodes inside the domain
11571 sgrp->Add(meshDS->fromVtkToSmds(vtkId));
11577 setOfInsideVol.insert(vtkId);
11578 MESSAGE(" volume inside, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
11579 int neighborsVtkIds[NBMAXNEIGHBORS];
11580 int downIds[NBMAXNEIGHBORS];
11581 unsigned char downTypes[NBMAXNEIGHBORS];
11582 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
11583 for (int n = 0; n < nbNeighbors; n++)
11584 if (!setOfInsideVol.count(neighborsVtkIds[n]) ||setOfOutsideVol.count(neighborsVtkIds[n]))
11585 setOfVolToCheck.insert(neighborsVtkIds[n]);
11589 setOfOutsideVol.insert(vtkId);
11590 MESSAGE(" volume outside, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
11592 setOfVolToCheck.erase(vtkId);
11596 // --- for outside hexahedrons, check if they have more than one neighbor volume inside
11597 // If yes, add the volume to the inside set
11599 bool addedInside = true;
11600 std::set<int> setOfVolToReCheck;
11601 while (addedInside)
11603 MESSAGE(" --------------------------- re check");
11604 addedInside = false;
11605 std::set<int>::iterator itv = setOfInsideVol.begin();
11606 for (; itv != setOfInsideVol.end(); ++itv)
11609 int neighborsVtkIds[NBMAXNEIGHBORS];
11610 int downIds[NBMAXNEIGHBORS];
11611 unsigned char downTypes[NBMAXNEIGHBORS];
11612 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
11613 for (int n = 0; n < nbNeighbors; n++)
11614 if (!setOfInsideVol.count(neighborsVtkIds[n]))
11615 setOfVolToReCheck.insert(neighborsVtkIds[n]);
11617 setOfVolToCheck = setOfVolToReCheck;
11618 setOfVolToReCheck.clear();
11619 while (!setOfVolToCheck.empty())
11621 std::set<int>::iterator it = setOfVolToCheck.begin();
11623 if (grid->GetCellType(vtkId) == VTK_HEXAHEDRON)
11625 MESSAGE("volume to recheck, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
11626 int countInside = 0;
11627 int neighborsVtkIds[NBMAXNEIGHBORS];
11628 int downIds[NBMAXNEIGHBORS];
11629 unsigned char downTypes[NBMAXNEIGHBORS];
11630 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
11631 for (int n = 0; n < nbNeighbors; n++)
11632 if (setOfInsideVol.count(neighborsVtkIds[n]))
11634 MESSAGE("countInside " << countInside);
11635 if (countInside > 1)
11637 MESSAGE(" volume inside, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
11638 setOfInsideVol.insert(vtkId);
11639 sgrp->Add(meshDS->fromVtkToSmds(vtkId));
11640 addedInside = true;
11643 setOfVolToReCheck.insert(vtkId);
11645 setOfVolToCheck.erase(vtkId);
11649 // --- map of Downward faces at the boundary, inside the global volume
11650 // map of Downward faces on the skin of the global volume (equivalent to SMDS faces on the skin)
11651 // fill group of SMDS faces inside the volume (when several volume shapes)
11652 // fill group of SMDS faces on the skin of the global volume (if skin)
11654 std::map<DownIdType, int, DownIdCompare> boundaryFaces; // boundary faces inside the volume --> corresponding cell
11655 std::map<DownIdType, int, DownIdCompare> skinFaces; // faces on the skin of the global volume --> corresponding cell
11656 std::set<int>::iterator it = setOfInsideVol.begin();
11657 for (; it != setOfInsideVol.end(); ++it)
11660 //MESSAGE(" vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
11661 int neighborsVtkIds[NBMAXNEIGHBORS];
11662 int downIds[NBMAXNEIGHBORS];
11663 unsigned char downTypes[NBMAXNEIGHBORS];
11664 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId, true);
11665 for (int n = 0; n < nbNeighbors; n++)
11667 int neighborDim = SMDS_Downward::getCellDimension(grid->GetCellType(neighborsVtkIds[n]));
11668 if (neighborDim == 3)
11670 if (! setOfInsideVol.count(neighborsVtkIds[n])) // neighbor volume is not inside : face is boundary
11672 DownIdType face(downIds[n], downTypes[n]);
11673 boundaryFaces[face] = vtkId;
11675 // if the face between to volumes is in the mesh, get it (internal face between shapes)
11676 int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
11677 if (vtkFaceId >= 0)
11679 sgrpi->Add(meshDS->fromVtkToSmds(vtkFaceId));
11680 // find also the smds edges on this face
11681 int nbEdges = grid->getDownArray(downTypes[n])->getNumberOfDownCells(downIds[n]);
11682 const int* dEdges = grid->getDownArray(downTypes[n])->getDownCells(downIds[n]);
11683 const unsigned char* dTypes = grid->getDownArray(downTypes[n])->getDownTypes(downIds[n]);
11684 for (int i = 0; i < nbEdges; i++)
11686 int vtkEdgeId = grid->getDownArray(dTypes[i])->getVtkCellId(dEdges[i]);
11687 if (vtkEdgeId >= 0)
11688 sgrpei->Add(meshDS->fromVtkToSmds(vtkEdgeId));
11692 else if (neighborDim == 2) // skin of the volume
11694 DownIdType face(downIds[n], downTypes[n]);
11695 skinFaces[face] = vtkId;
11696 int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
11697 if (vtkFaceId >= 0)
11698 sgrps->Add(meshDS->fromVtkToSmds(vtkFaceId));
11703 // --- identify the edges constituting the wire of each subshape on the skin
11704 // define polylines with the nodes of edges, equivalent to wires
11705 // project polylines on subshapes, and partition, to get geom faces
11707 std::map<int, std::set<int> > shapeIdToVtkIdSet; // shapeId --> set of vtkId on skin
11708 std::set<int> emptySet;
11710 std::set<int> shapeIds;
11712 SMDS_ElemIteratorPtr itelem = sgrps->GetElements();
11713 while (itelem->more())
11715 const SMDS_MeshElement *elem = itelem->next();
11716 int shapeId = elem->getshapeId();
11717 int vtkId = elem->getVtkId();
11718 if (!shapeIdToVtkIdSet.count(shapeId))
11720 shapeIdToVtkIdSet[shapeId] = emptySet;
11721 shapeIds.insert(shapeId);
11723 shapeIdToVtkIdSet[shapeId].insert(vtkId);
11726 std::map<int, std::set<DownIdType, DownIdCompare> > shapeIdToEdges; // shapeId --> set of downward edges
11727 std::set<DownIdType, DownIdCompare> emptyEdges;
11728 emptyEdges.clear();
11730 std::map<int, std::set<int> >::iterator itShape = shapeIdToVtkIdSet.begin();
11731 for (; itShape != shapeIdToVtkIdSet.end(); ++itShape)
11733 int shapeId = itShape->first;
11734 MESSAGE(" --- Shape ID --- "<< shapeId);
11735 shapeIdToEdges[shapeId] = emptyEdges;
11737 std::vector<int> nodesEdges;
11739 std::set<int>::iterator its = itShape->second.begin();
11740 for (; its != itShape->second.end(); ++its)
11743 MESSAGE(" " << vtkId);
11744 int neighborsVtkIds[NBMAXNEIGHBORS];
11745 int downIds[NBMAXNEIGHBORS];
11746 unsigned char downTypes[NBMAXNEIGHBORS];
11747 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
11748 for (int n = 0; n < nbNeighbors; n++)
11750 if (neighborsVtkIds[n]<0) // only smds faces are considered as neighbors here
11752 int smdsId = meshDS->fromVtkToSmds(neighborsVtkIds[n]);
11753 const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
11754 if ( shapeIds.count(elem->getshapeId()) && !sgrps->Contains(elem)) // edge : neighbor in the set of shape, not in the group
11756 DownIdType edge(downIds[n], downTypes[n]);
11757 if (!shapeIdToEdges[shapeId].count(edge))
11759 shapeIdToEdges[shapeId].insert(edge);
11761 int nbNodes = grid->getDownArray(downTypes[n])->getNodes(downIds[n],vtkNodeId);
11762 nodesEdges.push_back(vtkNodeId[0]);
11763 nodesEdges.push_back(vtkNodeId[nbNodes-1]);
11764 MESSAGE(" --- nodes " << vtkNodeId[0]+1 << " " << vtkNodeId[nbNodes-1]+1);
11770 std::list<int> order;
11772 if (nodesEdges.size() > 0)
11774 order.push_back(nodesEdges[0]); MESSAGE(" --- back " << order.back()+1); // SMDS id = VTK id + 1;
11775 nodesEdges[0] = -1;
11776 order.push_back(nodesEdges[1]); MESSAGE(" --- back " << order.back()+1);
11777 nodesEdges[1] = -1; // do not reuse this edge
11781 int nodeTofind = order.back(); // try first to push back
11783 for (i = 0; i<nodesEdges.size(); i++)
11784 if (nodesEdges[i] == nodeTofind)
11786 if (i == nodesEdges.size())
11787 found = false; // no follower found on back
11790 if (i%2) // odd ==> use the previous one
11791 if (nodesEdges[i-1] < 0)
11795 order.push_back(nodesEdges[i-1]); MESSAGE(" --- back " << order.back()+1);
11796 nodesEdges[i-1] = -1;
11798 else // even ==> use the next one
11799 if (nodesEdges[i+1] < 0)
11803 order.push_back(nodesEdges[i+1]); MESSAGE(" --- back " << order.back()+1);
11804 nodesEdges[i+1] = -1;
11809 // try to push front
11811 nodeTofind = order.front(); // try to push front
11812 for (i = 0; i<nodesEdges.size(); i++)
11813 if (nodesEdges[i] == nodeTofind)
11815 if (i == nodesEdges.size())
11817 found = false; // no predecessor found on front
11820 if (i%2) // odd ==> use the previous one
11821 if (nodesEdges[i-1] < 0)
11825 order.push_front(nodesEdges[i-1]); MESSAGE(" --- front " << order.front()+1);
11826 nodesEdges[i-1] = -1;
11828 else // even ==> use the next one
11829 if (nodesEdges[i+1] < 0)
11833 order.push_front(nodesEdges[i+1]); MESSAGE(" --- front " << order.front()+1);
11834 nodesEdges[i+1] = -1;
11840 std::vector<int> nodes;
11841 nodes.push_back(shapeId);
11842 std::list<int>::iterator itl = order.begin();
11843 for (; itl != order.end(); itl++)
11845 nodes.push_back((*itl) + 1); // SMDS id = VTK id + 1;
11846 MESSAGE(" ordered node " << nodes[nodes.size()-1]);
11848 listOfListOfNodes.push_back(nodes);
11851 // partition geom faces with blocFissure
11852 // mesh blocFissure and geom faces of the skin (external wires given, triangle algo to choose)
11853 // mesh volume around blocFissure (skin triangles and quadrangle given, tetra algo to choose)
11859 //================================================================================
11861 * \brief Generates skin mesh (containing 2D cells) from 3D mesh
11862 * The created 2D mesh elements based on nodes of free faces of boundary volumes
11863 * \return TRUE if operation has been completed successfully, FALSE otherwise
11865 //================================================================================
11867 bool SMESH_MeshEditor::Make2DMeshFrom3D()
11869 // iterates on volume elements and detect all free faces on them
11870 SMESHDS_Mesh* aMesh = GetMeshDS();
11873 //bool res = false;
11874 int nbFree = 0, nbExisted = 0, nbCreated = 0;
11875 SMDS_VolumeIteratorPtr vIt = aMesh->volumesIterator();
11878 const SMDS_MeshVolume* volume = vIt->next();
11879 SMDS_VolumeTool vTool( volume, /*ignoreCentralNodes=*/false );
11880 vTool.SetExternalNormal();
11881 //const bool isPoly = volume->IsPoly();
11882 const int iQuad = volume->IsQuadratic();
11883 for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
11885 if (!vTool.IsFreeFace(iface))
11888 vector<const SMDS_MeshNode *> nodes;
11889 int nbFaceNodes = vTool.NbFaceNodes(iface);
11890 const SMDS_MeshNode** faceNodes = vTool.GetFaceNodes(iface);
11892 for ( ; inode < nbFaceNodes; inode += iQuad+1)
11893 nodes.push_back(faceNodes[inode]);
11894 if (iQuad) { // add medium nodes
11895 for ( inode = 1; inode < nbFaceNodes; inode += 2)
11896 nodes.push_back(faceNodes[inode]);
11897 if ( nbFaceNodes == 9 ) // bi-quadratic quad
11898 nodes.push_back(faceNodes[8]);
11900 // add new face based on volume nodes
11901 if (aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false) ) {
11903 continue; // face already exsist
11905 AddElement(nodes, SMDSAbs_Face, ( !iQuad && nbFaceNodes/(iQuad+1) > 4 ));
11909 return ( nbFree==(nbExisted+nbCreated) );
11914 inline const SMDS_MeshNode* getNodeWithSameID(SMESHDS_Mesh* mesh, const SMDS_MeshNode* node)
11916 if ( const SMDS_MeshNode* n = mesh->FindNode( node->GetID() ))
11918 return mesh->AddNodeWithID( node->X(),node->Y(),node->Z(), node->GetID() );
11921 //================================================================================
11923 * \brief Creates missing boundary elements
11924 * \param elements - elements whose boundary is to be checked
11925 * \param dimension - defines type of boundary elements to create
11926 * \param group - a group to store created boundary elements in
11927 * \param targetMesh - a mesh to store created boundary elements in
11928 * \param toCopyElements - if true, the checked elements will be copied into the targetMesh
11929 * \param toCopyExistingBoundary - if true, not only new but also pre-existing
11930 * boundary elements will be copied into the targetMesh
11931 * \param toAddExistingBondary - if true, not only new but also pre-existing
11932 * boundary elements will be added into the new group
11933 * \param aroundElements - if true, elements will be created on boundary of given
11934 * elements else, on boundary of the whole mesh.
11935 * \return nb of added boundary elements
11937 //================================================================================
11939 int SMESH_MeshEditor::MakeBoundaryMesh(const TIDSortedElemSet& elements,
11940 Bnd_Dimension dimension,
11941 SMESH_Group* group/*=0*/,
11942 SMESH_Mesh* targetMesh/*=0*/,
11943 bool toCopyElements/*=false*/,
11944 bool toCopyExistingBoundary/*=false*/,
11945 bool toAddExistingBondary/*= false*/,
11946 bool aroundElements/*= false*/)
11948 SMDSAbs_ElementType missType = (dimension == BND_2DFROM3D) ? SMDSAbs_Face : SMDSAbs_Edge;
11949 SMDSAbs_ElementType elemType = (dimension == BND_1DFROM2D) ? SMDSAbs_Face : SMDSAbs_Volume;
11950 // hope that all elements are of the same type, do not check them all
11951 if ( !elements.empty() && (*elements.begin())->GetType() != elemType )
11952 throw SALOME_Exception(LOCALIZED("wrong element type"));
11955 toCopyElements = toCopyExistingBoundary = false;
11957 SMESH_MeshEditor tgtEditor( targetMesh ? targetMesh : myMesh );
11958 SMESHDS_Mesh* aMesh = GetMeshDS(), *tgtMeshDS = tgtEditor.GetMeshDS();
11959 int nbAddedBnd = 0;
11961 // editor adding present bnd elements and optionally holding elements to add to the group
11962 SMESH_MeshEditor* presentEditor;
11963 SMESH_MeshEditor tgtEditor2( tgtEditor.GetMesh() );
11964 presentEditor = toAddExistingBondary ? &tgtEditor : &tgtEditor2;
11966 SMESH_MesherHelper helper( *myMesh );
11967 const TopAbs_ShapeEnum missShapeType = ( missType==SMDSAbs_Face ? TopAbs_FACE : TopAbs_EDGE );
11968 SMDS_VolumeTool vTool;
11969 TIDSortedElemSet avoidSet;
11970 const TIDSortedElemSet emptySet, *elemSet = aroundElements ? &elements : &emptySet;
11973 typedef vector<const SMDS_MeshNode*> TConnectivity;
11975 SMDS_ElemIteratorPtr eIt;
11976 if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
11977 else eIt = elemSetIterator( elements );
11979 while (eIt->more())
11981 const SMDS_MeshElement* elem = eIt->next();
11982 const int iQuad = elem->IsQuadratic();
11984 // ------------------------------------------------------------------------------------
11985 // 1. For an elem, get present bnd elements and connectivities of missing bnd elements
11986 // ------------------------------------------------------------------------------------
11987 vector<const SMDS_MeshElement*> presentBndElems;
11988 vector<TConnectivity> missingBndElems;
11989 TConnectivity nodes, elemNodes;
11990 if ( vTool.Set(elem, /*ignoreCentralNodes=*/true) ) // elem is a volume --------------
11992 vTool.SetExternalNormal();
11993 const SMDS_MeshElement* otherVol = 0;
11994 for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
11996 if ( !vTool.IsFreeFace(iface, &otherVol) &&
11997 ( !aroundElements || elements.count( otherVol )))
11999 const SMDS_MeshNode** nn = vTool.GetFaceNodes(iface);
12000 const int nbFaceNodes = vTool.NbFaceNodes (iface);
12001 if ( missType == SMDSAbs_Edge ) // boundary edges
12003 nodes.resize( 2+iQuad );
12004 for ( int i = 0; i < nbFaceNodes; i += 1+iQuad)
12006 for ( int j = 0; j < nodes.size(); ++j )
12008 if ( const SMDS_MeshElement* edge =
12009 aMesh->FindElement(nodes,SMDSAbs_Edge,/*noMedium=*/false))
12010 presentBndElems.push_back( edge );
12012 missingBndElems.push_back( nodes );
12015 else // boundary face
12018 for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12019 nodes.push_back( nn[inode] ); // add corner nodes
12021 for ( inode = 1; inode < nbFaceNodes; inode += 2)
12022 nodes.push_back( nn[inode] ); // add medium nodes
12023 int iCenter = vTool.GetCenterNodeIndex(iface); // for HEX27
12025 nodes.push_back( vTool.GetNodes()[ iCenter ] );
12027 if (const SMDS_MeshElement * f = aMesh->FindElement( nodes,
12028 SMDSAbs_Face, /*noMedium=*/false ))
12029 presentBndElems.push_back( f );
12031 missingBndElems.push_back( nodes );
12033 if ( targetMesh != myMesh )
12035 // add 1D elements on face boundary to be added to a new mesh
12036 const SMDS_MeshElement* edge;
12037 for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
12040 edge = aMesh->FindEdge( nn[inode], nn[inode+1], nn[inode+2]);
12042 edge = aMesh->FindEdge( nn[inode], nn[inode+1]);
12043 if ( edge && avoidSet.insert( edge ).second )
12044 presentBndElems.push_back( edge );
12050 else if ( elem->GetType() == SMDSAbs_Face ) // elem is a face ------------------------
12052 avoidSet.clear(), avoidSet.insert( elem );
12053 elemNodes.assign( SMDS_MeshElement::iterator( elem->interlacedNodesElemIterator() ),
12054 SMDS_MeshElement::iterator() );
12055 elemNodes.push_back( elemNodes[0] );
12056 nodes.resize( 2 + iQuad );
12057 const int nbLinks = elem->NbCornerNodes();
12058 for ( int i = 0, iN = 0; i < nbLinks; i++, iN += 1+iQuad )
12060 nodes[0] = elemNodes[iN];
12061 nodes[1] = elemNodes[iN+1+iQuad];
12062 if ( SMESH_MeshAlgos::FindFaceInSet( nodes[0], nodes[1], *elemSet, avoidSet))
12063 continue; // not free link
12065 if ( iQuad ) nodes[2] = elemNodes[iN+1];
12066 if ( const SMDS_MeshElement* edge =
12067 aMesh->FindElement(nodes,SMDSAbs_Edge,/*noMedium=*/false))
12068 presentBndElems.push_back( edge );
12070 missingBndElems.push_back( nodes );
12074 // ---------------------------------
12075 // 2. Add missing boundary elements
12076 // ---------------------------------
12077 if ( targetMesh != myMesh )
12078 // instead of making a map of nodes in this mesh and targetMesh,
12079 // we create nodes with same IDs.
12080 for ( int i = 0; i < missingBndElems.size(); ++i )
12082 TConnectivity& srcNodes = missingBndElems[i];
12083 TConnectivity nodes( srcNodes.size() );
12084 for ( inode = 0; inode < nodes.size(); ++inode )
12085 nodes[inode] = getNodeWithSameID( tgtMeshDS, srcNodes[inode] );
12086 if ( aroundElements && tgtEditor.GetMeshDS()->FindElement( nodes,
12088 /*noMedium=*/false))
12090 tgtEditor.AddElement(nodes, missType, !iQuad && nodes.size()/(iQuad+1)>4);
12094 for ( int i = 0; i < missingBndElems.size(); ++i )
12096 TConnectivity& nodes = missingBndElems[i];
12097 if ( aroundElements && tgtEditor.GetMeshDS()->FindElement( nodes,
12099 /*noMedium=*/false))
12101 SMDS_MeshElement* elem =
12102 tgtEditor.AddElement(nodes, missType, !iQuad && nodes.size()/(iQuad+1)>4);
12105 // try to set a new element to a shape
12106 if ( myMesh->HasShapeToMesh() )
12109 set< pair<TopAbs_ShapeEnum, int > > mediumShapes;
12110 const int nbN = nodes.size() / (iQuad+1 );
12111 for ( inode = 0; inode < nbN && ok; ++inode )
12113 pair<int, TopAbs_ShapeEnum> i_stype =
12114 helper.GetMediumPos( nodes[inode], nodes[(inode+1)%nbN]);
12115 if (( ok = ( i_stype.first > 0 && i_stype.second >= TopAbs_FACE )))
12116 mediumShapes.insert( make_pair ( i_stype.second, i_stype.first ));
12118 if ( ok && mediumShapes.size() > 1 )
12120 set< pair<TopAbs_ShapeEnum, int > >::iterator stype_i = mediumShapes.begin();
12121 pair<TopAbs_ShapeEnum, int> stype_i_0 = *stype_i;
12122 for ( ++stype_i; stype_i != mediumShapes.end() && ok; ++stype_i )
12124 if (( ok = ( stype_i->first != stype_i_0.first )))
12125 ok = helper.IsSubShape( aMesh->IndexToShape( stype_i->second ),
12126 aMesh->IndexToShape( stype_i_0.second ));
12129 if ( ok && mediumShapes.begin()->first == missShapeType )
12130 aMesh->SetMeshElementOnShape( elem, mediumShapes.begin()->second );
12134 // ----------------------------------
12135 // 3. Copy present boundary elements
12136 // ----------------------------------
12137 if ( toCopyExistingBoundary )
12138 for ( int i = 0 ; i < presentBndElems.size(); ++i )
12140 const SMDS_MeshElement* e = presentBndElems[i];
12141 TConnectivity nodes( e->NbNodes() );
12142 for ( inode = 0; inode < nodes.size(); ++inode )
12143 nodes[inode] = getNodeWithSameID( tgtMeshDS, e->GetNode(inode) );
12144 presentEditor->AddElement(nodes, e->GetType(), e->IsPoly());
12146 else // store present elements to add them to a group
12147 for ( int i = 0 ; i < presentBndElems.size(); ++i )
12149 presentEditor->myLastCreatedElems.Append(presentBndElems[i]);
12152 } // loop on given elements
12154 // ---------------------------------------------
12155 // 4. Fill group with boundary elements
12156 // ---------------------------------------------
12159 if ( SMESHDS_Group* g = dynamic_cast<SMESHDS_Group*>( group->GetGroupDS() ))
12160 for ( int i = 0; i < tgtEditor.myLastCreatedElems.Size(); ++i )
12161 g->SMDSGroup().Add( tgtEditor.myLastCreatedElems( i+1 ));
12163 tgtEditor.myLastCreatedElems.Clear();
12164 tgtEditor2.myLastCreatedElems.Clear();
12166 // -----------------------
12167 // 5. Copy given elements
12168 // -----------------------
12169 if ( toCopyElements && targetMesh != myMesh )
12171 if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
12172 else eIt = elemSetIterator( elements );
12173 while (eIt->more())
12175 const SMDS_MeshElement* elem = eIt->next();
12176 TConnectivity nodes( elem->NbNodes() );
12177 for ( inode = 0; inode < nodes.size(); ++inode )
12178 nodes[inode] = getNodeWithSameID( tgtMeshDS, elem->GetNode(inode) );
12179 tgtEditor.AddElement(nodes, elemType, elem->IsPoly());
12181 tgtEditor.myLastCreatedElems.Clear();