1 // Copyright (C) 2007-2013 CEA/DEN, EDF R&D, OPEN CASCADE
3 // Copyright (C) 2003-2007 OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN,
4 // CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS
6 // This library is free software; you can redistribute it and/or
7 // modify it under the terms of the GNU Lesser General Public
8 // License as published by the Free Software Foundation; either
9 // version 2.1 of the License.
11 // This library is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 // Lesser General Public License for more details.
16 // You should have received a copy of the GNU Lesser General Public
17 // License along with this library; if not, write to the Free Software
18 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
23 // File : SMESH_MeshEditor.cxx
24 // Created : Mon Apr 12 16:10:22 2004
25 // Author : Edward AGAPOV (eap)
27 #include "SMESH_MeshEditor.hxx"
29 #include "SMDS_FaceOfNodes.hxx"
30 #include "SMDS_VolumeTool.hxx"
31 #include "SMDS_EdgePosition.hxx"
32 #include "SMDS_FacePosition.hxx"
33 #include "SMDS_SpacePosition.hxx"
34 #include "SMDS_MeshGroup.hxx"
35 #include "SMDS_LinearEdge.hxx"
36 #include "SMDS_Downward.hxx"
37 #include "SMDS_SetIterator.hxx"
39 #include "SMESHDS_Group.hxx"
40 #include "SMESHDS_Mesh.hxx"
42 #include "SMESH_Algo.hxx"
43 #include "SMESH_ControlsDef.hxx"
44 #include "SMESH_Group.hxx"
45 #include "SMESH_MeshAlgos.hxx"
46 #include "SMESH_MesherHelper.hxx"
47 #include "SMESH_OctreeNode.hxx"
48 #include "SMESH_subMesh.hxx"
50 #include <Basics_OCCTVersion.hxx>
52 #include "utilities.h"
54 #include <BRepAdaptor_Surface.hxx>
55 #include <BRepBuilderAPI_MakeEdge.hxx>
56 #include <BRepClass3d_SolidClassifier.hxx>
57 #include <BRep_Tool.hxx>
59 #include <Extrema_GenExtPS.hxx>
60 #include <Extrema_POnCurv.hxx>
61 #include <Extrema_POnSurf.hxx>
62 #include <Geom2d_Curve.hxx>
63 #include <GeomAdaptor_Surface.hxx>
64 #include <Geom_Curve.hxx>
65 #include <Geom_Surface.hxx>
66 #include <Precision.hxx>
67 #include <TColStd_ListOfInteger.hxx>
68 #include <TopAbs_State.hxx>
70 #include <TopExp_Explorer.hxx>
71 #include <TopTools_ListIteratorOfListOfShape.hxx>
72 #include <TopTools_ListOfShape.hxx>
73 #include <TopTools_SequenceOfShape.hxx>
75 #include <TopoDS_Face.hxx>
76 #include <TopoDS_Solid.hxx>
82 #include <gp_Trsf.hxx>
96 #include <boost/tuple/tuple.hpp>
98 #include <Standard_Failure.hxx>
99 #include <Standard_ErrorHandler.hxx>
101 #define cast2Node(elem) static_cast<const SMDS_MeshNode*>( elem )
104 using namespace SMESH::Controls;
108 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 [] = { 0, 0, 0, 0, 0, 0 };
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 = 0, iB = 0, i1 = 0, i2 = 0;
688 for ( i = 0; i < 6; i++ ) {
689 if ( sameInd [ i ] == 0 ) {
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 struct TTriangleFacet //!< stores indices of three nodes of tetra facet
1609 TTriangleFacet(int n1, int n2, int n3): _n1(n1), _n2(n2), _n3(n3) {}
1610 bool contains(int n) const { return ( n == _n1 || n == _n2 || n == _n3 ); }
1611 bool hasAdjacentTetra( const SMDS_MeshElement* elem ) const;
1616 const int* _connectivity; //!< foursomes of tetra connectivy finished by -1
1617 bool _baryNode; //!< additional node is to be created at cell barycenter
1618 bool _ownConn; //!< to delete _connectivity in destructor
1619 map<int, const SMDS_MeshNode*> _faceBaryNode; //!< map face index to node at BC of face
1621 TSplitMethod( int nbTet=0, const int* conn=0, bool addNode=false)
1622 : _nbTetra(nbTet), _connectivity(conn), _baryNode(addNode), _ownConn(false) {}
1623 ~TSplitMethod() { if ( _ownConn ) delete [] _connectivity; _connectivity = 0; }
1624 bool hasFacet( const TTriangleFacet& facet ) const
1626 const int* tetConn = _connectivity;
1627 for ( ; tetConn[0] >= 0; tetConn += 4 )
1628 if (( facet.contains( tetConn[0] ) +
1629 facet.contains( tetConn[1] ) +
1630 facet.contains( tetConn[2] ) +
1631 facet.contains( tetConn[3] )) == 3 )
1637 //=======================================================================
1639 * \brief return TSplitMethod for the given element
1641 //=======================================================================
1643 TSplitMethod getSplitMethod( SMDS_VolumeTool& vol, const int theMethodFlags)
1645 const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
1647 // at HEXA_TO_24 method, each face of volume is split into triangles each based on
1648 // an edge and a face barycenter; tertaherdons are based on triangles and
1649 // a volume barycenter
1650 const bool is24TetMode = ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_24 );
1652 // Find out how adjacent volumes are split
1654 vector < list< TTriangleFacet > > triaSplitsByFace( vol.NbFaces() ); // splits of each side
1655 int hasAdjacentSplits = 0, maxTetConnSize = 0;
1656 for ( int iF = 0; iF < vol.NbFaces(); ++iF )
1658 int nbNodes = vol.NbFaceNodes( iF ) / iQ;
1659 maxTetConnSize += 4 * ( nbNodes - (is24TetMode ? 0 : 2));
1660 if ( nbNodes < 4 ) continue;
1662 list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
1663 const int* nInd = vol.GetFaceNodesIndices( iF );
1666 TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] );
1667 TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] );
1668 if ( t012.hasAdjacentTetra( vol.Element() )) triaSplits.push_back( t012 );
1669 else if ( t123.hasAdjacentTetra( vol.Element() )) triaSplits.push_back( t123 );
1673 int iCom = 0; // common node of triangle faces to split into
1674 for ( int iVar = 0; iVar < nbNodes; ++iVar, ++iCom )
1676 TTriangleFacet t012( nInd[ iQ * ( iCom )],
1677 nInd[ iQ * ( (iCom+1)%nbNodes )],
1678 nInd[ iQ * ( (iCom+2)%nbNodes )]);
1679 TTriangleFacet t023( nInd[ iQ * ( iCom )],
1680 nInd[ iQ * ( (iCom+2)%nbNodes )],
1681 nInd[ iQ * ( (iCom+3)%nbNodes )]);
1682 if ( t012.hasAdjacentTetra( vol.Element() ) && t023.hasAdjacentTetra( vol.Element() ))
1684 triaSplits.push_back( t012 );
1685 triaSplits.push_back( t023 );
1690 if ( !triaSplits.empty() )
1691 hasAdjacentSplits = true;
1694 // Among variants of split method select one compliant with adjacent volumes
1696 TSplitMethod method;
1697 if ( !vol.Element()->IsPoly() && !is24TetMode )
1699 int nbVariants = 2, nbTet = 0;
1700 const int** connVariants = 0;
1701 switch ( vol.Element()->GetEntityType() )
1703 case SMDSEntity_Hexa:
1704 case SMDSEntity_Quad_Hexa:
1705 case SMDSEntity_TriQuad_Hexa:
1706 if ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_5 )
1707 connVariants = theHexTo5, nbTet = 5;
1709 connVariants = theHexTo6, nbTet = 6, nbVariants = 4;
1711 case SMDSEntity_Pyramid:
1712 case SMDSEntity_Quad_Pyramid:
1713 connVariants = thePyraTo2; nbTet = 2;
1715 case SMDSEntity_Penta:
1716 case SMDSEntity_Quad_Penta:
1717 connVariants = thePentaTo3; nbTet = 3; nbVariants = 6;
1722 for ( int variant = 0; variant < nbVariants && method._nbTetra == 0; ++variant )
1724 // check method compliancy with adjacent tetras,
1725 // all found splits must be among facets of tetras described by this method
1726 method = TSplitMethod( nbTet, connVariants[variant] );
1727 if ( hasAdjacentSplits && method._nbTetra > 0 )
1729 bool facetCreated = true;
1730 for ( int iF = 0; facetCreated && iF < triaSplitsByFace.size(); ++iF )
1732 list< TTriangleFacet >::const_iterator facet = triaSplitsByFace[iF].begin();
1733 for ( ; facetCreated && facet != triaSplitsByFace[iF].end(); ++facet )
1734 facetCreated = method.hasFacet( *facet );
1736 if ( !facetCreated )
1737 method = TSplitMethod(0); // incompatible method
1741 if ( method._nbTetra < 1 )
1743 // No standard method is applicable, use a generic solution:
1744 // each facet of a volume is split into triangles and
1745 // each of triangles and a volume barycenter form a tetrahedron.
1747 const bool isHex27 = ( vol.Element()->GetEntityType() == SMDSEntity_TriQuad_Hexa );
1749 int* connectivity = new int[ maxTetConnSize + 1 ];
1750 method._connectivity = connectivity;
1751 method._ownConn = true;
1752 method._baryNode = !isHex27; // to create central node or not
1755 int baryCenInd = vol.NbNodes() - int( isHex27 );
1756 for ( int iF = 0; iF < vol.NbFaces(); ++iF )
1758 const int nbNodes = vol.NbFaceNodes( iF ) / iQ;
1759 const int* nInd = vol.GetFaceNodesIndices( iF );
1760 // find common node of triangle facets of tetra to create
1761 int iCommon = 0; // index in linear numeration
1762 const list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ];
1763 if ( !triaSplits.empty() )
1766 const TTriangleFacet* facet = &triaSplits.front();
1767 for ( ; iCommon < nbNodes-1 ; ++iCommon )
1768 if ( facet->contains( nInd[ iQ * iCommon ]) &&
1769 facet->contains( nInd[ iQ * ((iCommon+2)%nbNodes) ]))
1772 else if ( nbNodes > 3 && !is24TetMode )
1774 // find the best method of splitting into triangles by aspect ratio
1775 SMESH::Controls::NumericalFunctorPtr aspectRatio( new SMESH::Controls::AspectRatio);
1776 map< double, int > badness2iCommon;
1777 const SMDS_MeshNode** nodes = vol.GetFaceNodes( iF );
1778 int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
1779 for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCommon )
1782 for ( int iLast = iCommon+2; iLast < iCommon+nbNodes; ++iLast )
1784 SMDS_FaceOfNodes tria ( nodes[ iQ*( iCommon )],
1785 nodes[ iQ*((iLast-1)%nbNodes)],
1786 nodes[ iQ*((iLast )%nbNodes)]);
1787 badness += getBadRate( &tria, aspectRatio );
1789 badness2iCommon.insert( make_pair( badness, iCommon ));
1791 // use iCommon with lowest badness
1792 iCommon = badness2iCommon.begin()->second;
1794 if ( iCommon >= nbNodes )
1795 iCommon = 0; // something wrong
1797 // fill connectivity of tetrahedra based on a current face
1798 int nbTet = nbNodes - 2;
1799 if ( is24TetMode && nbNodes > 3 && triaSplits.empty())
1804 faceBaryCenInd = vol.GetCenterNodeIndex( iF );
1805 method._faceBaryNode[ iF ] = vol.GetNodes()[ faceBaryCenInd ];
1809 method._faceBaryNode[ iF ] = 0;
1810 faceBaryCenInd = baryCenInd + method._faceBaryNode.size();
1813 for ( int i = 0; i < nbTet; ++i )
1815 int i1 = i, i2 = (i+1) % nbNodes;
1816 if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
1817 connectivity[ connSize++ ] = nInd[ iQ * i1 ];
1818 connectivity[ connSize++ ] = nInd[ iQ * i2 ];
1819 connectivity[ connSize++ ] = faceBaryCenInd;
1820 connectivity[ connSize++ ] = baryCenInd;
1825 for ( int i = 0; i < nbTet; ++i )
1827 int i1 = (iCommon+1+i) % nbNodes, i2 = (iCommon+2+i) % nbNodes;
1828 if ( !vol.IsFaceExternal( iF )) swap( i1, i2 );
1829 connectivity[ connSize++ ] = nInd[ iQ * iCommon ];
1830 connectivity[ connSize++ ] = nInd[ iQ * i1 ];
1831 connectivity[ connSize++ ] = nInd[ iQ * i2 ];
1832 connectivity[ connSize++ ] = baryCenInd;
1835 method._nbTetra += nbTet;
1837 } // loop on volume faces
1839 connectivity[ connSize++ ] = -1;
1841 } // end of generic solution
1845 //================================================================================
1847 * \brief Check if there is a tetraherdon adjacent to the given element via this facet
1849 //================================================================================
1851 bool TTriangleFacet::hasAdjacentTetra( const SMDS_MeshElement* elem ) const
1853 // find the tetrahedron including the three nodes of facet
1854 const SMDS_MeshNode* n1 = elem->GetNode(_n1);
1855 const SMDS_MeshNode* n2 = elem->GetNode(_n2);
1856 const SMDS_MeshNode* n3 = elem->GetNode(_n3);
1857 SMDS_ElemIteratorPtr volIt1 = n1->GetInverseElementIterator(SMDSAbs_Volume);
1858 while ( volIt1->more() )
1860 const SMDS_MeshElement* v = volIt1->next();
1861 SMDSAbs_EntityType type = v->GetEntityType();
1862 if ( type != SMDSEntity_Tetra && type != SMDSEntity_Quad_Tetra )
1864 if ( type == SMDSEntity_Quad_Tetra && v->GetNodeIndex( n1 ) > 3 )
1865 continue; // medium node not allowed
1866 const int ind2 = v->GetNodeIndex( n2 );
1867 if ( ind2 < 0 || 3 < ind2 )
1869 const int ind3 = v->GetNodeIndex( n3 );
1870 if ( ind3 < 0 || 3 < ind3 )
1877 //=======================================================================
1879 * \brief A key of a face of volume
1881 //=======================================================================
1883 struct TVolumeFaceKey: pair< pair< int, int>, pair< int, int> >
1885 TVolumeFaceKey( SMDS_VolumeTool& vol, int iF )
1887 TIDSortedNodeSet sortedNodes;
1888 const int iQ = vol.Element()->IsQuadratic() ? 2 : 1;
1889 int nbNodes = vol.NbFaceNodes( iF );
1890 const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iF );
1891 for ( int i = 0; i < nbNodes; i += iQ )
1892 sortedNodes.insert( fNodes[i] );
1893 TIDSortedNodeSet::iterator n = sortedNodes.begin();
1894 first.first = (*(n++))->GetID();
1895 first.second = (*(n++))->GetID();
1896 second.first = (*(n++))->GetID();
1897 second.second = ( sortedNodes.size() > 3 ) ? (*(n++))->GetID() : 0;
1905 TElemToDelete(const SMDS_MeshElement* theElem, SMESHDS_SubMesh* theSubMesh)
1908 subMesh = theSubMesh;
1910 const SMDS_MeshElement* Elem() const {return elem;}
1911 SMESHDS_SubMesh* Submesh() {return subMesh;}
1912 const SMDS_MeshElement* elem;
1913 SMESHDS_SubMesh* subMesh;
1916 //=======================================================================
1917 //function : SplitVolumesIntoTetra
1918 //purpose : Split volume elements into tetrahedra.
1919 //=======================================================================
1921 void SMESH_MeshEditor::SplitVolumesIntoTetra (const TIDSortedElemSet & theElems,
1922 const int theMethodFlags)
1924 // std-like iterator on coordinates of nodes of mesh element
1925 typedef SMDS_StdIterator< SMESH_TNodeXYZ, SMDS_ElemIteratorPtr > NXyzIterator;
1926 NXyzIterator xyzEnd;
1928 SMDS_VolumeTool volTool;
1929 SMESH_MesherHelper helper( *GetMesh());
1931 SMESHDS_SubMesh* subMesh = 0;//GetMeshDS()->MeshElements(1);
1932 SMESHDS_SubMesh* fSubMesh = 0;//subMesh;
1934 SMESH_SequenceOfElemPtr newNodes, newElems;
1936 // map face of volume to it's baricenrtic node
1937 map< TVolumeFaceKey, const SMDS_MeshNode* > volFace2BaryNode;
1940 TIDSortedElemSet::const_iterator elem = theElems.begin();
1941 std::vector<TElemToDelete> elem_to_delete;
1942 for ( ; elem != theElems.end(); ++elem )
1944 if ( (*elem)->GetType() != SMDSAbs_Volume )
1946 SMDSAbs_EntityType geomType = (*elem)->GetEntityType();
1947 if ( geomType == SMDSEntity_Tetra || geomType == SMDSEntity_Quad_Tetra )
1950 if ( !volTool.Set( *elem, /*ignoreCentralNodes=*/false )) continue; // strange...
1952 TSplitMethod splitMethod = getSplitMethod( volTool, theMethodFlags );
1953 if ( splitMethod._nbTetra < 1 ) continue;
1955 // find submesh to add new tetras to
1956 if ( !subMesh || !subMesh->Contains( *elem ))
1958 int shapeID = FindShape( *elem );
1959 helper.SetSubShape( shapeID ); // helper will add tetras to the found submesh
1960 subMesh = GetMeshDS()->MeshElements( shapeID );
1963 if ( (*elem)->IsQuadratic() )
1966 // add quadratic links to the helper
1967 for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
1969 const SMDS_MeshNode** fNodes = volTool.GetFaceNodes( iF );
1970 int nbN = volTool.NbFaceNodes( iF ) - bool( volTool.GetCenterNodeIndex(iF) > 0 );
1971 for ( int iN = 0; iN < nbN; iN += iQ )
1972 helper.AddTLinkNode( fNodes[iN], fNodes[iN+2], fNodes[iN+1] );
1974 helper.SetIsQuadratic( true );
1979 helper.SetIsQuadratic( false );
1981 vector<const SMDS_MeshNode*> nodes( (*elem)->begin_nodes(), (*elem)->end_nodes() );
1982 helper.SetElementsOnShape( true );
1983 if ( splitMethod._baryNode )
1985 // make a node at barycenter
1986 volTool.GetBaryCenter( bc[0], bc[1], bc[2] );
1987 SMDS_MeshNode* gcNode = helper.AddNode( bc[0], bc[1], bc[2] );
1988 nodes.push_back( gcNode );
1989 newNodes.Append( gcNode );
1991 if ( !splitMethod._faceBaryNode.empty() )
1993 // make or find baricentric nodes of faces
1994 map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.begin();
1995 for ( ; iF_n != splitMethod._faceBaryNode.end(); ++iF_n )
1997 map< TVolumeFaceKey, const SMDS_MeshNode* >::iterator f_n =
1998 volFace2BaryNode.insert
1999 ( make_pair( TVolumeFaceKey( volTool,iF_n->first ), iF_n->second )).first;
2002 volTool.GetFaceBaryCenter( iF_n->first, bc[0], bc[1], bc[2] );
2003 newNodes.Append( f_n->second = helper.AddNode( bc[0], bc[1], bc[2] ));
2005 nodes.push_back( iF_n->second = f_n->second );
2010 vector<const SMDS_MeshElement* > tetras( splitMethod._nbTetra ); // splits of a volume
2011 const int* tetConn = splitMethod._connectivity;
2012 for ( int i = 0; i < splitMethod._nbTetra; ++i, tetConn += 4 )
2013 newElems.Append( tetras[ i ] = helper.AddVolume( nodes[ tetConn[0] ],
2014 nodes[ tetConn[1] ],
2015 nodes[ tetConn[2] ],
2016 nodes[ tetConn[3] ]));
2018 ReplaceElemInGroups( *elem, tetras, GetMeshDS() );
2020 // Split faces on sides of the split volume
2022 const SMDS_MeshNode** volNodes = volTool.GetNodes();
2023 for ( int iF = 0; iF < volTool.NbFaces(); ++iF )
2025 const int nbNodes = volTool.NbFaceNodes( iF ) / iQ;
2026 if ( nbNodes < 4 ) continue;
2028 // find an existing face
2029 vector<const SMDS_MeshNode*> fNodes( volTool.GetFaceNodes( iF ),
2030 volTool.GetFaceNodes( iF ) + volTool.NbFaceNodes( iF ));
2031 while ( const SMDS_MeshElement* face = GetMeshDS()->FindElement( fNodes, SMDSAbs_Face,
2032 /*noMedium=*/false))
2035 helper.SetElementsOnShape( false );
2036 vector< const SMDS_MeshElement* > triangles;
2038 // find submesh to add new triangles in
2039 if ( !fSubMesh || !fSubMesh->Contains( face ))
2041 int shapeID = FindShape( face );
2042 fSubMesh = GetMeshDS()->MeshElements( shapeID );
2044 map<int, const SMDS_MeshNode*>::iterator iF_n = splitMethod._faceBaryNode.find(iF);
2045 if ( iF_n != splitMethod._faceBaryNode.end() )
2047 for ( int iN = 0; iN < nbNodes*iQ; iN += iQ )
2049 const SMDS_MeshNode* n1 = fNodes[iN];
2050 const SMDS_MeshNode *n2 = fNodes[(iN+iQ)%(nbNodes*iQ)];
2051 const SMDS_MeshNode *n3 = iF_n->second;
2052 if ( !volTool.IsFaceExternal( iF ))
2054 triangles.push_back( helper.AddFace( n1,n2,n3 ));
2056 if ( fSubMesh && n3->getshapeId() < 1 )
2057 fSubMesh->AddNode( n3 );
2062 // among possible triangles create ones discribed by split method
2063 const int* nInd = volTool.GetFaceNodesIndices( iF );
2064 int nbVariants = ( nbNodes == 4 ? 2 : nbNodes );
2065 int iCom = 0; // common node of triangle faces to split into
2066 list< TTriangleFacet > facets;
2067 for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCom )
2069 TTriangleFacet t012( nInd[ iQ * ( iCom )],
2070 nInd[ iQ * ( (iCom+1)%nbNodes )],
2071 nInd[ iQ * ( (iCom+2)%nbNodes )]);
2072 TTriangleFacet t023( nInd[ iQ * ( iCom )],
2073 nInd[ iQ * ( (iCom+2)%nbNodes )],
2074 nInd[ iQ * ( (iCom+3)%nbNodes )]);
2075 if ( splitMethod.hasFacet( t012 ) && splitMethod.hasFacet( t023 ))
2077 facets.push_back( t012 );
2078 facets.push_back( t023 );
2079 for ( int iLast = iCom+4; iLast < iCom+nbNodes; ++iLast )
2080 facets.push_back( TTriangleFacet( nInd[ iQ * ( iCom )],
2081 nInd[ iQ * ((iLast-1)%nbNodes )],
2082 nInd[ iQ * ((iLast )%nbNodes )]));
2086 list< TTriangleFacet >::iterator facet = facets.begin();
2087 for ( ; facet != facets.end(); ++facet )
2089 if ( !volTool.IsFaceExternal( iF ))
2090 swap( facet->_n2, facet->_n3 );
2091 triangles.push_back( helper.AddFace( volNodes[ facet->_n1 ],
2092 volNodes[ facet->_n2 ],
2093 volNodes[ facet->_n3 ]));
2096 for ( int i = 0; i < triangles.size(); ++i )
2098 if ( !triangles[i] ) continue;
2100 fSubMesh->AddElement( triangles[i]);
2101 newElems.Append( triangles[i] );
2103 ReplaceElemInGroups( face, triangles, GetMeshDS() );
2104 GetMeshDS()->RemoveFreeElement( face, fSubMesh, /*fromGroups=*/false );
2105 // TElemToDelete faceToDelete(face, fSubMesh);
2106 // elem_to_delete.push_back(faceToDelete);
2109 } // loop on volume faces to split them into triangles
2111 // GetMeshDS()->RemoveFreeElement( *elem, subMesh, /*fromGroups=*/false );
2113 // rnc : don't delete the elem here because it results in a mesh with a free
2114 // ID at the beginning of the ID list. The first tetra is then inserted in O(1)
2115 // but the second one is inserted in O(n), then the whole procedure has almost a O(n^2)
2116 // complexity. If all elements to remove are stored and removed after tetra creation
2117 // we get a O(n) complexity for the whole procedure.
2118 // The memory cost is at worst a 6*n*constant memory occupation (where n is the number of elements)
2119 // before deletion of the hexas and then 5*n*constant instead of a maximum of 5*n*constant.
2120 // So there is a transient 1/5*(memory occupation) additional cost.
2122 // Store the elements to delete
2123 TElemToDelete elemToDelete(*elem, subMesh);
2124 elem_to_delete.push_back(elemToDelete);
2126 if ( geomType == SMDSEntity_TriQuad_Hexa )
2128 // remove medium nodes that could become free
2129 for ( int i = 20; i < volTool.NbNodes(); ++i )
2130 if ( volNodes[i]->NbInverseElements() == 0 )
2131 GetMeshDS()->RemoveNode( volNodes[i] );
2133 } // loop on volumes to split
2135 // Delete stored elements
2136 std::vector<TElemToDelete>::iterator it;
2137 for( it = elem_to_delete.begin(); it!= elem_to_delete.end(); it++)
2139 GetMeshDS()->RemoveFreeElement( it->Elem(), it->Submesh(), /*fromGroups=*/false );
2142 myLastCreatedNodes = newNodes;
2143 myLastCreatedElems = newElems;
2146 //=======================================================================
2147 //function : AddToSameGroups
2148 //purpose : add elemToAdd to the groups the elemInGroups belongs to
2149 //=======================================================================
2151 void SMESH_MeshEditor::AddToSameGroups (const SMDS_MeshElement* elemToAdd,
2152 const SMDS_MeshElement* elemInGroups,
2153 SMESHDS_Mesh * aMesh)
2155 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2156 if (!groups.empty()) {
2157 set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2158 for ( ; grIt != groups.end(); grIt++ ) {
2159 SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2160 if ( group && group->Contains( elemInGroups ))
2161 group->SMDSGroup().Add( elemToAdd );
2167 //=======================================================================
2168 //function : RemoveElemFromGroups
2169 //purpose : Remove removeelem to the groups the elemInGroups belongs to
2170 //=======================================================================
2171 void SMESH_MeshEditor::RemoveElemFromGroups (const SMDS_MeshElement* removeelem,
2172 SMESHDS_Mesh * aMesh)
2174 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2175 if (!groups.empty())
2177 set<SMESHDS_GroupBase*>::const_iterator GrIt = groups.begin();
2178 for (; GrIt != groups.end(); GrIt++)
2180 SMESHDS_Group* grp = dynamic_cast<SMESHDS_Group*>(*GrIt);
2181 if (!grp || grp->IsEmpty()) continue;
2182 grp->SMDSGroup().Remove(removeelem);
2187 //================================================================================
2189 * \brief Replace elemToRm by elemToAdd in the all groups
2191 //================================================================================
2193 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement* elemToRm,
2194 const SMDS_MeshElement* elemToAdd,
2195 SMESHDS_Mesh * aMesh)
2197 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2198 if (!groups.empty()) {
2199 set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2200 for ( ; grIt != groups.end(); grIt++ ) {
2201 SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2202 if ( group && group->SMDSGroup().Remove( elemToRm ) && elemToAdd )
2203 group->SMDSGroup().Add( elemToAdd );
2208 //================================================================================
2210 * \brief Replace elemToRm by elemToAdd in the all groups
2212 //================================================================================
2214 void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement* elemToRm,
2215 const vector<const SMDS_MeshElement*>& elemToAdd,
2216 SMESHDS_Mesh * aMesh)
2218 const set<SMESHDS_GroupBase*>& groups = aMesh->GetGroups();
2219 if (!groups.empty())
2221 set<SMESHDS_GroupBase*>::const_iterator grIt = groups.begin();
2222 for ( ; grIt != groups.end(); grIt++ ) {
2223 SMESHDS_Group* group = dynamic_cast<SMESHDS_Group*>( *grIt );
2224 if ( group && group->SMDSGroup().Remove( elemToRm ) )
2225 for ( int i = 0; i < elemToAdd.size(); ++i )
2226 group->SMDSGroup().Add( elemToAdd[ i ] );
2231 //=======================================================================
2232 //function : QuadToTri
2233 //purpose : Cut quadrangles into triangles.
2234 // theCrit is used to select a diagonal to cut
2235 //=======================================================================
2237 bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet & theElems,
2238 const bool the13Diag)
2240 myLastCreatedElems.Clear();
2241 myLastCreatedNodes.Clear();
2243 MESSAGE( "::QuadToTri()" );
2245 SMESHDS_Mesh * aMesh = GetMeshDS();
2247 Handle(Geom_Surface) surface;
2248 SMESH_MesherHelper helper( *GetMesh() );
2250 TIDSortedElemSet::iterator itElem;
2251 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
2252 const SMDS_MeshElement* elem = *itElem;
2253 if ( !elem || elem->GetType() != SMDSAbs_Face )
2255 bool isquad = elem->NbNodes()==4 || elem->NbNodes()==8;
2256 if(!isquad) continue;
2258 if(elem->NbNodes()==4) {
2259 // retrieve element nodes
2260 const SMDS_MeshNode* aNodes [4];
2261 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
2263 while ( itN->more() )
2264 aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
2266 int aShapeId = FindShape( elem );
2267 const SMDS_MeshElement* newElem1 = 0;
2268 const SMDS_MeshElement* newElem2 = 0;
2270 newElem1 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] );
2271 newElem2 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] );
2274 newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] );
2275 newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] );
2277 myLastCreatedElems.Append(newElem1);
2278 myLastCreatedElems.Append(newElem2);
2279 // put a new triangle on the same shape and add to the same groups
2282 aMesh->SetMeshElementOnShape( newElem1, aShapeId );
2283 aMesh->SetMeshElementOnShape( newElem2, aShapeId );
2285 AddToSameGroups( newElem1, elem, aMesh );
2286 AddToSameGroups( newElem2, elem, aMesh );
2287 //aMesh->RemoveFreeElement(elem, aMesh->MeshElements(aShapeId), true);
2288 aMesh->RemoveElement( elem );
2291 // Quadratic quadrangle
2293 if( elem->NbNodes()==8 && elem->IsQuadratic() ) {
2295 // get surface elem is on
2296 int aShapeId = FindShape( elem );
2297 if ( aShapeId != helper.GetSubShapeID() ) {
2301 shape = aMesh->IndexToShape( aShapeId );
2302 if ( !shape.IsNull() && shape.ShapeType() == TopAbs_FACE ) {
2303 TopoDS_Face face = TopoDS::Face( shape );
2304 surface = BRep_Tool::Surface( face );
2305 if ( !surface.IsNull() )
2306 helper.SetSubShape( shape );
2310 const SMDS_MeshNode* aNodes [8];
2311 const SMDS_MeshNode* inFaceNode = 0;
2312 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
2314 while ( itN->more() ) {
2315 aNodes[ i++ ] = static_cast<const SMDS_MeshNode*>( itN->next() );
2316 if ( !inFaceNode && helper.GetNodeUVneedInFaceNode() &&
2317 aNodes[ i-1 ]->GetPosition()->GetTypeOfPosition() == SMDS_TOP_FACE )
2319 inFaceNode = aNodes[ i-1 ];
2323 // find middle point for (0,1,2,3)
2324 // and create a node in this point;
2326 if ( surface.IsNull() ) {
2328 p += gp_XYZ(aNodes[i]->X(), aNodes[i]->Y(), aNodes[i]->Z() );
2332 TopoDS_Face geomFace = TopoDS::Face( helper.GetSubShape() );
2335 uv += helper.GetNodeUV( geomFace, aNodes[i], inFaceNode );
2337 p = surface->Value( uv.X(), uv.Y() ).XYZ();
2339 const SMDS_MeshNode* newN = aMesh->AddNode( p.X(), p.Y(), p.Z() );
2340 myLastCreatedNodes.Append(newN);
2342 // create a new element
2343 const SMDS_MeshElement* newElem1 = 0;
2344 const SMDS_MeshElement* newElem2 = 0;
2346 newElem1 = aMesh->AddFace(aNodes[2], aNodes[3], aNodes[0],
2347 aNodes[6], aNodes[7], newN );
2348 newElem2 = aMesh->AddFace(aNodes[2], aNodes[0], aNodes[1],
2349 newN, aNodes[4], aNodes[5] );
2352 newElem1 = aMesh->AddFace(aNodes[3], aNodes[0], aNodes[1],
2353 aNodes[7], aNodes[4], newN );
2354 newElem2 = aMesh->AddFace(aNodes[3], aNodes[1], aNodes[2],
2355 newN, aNodes[5], aNodes[6] );
2357 myLastCreatedElems.Append(newElem1);
2358 myLastCreatedElems.Append(newElem2);
2359 // put a new triangle on the same shape and add to the same groups
2362 aMesh->SetMeshElementOnShape( newElem1, aShapeId );
2363 aMesh->SetMeshElementOnShape( newElem2, aShapeId );
2365 AddToSameGroups( newElem1, elem, aMesh );
2366 AddToSameGroups( newElem2, elem, aMesh );
2367 aMesh->RemoveElement( elem );
2374 //=======================================================================
2375 //function : getAngle
2377 //=======================================================================
2379 double getAngle(const SMDS_MeshElement * tr1,
2380 const SMDS_MeshElement * tr2,
2381 const SMDS_MeshNode * n1,
2382 const SMDS_MeshNode * n2)
2384 double angle = 2. * M_PI; // bad angle
2387 SMESH::Controls::TSequenceOfXYZ P1, P2;
2388 if ( !SMESH::Controls::NumericalFunctor::GetPoints( tr1, P1 ) ||
2389 !SMESH::Controls::NumericalFunctor::GetPoints( tr2, P2 ))
2392 if(!tr1->IsQuadratic())
2393 N1 = gp_Vec( P1(2) - P1(1) ) ^ gp_Vec( P1(3) - P1(1) );
2395 N1 = gp_Vec( P1(3) - P1(1) ) ^ gp_Vec( P1(5) - P1(1) );
2396 if ( N1.SquareMagnitude() <= gp::Resolution() )
2398 if(!tr2->IsQuadratic())
2399 N2 = gp_Vec( P2(2) - P2(1) ) ^ gp_Vec( P2(3) - P2(1) );
2401 N2 = gp_Vec( P2(3) - P2(1) ) ^ gp_Vec( P2(5) - P2(1) );
2402 if ( N2.SquareMagnitude() <= gp::Resolution() )
2405 // find the first diagonal node n1 in the triangles:
2406 // take in account a diagonal link orientation
2407 const SMDS_MeshElement *nFirst[2], *tr[] = { tr1, tr2 };
2408 for ( int t = 0; t < 2; t++ ) {
2409 SMDS_ElemIteratorPtr it = tr[ t ]->nodesIterator();
2410 int i = 0, iDiag = -1;
2411 while ( it->more()) {
2412 const SMDS_MeshElement *n = it->next();
2413 if ( n == n1 || n == n2 ) {
2417 if ( i - iDiag == 1 )
2418 nFirst[ t ] = ( n == n1 ? n2 : n1 );
2427 if ( nFirst[ 0 ] == nFirst[ 1 ] )
2430 angle = N1.Angle( N2 );
2435 // =================================================
2436 // class generating a unique ID for a pair of nodes
2437 // and able to return nodes by that ID
2438 // =================================================
2442 LinkID_Gen( const SMESHDS_Mesh* theMesh )
2443 :myMesh( theMesh ), myMaxID( theMesh->MaxNodeID() + 1)
2446 long GetLinkID (const SMDS_MeshNode * n1,
2447 const SMDS_MeshNode * n2) const
2449 return ( Min(n1->GetID(),n2->GetID()) * myMaxID + Max(n1->GetID(),n2->GetID()));
2452 bool GetNodes (const long theLinkID,
2453 const SMDS_MeshNode* & theNode1,
2454 const SMDS_MeshNode* & theNode2) const
2456 theNode1 = myMesh->FindNode( theLinkID / myMaxID );
2457 if ( !theNode1 ) return false;
2458 theNode2 = myMesh->FindNode( theLinkID % myMaxID );
2459 if ( !theNode2 ) return false;
2465 const SMESHDS_Mesh* myMesh;
2470 //=======================================================================
2471 //function : TriToQuad
2472 //purpose : Fuse neighbour triangles into quadrangles.
2473 // theCrit is used to select a neighbour to fuse with.
2474 // theMaxAngle is a max angle between element normals at which
2475 // fusion is still performed.
2476 //=======================================================================
2478 bool SMESH_MeshEditor::TriToQuad (TIDSortedElemSet & theElems,
2479 SMESH::Controls::NumericalFunctorPtr theCrit,
2480 const double theMaxAngle)
2482 myLastCreatedElems.Clear();
2483 myLastCreatedNodes.Clear();
2485 MESSAGE( "::TriToQuad()" );
2487 if ( !theCrit.get() )
2490 SMESHDS_Mesh * aMesh = GetMeshDS();
2492 // Prepare data for algo: build
2493 // 1. map of elements with their linkIDs
2494 // 2. map of linkIDs with their elements
2496 map< SMESH_TLink, list< const SMDS_MeshElement* > > mapLi_listEl;
2497 map< SMESH_TLink, list< const SMDS_MeshElement* > >::iterator itLE;
2498 map< const SMDS_MeshElement*, set< SMESH_TLink > > mapEl_setLi;
2499 map< const SMDS_MeshElement*, set< SMESH_TLink > >::iterator itEL;
2501 TIDSortedElemSet::iterator itElem;
2502 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
2504 const SMDS_MeshElement* elem = *itElem;
2505 if(!elem || elem->GetType() != SMDSAbs_Face ) continue;
2506 bool IsTria = ( elem->NbCornerNodes()==3 );
2507 if (!IsTria) continue;
2509 // retrieve element nodes
2510 const SMDS_MeshNode* aNodes [4];
2511 SMDS_NodeIteratorPtr itN = elem->nodeIterator();
2514 aNodes[ i++ ] = itN->next();
2515 aNodes[ 3 ] = aNodes[ 0 ];
2518 for ( i = 0; i < 3; i++ ) {
2519 SMESH_TLink link( aNodes[i], aNodes[i+1] );
2520 // check if elements sharing a link can be fused
2521 itLE = mapLi_listEl.find( link );
2522 if ( itLE != mapLi_listEl.end() ) {
2523 if ((*itLE).second.size() > 1 ) // consider only 2 elems adjacent by a link
2525 const SMDS_MeshElement* elem2 = (*itLE).second.front();
2526 //if ( FindShape( elem ) != FindShape( elem2 ))
2527 // continue; // do not fuse triangles laying on different shapes
2528 if ( getAngle( elem, elem2, aNodes[i], aNodes[i+1] ) > theMaxAngle )
2529 continue; // avoid making badly shaped quads
2530 (*itLE).second.push_back( elem );
2533 mapLi_listEl[ link ].push_back( elem );
2535 mapEl_setLi [ elem ].insert( link );
2538 // Clean the maps from the links shared by a sole element, ie
2539 // links to which only one element is bound in mapLi_listEl
2541 for ( itLE = mapLi_listEl.begin(); itLE != mapLi_listEl.end(); itLE++ ) {
2542 int nbElems = (*itLE).second.size();
2543 if ( nbElems < 2 ) {
2544 const SMDS_MeshElement* elem = (*itLE).second.front();
2545 SMESH_TLink link = (*itLE).first;
2546 mapEl_setLi[ elem ].erase( link );
2547 if ( mapEl_setLi[ elem ].empty() )
2548 mapEl_setLi.erase( elem );
2552 // Algo: fuse triangles into quadrangles
2554 while ( ! mapEl_setLi.empty() ) {
2555 // Look for the start element:
2556 // the element having the least nb of shared links
2557 const SMDS_MeshElement* startElem = 0;
2559 for ( itEL = mapEl_setLi.begin(); itEL != mapEl_setLi.end(); itEL++ ) {
2560 int nbLinks = (*itEL).second.size();
2561 if ( nbLinks < minNbLinks ) {
2562 startElem = (*itEL).first;
2563 minNbLinks = nbLinks;
2564 if ( minNbLinks == 1 )
2569 // search elements to fuse starting from startElem or links of elements
2570 // fused earlyer - startLinks
2571 list< SMESH_TLink > startLinks;
2572 while ( startElem || !startLinks.empty() ) {
2573 while ( !startElem && !startLinks.empty() ) {
2574 // Get an element to start, by a link
2575 SMESH_TLink linkId = startLinks.front();
2576 startLinks.pop_front();
2577 itLE = mapLi_listEl.find( linkId );
2578 if ( itLE != mapLi_listEl.end() ) {
2579 list< const SMDS_MeshElement* > & listElem = (*itLE).second;
2580 list< const SMDS_MeshElement* >::iterator itE = listElem.begin();
2581 for ( ; itE != listElem.end() ; itE++ )
2582 if ( mapEl_setLi.find( (*itE) ) != mapEl_setLi.end() )
2584 mapLi_listEl.erase( itLE );
2589 // Get candidates to be fused
2590 const SMDS_MeshElement *tr1 = startElem, *tr2 = 0, *tr3 = 0;
2591 const SMESH_TLink *link12, *link13;
2593 ASSERT( mapEl_setLi.find( tr1 ) != mapEl_setLi.end() );
2594 set< SMESH_TLink >& setLi = mapEl_setLi[ tr1 ];
2595 ASSERT( !setLi.empty() );
2596 set< SMESH_TLink >::iterator itLi;
2597 for ( itLi = setLi.begin(); itLi != setLi.end(); itLi++ )
2599 const SMESH_TLink & link = (*itLi);
2600 itLE = mapLi_listEl.find( link );
2601 if ( itLE == mapLi_listEl.end() )
2604 const SMDS_MeshElement* elem = (*itLE).second.front();
2606 elem = (*itLE).second.back();
2607 mapLi_listEl.erase( itLE );
2608 if ( mapEl_setLi.find( elem ) == mapEl_setLi.end())
2619 // add other links of elem to list of links to re-start from
2620 set< SMESH_TLink >& links = mapEl_setLi[ elem ];
2621 set< SMESH_TLink >::iterator it;
2622 for ( it = links.begin(); it != links.end(); it++ ) {
2623 const SMESH_TLink& link2 = (*it);
2624 if ( link2 != link )
2625 startLinks.push_back( link2 );
2629 // Get nodes of possible quadrangles
2630 const SMDS_MeshNode *n12 [4], *n13 [4];
2631 bool Ok12 = false, Ok13 = false;
2632 const SMDS_MeshNode *linkNode1, *linkNode2;
2634 linkNode1 = link12->first;
2635 linkNode2 = link12->second;
2636 if ( tr2 && getQuadrangleNodes( n12, linkNode1, linkNode2, tr1, tr2 ))
2640 linkNode1 = link13->first;
2641 linkNode2 = link13->second;
2642 if ( tr3 && getQuadrangleNodes( n13, linkNode1, linkNode2, tr1, tr3 ))
2646 // Choose a pair to fuse
2647 if ( Ok12 && Ok13 ) {
2648 SMDS_FaceOfNodes quad12 ( n12[ 0 ], n12[ 1 ], n12[ 2 ], n12[ 3 ] );
2649 SMDS_FaceOfNodes quad13 ( n13[ 0 ], n13[ 1 ], n13[ 2 ], n13[ 3 ] );
2650 double aBadRate12 = getBadRate( &quad12, theCrit );
2651 double aBadRate13 = getBadRate( &quad13, theCrit );
2652 if ( aBadRate13 < aBadRate12 )
2659 // and remove fused elems and remove links from the maps
2660 mapEl_setLi.erase( tr1 );
2663 mapEl_setLi.erase( tr2 );
2664 mapLi_listEl.erase( *link12 );
2665 if ( tr1->NbNodes() == 3 )
2667 const SMDS_MeshElement* newElem = 0;
2668 newElem = aMesh->AddFace(n12[0], n12[1], n12[2], n12[3] );
2669 myLastCreatedElems.Append(newElem);
2670 AddToSameGroups( newElem, tr1, aMesh );
2671 int aShapeId = tr1->getshapeId();
2673 aMesh->SetMeshElementOnShape( newElem, aShapeId );
2674 aMesh->RemoveElement( tr1 );
2675 aMesh->RemoveElement( tr2 );
2678 vector< const SMDS_MeshNode* > N1;
2679 vector< const SMDS_MeshNode* > N2;
2680 getNodesFromTwoTria(tr1,tr2,N1,N2);
2681 // now we receive following N1 and N2 (using numeration as in image in InverseDiag())
2682 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
2683 // i.e. first nodes from both arrays form a new diagonal
2684 const SMDS_MeshNode* aNodes[8];
2693 const SMDS_MeshElement* newElem = 0;
2694 if ( N1.size() == 7 || N2.size() == 7 ) // biquadratic
2695 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
2696 aNodes[4], aNodes[5], aNodes[6], aNodes[7], N1[4]);
2698 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
2699 aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
2700 myLastCreatedElems.Append(newElem);
2701 AddToSameGroups( newElem, tr1, aMesh );
2702 int aShapeId = tr1->getshapeId();
2704 aMesh->SetMeshElementOnShape( newElem, aShapeId );
2705 aMesh->RemoveElement( tr1 );
2706 aMesh->RemoveElement( tr2 );
2707 // remove middle node (9)
2708 if ( N1[4]->NbInverseElements() == 0 )
2709 aMesh->RemoveNode( N1[4] );
2710 if ( N1.size() == 7 && N1[6]->NbInverseElements() == 0 )
2711 aMesh->RemoveNode( N1[6] );
2712 if ( N2.size() == 7 && N2[6]->NbInverseElements() == 0 )
2713 aMesh->RemoveNode( N2[6] );
2718 mapEl_setLi.erase( tr3 );
2719 mapLi_listEl.erase( *link13 );
2720 if ( tr1->NbNodes() == 3 ) {
2721 const SMDS_MeshElement* newElem = 0;
2722 newElem = aMesh->AddFace(n13[0], n13[1], n13[2], n13[3] );
2723 myLastCreatedElems.Append(newElem);
2724 AddToSameGroups( newElem, tr1, aMesh );
2725 int aShapeId = tr1->getshapeId();
2727 aMesh->SetMeshElementOnShape( newElem, aShapeId );
2728 aMesh->RemoveElement( tr1 );
2729 aMesh->RemoveElement( tr3 );
2732 vector< const SMDS_MeshNode* > N1;
2733 vector< const SMDS_MeshNode* > N2;
2734 getNodesFromTwoTria(tr1,tr3,N1,N2);
2735 // now we receive following N1 and N2 (using numeration as above image)
2736 // tria1 : (1 2 4 5 9 7) and tria2 : (3 4 2 8 9 6)
2737 // i.e. first nodes from both arrays form a new diagonal
2738 const SMDS_MeshNode* aNodes[8];
2747 const SMDS_MeshElement* newElem = 0;
2748 if ( N1.size() == 7 || N2.size() == 7 ) // biquadratic
2749 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
2750 aNodes[4], aNodes[5], aNodes[6], aNodes[7], N1[4]);
2752 newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3],
2753 aNodes[4], aNodes[5], aNodes[6], aNodes[7]);
2754 myLastCreatedElems.Append(newElem);
2755 AddToSameGroups( newElem, tr1, aMesh );
2756 int aShapeId = tr1->getshapeId();
2758 aMesh->SetMeshElementOnShape( newElem, aShapeId );
2759 aMesh->RemoveElement( tr1 );
2760 aMesh->RemoveElement( tr3 );
2761 // remove middle node (9)
2762 if ( N1[4]->NbInverseElements() == 0 )
2763 aMesh->RemoveNode( N1[4] );
2764 if ( N1.size() == 7 && N1[6]->NbInverseElements() == 0 )
2765 aMesh->RemoveNode( N1[6] );
2766 if ( N2.size() == 7 && N2[6]->NbInverseElements() == 0 )
2767 aMesh->RemoveNode( N2[6] );
2771 // Next element to fuse: the rejected one
2773 startElem = Ok12 ? tr3 : tr2;
2775 } // if ( startElem )
2776 } // while ( startElem || !startLinks.empty() )
2777 } // while ( ! mapEl_setLi.empty() )
2783 /*#define DUMPSO(txt) \
2784 // cout << txt << endl;
2785 //=============================================================================
2789 //=============================================================================
2790 static void swap( int i1, int i2, int idNodes[], gp_Pnt P[] )
2794 int tmp = idNodes[ i1 ];
2795 idNodes[ i1 ] = idNodes[ i2 ];
2796 idNodes[ i2 ] = tmp;
2797 gp_Pnt Ptmp = P[ i1 ];
2800 DUMPSO( i1 << "(" << idNodes[ i2 ] << ") <-> " << i2 << "(" << idNodes[ i1 ] << ")");
2803 //=======================================================================
2804 //function : SortQuadNodes
2805 //purpose : Set 4 nodes of a quadrangle face in a good order.
2806 // Swap 1<->2 or 2<->3 nodes and correspondingly return
2808 //=======================================================================
2810 int SMESH_MeshEditor::SortQuadNodes (const SMDS_Mesh * theMesh,
2815 for ( i = 0; i < 4; i++ ) {
2816 const SMDS_MeshNode *n = theMesh->FindNode( idNodes[i] );
2818 P[ i ].SetCoord( n->X(), n->Y(), n->Z() );
2821 gp_Vec V1(P[0], P[1]);
2822 gp_Vec V2(P[0], P[2]);
2823 gp_Vec V3(P[0], P[3]);
2825 gp_Vec Cross1 = V1 ^ V2;
2826 gp_Vec Cross2 = V2 ^ V3;
2829 if (Cross1.Dot(Cross2) < 0)
2834 if (Cross1.Dot(Cross2) < 0)
2838 swap ( i, i + 1, idNodes, P );
2840 // for ( int ii = 0; ii < 4; ii++ ) {
2841 // const SMDS_MeshNode *n = theMesh->FindNode( idNodes[ii] );
2842 // DUMPSO( ii << "(" << idNodes[ii] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
2848 //=======================================================================
2849 //function : SortHexaNodes
2850 //purpose : Set 8 nodes of a hexahedron in a good order.
2851 // Return success status
2852 //=======================================================================
2854 bool SMESH_MeshEditor::SortHexaNodes (const SMDS_Mesh * theMesh,
2859 DUMPSO( "INPUT: ========================================");
2860 for ( i = 0; i < 8; i++ ) {
2861 const SMDS_MeshNode *n = theMesh->FindNode( idNodes[i] );
2862 if ( !n ) return false;
2863 P[ i ].SetCoord( n->X(), n->Y(), n->Z() );
2864 DUMPSO( i << "(" << idNodes[i] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
2866 DUMPSO( "========================================");
2869 set<int> faceNodes; // ids of bottom face nodes, to be found
2870 set<int> checkedId1; // ids of tried 2-nd nodes
2871 Standard_Real leastDist = DBL_MAX; // dist of the 4-th node from 123 plane
2872 const Standard_Real tol = 1.e-6; // tolerance to find nodes in plane
2873 int iMin, iLoop1 = 0;
2875 // Loop to try the 2-nd nodes
2877 while ( leastDist > DBL_MIN && ++iLoop1 < 8 )
2879 // Find not checked 2-nd node
2880 for ( i = 1; i < 8; i++ )
2881 if ( checkedId1.find( idNodes[i] ) == checkedId1.end() ) {
2882 int id1 = idNodes[i];
2883 swap ( 1, i, idNodes, P );
2884 checkedId1.insert ( id1 );
2888 // Find the 3-d node so that 1-2-3 triangle to be on a hexa face,
2889 // ie that all but meybe one (id3 which is on the same face) nodes
2890 // lay on the same side from the triangle plane.
2892 bool manyInPlane = false; // more than 4 nodes lay in plane
2894 while ( ++iLoop2 < 6 ) {
2896 // get 1-2-3 plane coeffs
2897 Standard_Real A, B, C, D;
2898 gp_Vec N = gp_Vec (P[0], P[1]).Crossed( gp_Vec (P[0], P[2]) );
2899 if ( N.SquareMagnitude() > gp::Resolution() )
2901 gp_Pln pln ( P[0], N );
2902 pln.Coefficients( A, B, C, D );
2904 // find the node (iMin) closest to pln
2905 Standard_Real dist[ 8 ], minDist = DBL_MAX;
2907 for ( i = 3; i < 8; i++ ) {
2908 dist[i] = A * P[i].X() + B * P[i].Y() + C * P[i].Z() + D;
2909 if ( fabs( dist[i] ) < minDist ) {
2910 minDist = fabs( dist[i] );
2913 if ( fabs( dist[i] ) <= tol )
2914 idInPln.insert( idNodes[i] );
2917 // there should not be more than 4 nodes in bottom plane
2918 if ( idInPln.size() > 1 )
2920 DUMPSO( "### idInPln.size() = " << idInPln.size());
2921 // idInPlane does not contain the first 3 nodes
2922 if ( manyInPlane || idInPln.size() == 5)
2923 return false; // all nodes in one plane
2926 // set the 1-st node to be not in plane
2927 for ( i = 3; i < 8; i++ ) {
2928 if ( idInPln.find( idNodes[ i ] ) == idInPln.end() ) {
2929 DUMPSO( "### Reset 0-th node");
2930 swap( 0, i, idNodes, P );
2935 // reset to re-check second nodes
2936 leastDist = DBL_MAX;
2940 break; // from iLoop2;
2943 // check that the other 4 nodes are on the same side
2944 bool sameSide = true;
2945 bool isNeg = dist[ iMin == 3 ? 4 : 3 ] <= 0.;
2946 for ( i = 3; sameSide && i < 8; i++ ) {
2948 sameSide = ( isNeg == dist[i] <= 0.);
2951 // keep best solution
2952 if ( sameSide && minDist < leastDist ) {
2953 leastDist = minDist;
2955 faceNodes.insert( idNodes[ 1 ] );
2956 faceNodes.insert( idNodes[ 2 ] );
2957 faceNodes.insert( idNodes[ iMin ] );
2958 DUMPSO( "loop " << iLoop2 << " id2 " << idNodes[ 1 ] << " id3 " << idNodes[ 2 ]
2959 << " leastDist = " << leastDist);
2960 if ( leastDist <= DBL_MIN )
2965 // set next 3-d node to check
2966 int iNext = 2 + iLoop2;
2968 DUMPSO( "Try 2-nd");
2969 swap ( 2, iNext, idNodes, P );
2971 } // while ( iLoop2 < 6 )
2974 if ( faceNodes.empty() ) return false;
2976 // Put the faceNodes in proper places
2977 for ( i = 4; i < 8; i++ ) {
2978 if ( faceNodes.find( idNodes[ i ] ) != faceNodes.end() ) {
2979 // find a place to put
2981 while ( faceNodes.find( idNodes[ iTo ] ) != faceNodes.end() )
2983 DUMPSO( "Set faceNodes");
2984 swap ( iTo, i, idNodes, P );
2989 // Set nodes of the found bottom face in good order
2990 DUMPSO( " Found bottom face: ");
2991 i = SortQuadNodes( theMesh, idNodes );
2993 gp_Pnt Ptmp = P[ i ];
2998 // for ( int ii = 0; ii < 4; ii++ ) {
2999 // const SMDS_MeshNode *n = theMesh->FindNode( idNodes[ii] );
3000 // DUMPSO( ii << "(" << idNodes[ii] <<") : "<<n->X()<<" "<<n->Y()<<" "<<n->Z());
3003 // Gravity center of the top and bottom faces
3004 gp_Pnt aGCb = ( P[0].XYZ() + P[1].XYZ() + P[2].XYZ() + P[3].XYZ() ) / 4.;
3005 gp_Pnt aGCt = ( P[4].XYZ() + P[5].XYZ() + P[6].XYZ() + P[7].XYZ() ) / 4.;
3007 // Get direction from the bottom to the top face
3008 gp_Vec upDir ( aGCb, aGCt );
3009 Standard_Real upDirSize = upDir.Magnitude();
3010 if ( upDirSize <= gp::Resolution() ) return false;
3013 // Assure that the bottom face normal points up
3014 gp_Vec Nb = gp_Vec (P[0], P[1]).Crossed( gp_Vec (P[0], P[2]) );
3015 Nb += gp_Vec (P[0], P[2]).Crossed( gp_Vec (P[0], P[3]) );
3016 if ( Nb.Dot( upDir ) < 0 ) {
3017 DUMPSO( "Reverse bottom face");
3018 swap( 1, 3, idNodes, P );
3021 // Find 5-th node - the one closest to the 1-st among the last 4 nodes.
3022 Standard_Real minDist = DBL_MAX;
3023 for ( i = 4; i < 8; i++ ) {
3024 // projection of P[i] to the plane defined by P[0] and upDir
3025 gp_Pnt Pp = P[i].Translated( upDir * ( upDir.Dot( gp_Vec( P[i], P[0] ))));
3026 Standard_Real sqDist = P[0].SquareDistance( Pp );
3027 if ( sqDist < minDist ) {
3032 DUMPSO( "Set 4-th");
3033 swap ( 4, iMin, idNodes, P );
3035 // Set nodes of the top face in good order
3036 DUMPSO( "Sort top face");
3037 i = SortQuadNodes( theMesh, &idNodes[4] );
3040 gp_Pnt Ptmp = P[ i ];
3045 // Assure that direction of the top face normal is from the bottom face
3046 gp_Vec Nt = gp_Vec (P[4], P[5]).Crossed( gp_Vec (P[4], P[6]) );
3047 Nt += gp_Vec (P[4], P[6]).Crossed( gp_Vec (P[4], P[7]) );
3048 if ( Nt.Dot( upDir ) < 0 ) {
3049 DUMPSO( "Reverse top face");
3050 swap( 5, 7, idNodes, P );
3053 // DUMPSO( "OUTPUT: ========================================");
3054 // for ( i = 0; i < 8; i++ ) {
3055 // float *p = ugrid->GetPoint(idNodes[i]);
3056 // DUMPSO( i << "(" << idNodes[i] << ") : " << p[0] << " " << p[1] << " " << p[2]);
3062 //================================================================================
3064 * \brief Return nodes linked to the given one
3065 * \param theNode - the node
3066 * \param linkedNodes - the found nodes
3067 * \param type - the type of elements to check
3069 * Medium nodes are ignored
3071 //================================================================================
3073 void SMESH_MeshEditor::GetLinkedNodes( const SMDS_MeshNode* theNode,
3074 TIDSortedElemSet & linkedNodes,
3075 SMDSAbs_ElementType type )
3077 SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(type);
3078 while ( elemIt->more() )
3080 const SMDS_MeshElement* elem = elemIt->next();
3081 if(elem->GetType() == SMDSAbs_0DElement)
3084 SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
3085 if ( elem->GetType() == SMDSAbs_Volume )
3087 SMDS_VolumeTool vol( elem );
3088 while ( nodeIt->more() ) {
3089 const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
3090 if ( theNode != n && vol.IsLinked( theNode, n ))
3091 linkedNodes.insert( n );
3096 for ( int i = 0; nodeIt->more(); ++i ) {
3097 const SMDS_MeshNode* n = cast2Node( nodeIt->next() );
3098 if ( n == theNode ) {
3099 int iBefore = i - 1;
3101 if ( elem->IsQuadratic() ) {
3102 int nb = elem->NbNodes() / 2;
3103 iAfter = SMESH_MesherHelper::WrapIndex( iAfter, nb );
3104 iBefore = SMESH_MesherHelper::WrapIndex( iBefore, nb );
3106 linkedNodes.insert( elem->GetNodeWrap( iAfter ));
3107 linkedNodes.insert( elem->GetNodeWrap( iBefore ));
3114 //=======================================================================
3115 //function : laplacianSmooth
3116 //purpose : pulls theNode toward the center of surrounding nodes directly
3117 // connected to that node along an element edge
3118 //=======================================================================
3120 void laplacianSmooth(const SMDS_MeshNode* theNode,
3121 const Handle(Geom_Surface)& theSurface,
3122 map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
3124 // find surrounding nodes
3126 TIDSortedElemSet nodeSet;
3127 SMESH_MeshEditor::GetLinkedNodes( theNode, nodeSet, SMDSAbs_Face );
3129 // compute new coodrs
3131 double coord[] = { 0., 0., 0. };
3132 TIDSortedElemSet::iterator nodeSetIt = nodeSet.begin();
3133 for ( ; nodeSetIt != nodeSet.end(); nodeSetIt++ ) {
3134 const SMDS_MeshNode* node = cast2Node(*nodeSetIt);
3135 if ( theSurface.IsNull() ) { // smooth in 3D
3136 coord[0] += node->X();
3137 coord[1] += node->Y();
3138 coord[2] += node->Z();
3140 else { // smooth in 2D
3141 ASSERT( theUVMap.find( node ) != theUVMap.end() );
3142 gp_XY* uv = theUVMap[ node ];
3143 coord[0] += uv->X();
3144 coord[1] += uv->Y();
3147 int nbNodes = nodeSet.size();
3150 coord[0] /= nbNodes;
3151 coord[1] /= nbNodes;
3153 if ( !theSurface.IsNull() ) {
3154 ASSERT( theUVMap.find( theNode ) != theUVMap.end() );
3155 theUVMap[ theNode ]->SetCoord( coord[0], coord[1] );
3156 gp_Pnt p3d = theSurface->Value( coord[0], coord[1] );
3162 coord[2] /= nbNodes;
3166 const_cast< SMDS_MeshNode* >( theNode )->setXYZ(coord[0],coord[1],coord[2]);
3169 //=======================================================================
3170 //function : centroidalSmooth
3171 //purpose : pulls theNode toward the element-area-weighted centroid of the
3172 // surrounding elements
3173 //=======================================================================
3175 void centroidalSmooth(const SMDS_MeshNode* theNode,
3176 const Handle(Geom_Surface)& theSurface,
3177 map< const SMDS_MeshNode*, gp_XY* >& theUVMap)
3179 gp_XYZ aNewXYZ(0.,0.,0.);
3180 SMESH::Controls::Area anAreaFunc;
3181 double totalArea = 0.;
3186 SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(SMDSAbs_Face);
3187 while ( elemIt->more() )
3189 const SMDS_MeshElement* elem = elemIt->next();
3192 gp_XYZ elemCenter(0.,0.,0.);
3193 SMESH::Controls::TSequenceOfXYZ aNodePoints;
3194 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3195 int nn = elem->NbNodes();
3196 if(elem->IsQuadratic()) nn = nn/2;
3198 //while ( itN->more() ) {
3200 const SMDS_MeshNode* aNode = static_cast<const SMDS_MeshNode*>( itN->next() );
3202 gp_XYZ aP( aNode->X(), aNode->Y(), aNode->Z() );
3203 aNodePoints.push_back( aP );
3204 if ( !theSurface.IsNull() ) { // smooth in 2D
3205 ASSERT( theUVMap.find( aNode ) != theUVMap.end() );
3206 gp_XY* uv = theUVMap[ aNode ];
3207 aP.SetCoord( uv->X(), uv->Y(), 0. );
3211 double elemArea = anAreaFunc.GetValue( aNodePoints );
3212 totalArea += elemArea;
3214 aNewXYZ += elemCenter * elemArea;
3216 aNewXYZ /= totalArea;
3217 if ( !theSurface.IsNull() ) {
3218 theUVMap[ theNode ]->SetCoord( aNewXYZ.X(), aNewXYZ.Y() );
3219 aNewXYZ = theSurface->Value( aNewXYZ.X(), aNewXYZ.Y() ).XYZ();
3224 const_cast< SMDS_MeshNode* >( theNode )->setXYZ(aNewXYZ.X(),aNewXYZ.Y(),aNewXYZ.Z());
3227 //=======================================================================
3228 //function : getClosestUV
3229 //purpose : return UV of closest projection
3230 //=======================================================================
3232 static bool getClosestUV (Extrema_GenExtPS& projector,
3233 const gp_Pnt& point,
3236 projector.Perform( point );
3237 if ( projector.IsDone() ) {
3238 double u, v, minVal = DBL_MAX;
3239 for ( int i = projector.NbExt(); i > 0; i-- )
3240 #if OCC_VERSION_LARGE > 0x06040000 // Porting to OCCT6.5.1
3241 if ( projector.SquareDistance( i ) < minVal ) {
3242 minVal = projector.SquareDistance( i );
3244 if ( projector.Value( i ) < minVal ) {
3245 minVal = projector.Value( i );
3247 projector.Point( i ).Parameter( u, v );
3249 result.SetCoord( u, v );
3255 //=======================================================================
3257 //purpose : Smooth theElements during theNbIterations or until a worst
3258 // element has aspect ratio <= theTgtAspectRatio.
3259 // Aspect Ratio varies in range [1.0, inf].
3260 // If theElements is empty, the whole mesh is smoothed.
3261 // theFixedNodes contains additionally fixed nodes. Nodes built
3262 // on edges and boundary nodes are always fixed.
3263 //=======================================================================
3265 void SMESH_MeshEditor::Smooth (TIDSortedElemSet & theElems,
3266 set<const SMDS_MeshNode*> & theFixedNodes,
3267 const SmoothMethod theSmoothMethod,
3268 const int theNbIterations,
3269 double theTgtAspectRatio,
3272 myLastCreatedElems.Clear();
3273 myLastCreatedNodes.Clear();
3275 MESSAGE((theSmoothMethod==LAPLACIAN ? "LAPLACIAN" : "CENTROIDAL") << "--::Smooth()");
3277 if ( theTgtAspectRatio < 1.0 )
3278 theTgtAspectRatio = 1.0;
3280 const double disttol = 1.e-16;
3282 SMESH::Controls::AspectRatio aQualityFunc;
3284 SMESHDS_Mesh* aMesh = GetMeshDS();
3286 if ( theElems.empty() ) {
3287 // add all faces to theElems
3288 SMDS_FaceIteratorPtr fIt = aMesh->facesIterator();
3289 while ( fIt->more() ) {
3290 const SMDS_MeshElement* face = fIt->next();
3291 theElems.insert( theElems.end(), face );
3294 // get all face ids theElems are on
3295 set< int > faceIdSet;
3296 TIDSortedElemSet::iterator itElem;
3298 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
3299 int fId = FindShape( *itElem );
3300 // check that corresponding submesh exists and a shape is face
3302 faceIdSet.find( fId ) == faceIdSet.end() &&
3303 aMesh->MeshElements( fId )) {
3304 TopoDS_Shape F = aMesh->IndexToShape( fId );
3305 if ( !F.IsNull() && F.ShapeType() == TopAbs_FACE )
3306 faceIdSet.insert( fId );
3309 faceIdSet.insert( 0 ); // to smooth elements that are not on any TopoDS_Face
3311 // ===============================================
3312 // smooth elements on each TopoDS_Face separately
3313 // ===============================================
3315 set< int >::reverse_iterator fId = faceIdSet.rbegin(); // treate 0 fId at the end
3316 for ( ; fId != faceIdSet.rend(); ++fId ) {
3317 // get face surface and submesh
3318 Handle(Geom_Surface) surface;
3319 SMESHDS_SubMesh* faceSubMesh = 0;
3321 double fToler2 = 0, f,l;
3322 double u1 = 0, u2 = 0, v1 = 0, v2 = 0;
3323 bool isUPeriodic = false, isVPeriodic = false;
3325 face = TopoDS::Face( aMesh->IndexToShape( *fId ));
3326 surface = BRep_Tool::Surface( face );
3327 faceSubMesh = aMesh->MeshElements( *fId );
3328 fToler2 = BRep_Tool::Tolerance( face );
3329 fToler2 *= fToler2 * 10.;
3330 isUPeriodic = surface->IsUPeriodic();
3333 isVPeriodic = surface->IsVPeriodic();
3336 surface->Bounds( u1, u2, v1, v2 );
3338 // ---------------------------------------------------------
3339 // for elements on a face, find movable and fixed nodes and
3340 // compute UV for them
3341 // ---------------------------------------------------------
3342 bool checkBoundaryNodes = false;
3343 bool isQuadratic = false;
3344 set<const SMDS_MeshNode*> setMovableNodes;
3345 map< const SMDS_MeshNode*, gp_XY* > uvMap, uvMap2;
3346 list< gp_XY > listUV; // uvs the 2 uvMaps refer to
3347 list< const SMDS_MeshElement* > elemsOnFace;
3349 Extrema_GenExtPS projector;
3350 GeomAdaptor_Surface surfAdaptor;
3351 if ( !surface.IsNull() ) {
3352 surfAdaptor.Load( surface );
3353 projector.Initialize( surfAdaptor, 20,20, 1e-5,1e-5 );
3355 int nbElemOnFace = 0;
3356 itElem = theElems.begin();
3357 // loop on not yet smoothed elements: look for elems on a face
3358 while ( itElem != theElems.end() ) {
3359 if ( faceSubMesh && nbElemOnFace == faceSubMesh->NbElements() )
3360 break; // all elements found
3362 const SMDS_MeshElement* elem = *itElem;
3363 if ( !elem || elem->GetType() != SMDSAbs_Face || elem->NbNodes() < 3 ||
3364 ( faceSubMesh && !faceSubMesh->Contains( elem ))) {
3368 elemsOnFace.push_back( elem );
3369 theElems.erase( itElem++ );
3373 isQuadratic = elem->IsQuadratic();
3375 // get movable nodes of elem
3376 const SMDS_MeshNode* node;
3377 SMDS_TypeOfPosition posType;
3378 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
3379 int nn = 0, nbn = elem->NbNodes();
3380 if(elem->IsQuadratic())
3382 while ( nn++ < nbn ) {
3383 node = static_cast<const SMDS_MeshNode*>( itN->next() );
3384 const SMDS_PositionPtr& pos = node->GetPosition();
3385 posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
3386 if (posType != SMDS_TOP_EDGE &&
3387 posType != SMDS_TOP_VERTEX &&
3388 theFixedNodes.find( node ) == theFixedNodes.end())
3390 // check if all faces around the node are on faceSubMesh
3391 // because a node on edge may be bound to face
3392 SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
3394 if ( faceSubMesh ) {
3395 while ( eIt->more() && all ) {
3396 const SMDS_MeshElement* e = eIt->next();
3397 all = faceSubMesh->Contains( e );
3401 setMovableNodes.insert( node );
3403 checkBoundaryNodes = true;
3405 if ( posType == SMDS_TOP_3DSPACE )
3406 checkBoundaryNodes = true;
3409 if ( surface.IsNull() )
3412 // get nodes to check UV
3413 list< const SMDS_MeshNode* > uvCheckNodes;
3414 itN = elem->nodesIterator();
3415 nn = 0; nbn = elem->NbNodes();
3416 if(elem->IsQuadratic())
3418 while ( nn++ < nbn ) {
3419 node = static_cast<const SMDS_MeshNode*>( itN->next() );
3420 if ( uvMap.find( node ) == uvMap.end() )
3421 uvCheckNodes.push_back( node );
3422 // add nodes of elems sharing node
3423 // SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face);
3424 // while ( eIt->more() ) {
3425 // const SMDS_MeshElement* e = eIt->next();
3426 // if ( e != elem ) {
3427 // SMDS_ElemIteratorPtr nIt = e->nodesIterator();
3428 // while ( nIt->more() ) {
3429 // const SMDS_MeshNode* n =
3430 // static_cast<const SMDS_MeshNode*>( nIt->next() );
3431 // if ( uvMap.find( n ) == uvMap.end() )
3432 // uvCheckNodes.push_back( n );
3438 list< const SMDS_MeshNode* >::iterator n = uvCheckNodes.begin();
3439 for ( ; n != uvCheckNodes.end(); ++n ) {
3442 const SMDS_PositionPtr& pos = node->GetPosition();
3443 posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE;
3445 switch ( posType ) {
3446 case SMDS_TOP_FACE: {
3447 SMDS_FacePosition* fPos = ( SMDS_FacePosition* ) pos;
3448 uv.SetCoord( fPos->GetUParameter(), fPos->GetVParameter() );
3451 case SMDS_TOP_EDGE: {
3452 TopoDS_Shape S = aMesh->IndexToShape( node->getshapeId() );
3453 Handle(Geom2d_Curve) pcurve;
3454 if ( !S.IsNull() && S.ShapeType() == TopAbs_EDGE )
3455 pcurve = BRep_Tool::CurveOnSurface( TopoDS::Edge( S ), face, f,l );
3456 if ( !pcurve.IsNull() ) {
3457 double u = (( SMDS_EdgePosition* ) pos )->GetUParameter();
3458 uv = pcurve->Value( u ).XY();
3462 case SMDS_TOP_VERTEX: {
3463 TopoDS_Shape S = aMesh->IndexToShape( node->getshapeId() );
3464 if ( !S.IsNull() && S.ShapeType() == TopAbs_VERTEX )
3465 uv = BRep_Tool::Parameters( TopoDS::Vertex( S ), face ).XY();
3470 // check existing UV
3471 bool project = true;
3472 gp_Pnt pNode ( node->X(), node->Y(), node->Z() );
3473 double dist1 = DBL_MAX, dist2 = 0;
3474 if ( posType != SMDS_TOP_3DSPACE ) {
3475 dist1 = pNode.SquareDistance( surface->Value( uv.X(), uv.Y() ));
3476 project = dist1 > fToler2;
3478 if ( project ) { // compute new UV
3480 if ( !getClosestUV( projector, pNode, newUV )) {
3481 MESSAGE("Node Projection Failed " << node);
3485 newUV.SetX( ElCLib::InPeriod( newUV.X(), u1, u2 ));
3487 newUV.SetY( ElCLib::InPeriod( newUV.Y(), v1, v2 ));
3489 if ( posType != SMDS_TOP_3DSPACE )
3490 dist2 = pNode.SquareDistance( surface->Value( newUV.X(), newUV.Y() ));
3491 if ( dist2 < dist1 )
3495 // store UV in the map
3496 listUV.push_back( uv );
3497 uvMap.insert( make_pair( node, &listUV.back() ));
3499 } // loop on not yet smoothed elements
3501 if ( !faceSubMesh || nbElemOnFace != faceSubMesh->NbElements() )
3502 checkBoundaryNodes = true;
3504 // fix nodes on mesh boundary
3506 if ( checkBoundaryNodes ) {
3507 map< SMESH_TLink, int > linkNbMap; // how many times a link encounters in elemsOnFace
3508 map< SMESH_TLink, int >::iterator link_nb;
3509 // put all elements links to linkNbMap
3510 list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
3511 for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
3512 const SMDS_MeshElement* elem = (*elemIt);
3513 int nbn = elem->NbCornerNodes();
3514 // loop on elem links: insert them in linkNbMap
3515 for ( int iN = 0; iN < nbn; ++iN ) {
3516 const SMDS_MeshNode* n1 = elem->GetNode( iN );
3517 const SMDS_MeshNode* n2 = elem->GetNode(( iN+1 ) % nbn);
3518 SMESH_TLink link( n1, n2 );
3519 link_nb = linkNbMap.insert( make_pair( link, 0 )).first;
3523 // remove nodes that are in links encountered only once from setMovableNodes
3524 for ( link_nb = linkNbMap.begin(); link_nb != linkNbMap.end(); ++link_nb ) {
3525 if ( link_nb->second == 1 ) {
3526 setMovableNodes.erase( link_nb->first.node1() );
3527 setMovableNodes.erase( link_nb->first.node2() );
3532 // -----------------------------------------------------
3533 // for nodes on seam edge, compute one more UV ( uvMap2 );
3534 // find movable nodes linked to nodes on seam and which
3535 // are to be smoothed using the second UV ( uvMap2 )
3536 // -----------------------------------------------------
3538 set<const SMDS_MeshNode*> nodesNearSeam; // to smooth using uvMap2
3539 if ( !surface.IsNull() ) {
3540 TopExp_Explorer eExp( face, TopAbs_EDGE );
3541 for ( ; eExp.More(); eExp.Next() ) {
3542 TopoDS_Edge edge = TopoDS::Edge( eExp.Current() );
3543 if ( !BRep_Tool::IsClosed( edge, face ))
3545 SMESHDS_SubMesh* sm = aMesh->MeshElements( edge );
3546 if ( !sm ) continue;
3547 // find out which parameter varies for a node on seam
3550 Handle(Geom2d_Curve) pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
3551 if ( pcurve.IsNull() ) continue;
3552 uv1 = pcurve->Value( f );
3554 pcurve = BRep_Tool::CurveOnSurface( edge, face, f, l );
3555 if ( pcurve.IsNull() ) continue;
3556 uv2 = pcurve->Value( f );
3557 int iPar = Abs( uv1.X() - uv2.X() ) > Abs( uv1.Y() - uv2.Y() ) ? 1 : 2;
3559 if ( uv1.Coord( iPar ) > uv2.Coord( iPar )) {
3560 gp_Pnt2d tmp = uv1; uv1 = uv2; uv2 = tmp;
3562 // get nodes on seam and its vertices
3563 list< const SMDS_MeshNode* > seamNodes;
3564 SMDS_NodeIteratorPtr nSeamIt = sm->GetNodes();
3565 while ( nSeamIt->more() ) {
3566 const SMDS_MeshNode* node = nSeamIt->next();
3567 if ( !isQuadratic || !IsMedium( node ))
3568 seamNodes.push_back( node );
3570 TopExp_Explorer vExp( edge, TopAbs_VERTEX );
3571 for ( ; vExp.More(); vExp.Next() ) {
3572 sm = aMesh->MeshElements( vExp.Current() );
3574 nSeamIt = sm->GetNodes();
3575 while ( nSeamIt->more() )
3576 seamNodes.push_back( nSeamIt->next() );
3579 // loop on nodes on seam
3580 list< const SMDS_MeshNode* >::iterator noSeIt = seamNodes.begin();
3581 for ( ; noSeIt != seamNodes.end(); ++noSeIt ) {
3582 const SMDS_MeshNode* nSeam = *noSeIt;
3583 map< const SMDS_MeshNode*, gp_XY* >::iterator n_uv = uvMap.find( nSeam );
3584 if ( n_uv == uvMap.end() )
3587 n_uv->second->SetCoord( iPar, uv1.Coord( iPar ));
3588 // set the second UV
3589 listUV.push_back( *n_uv->second );
3590 listUV.back().SetCoord( iPar, uv2.Coord( iPar ));
3591 if ( uvMap2.empty() )
3592 uvMap2 = uvMap; // copy the uvMap contents
3593 uvMap2[ nSeam ] = &listUV.back();
3595 // collect movable nodes linked to ones on seam in nodesNearSeam
3596 SMDS_ElemIteratorPtr eIt = nSeam->GetInverseElementIterator(SMDSAbs_Face);
3597 while ( eIt->more() ) {
3598 const SMDS_MeshElement* e = eIt->next();
3599 int nbUseMap1 = 0, nbUseMap2 = 0;
3600 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
3601 int nn = 0, nbn = e->NbNodes();
3602 if(e->IsQuadratic()) nbn = nbn/2;
3603 while ( nn++ < nbn )
3605 const SMDS_MeshNode* n =
3606 static_cast<const SMDS_MeshNode*>( nIt->next() );
3608 setMovableNodes.find( n ) == setMovableNodes.end() )
3610 // add only nodes being closer to uv2 than to uv1
3611 gp_Pnt pMid (0.5 * ( n->X() + nSeam->X() ),
3612 0.5 * ( n->Y() + nSeam->Y() ),
3613 0.5 * ( n->Z() + nSeam->Z() ));
3615 getClosestUV( projector, pMid, uv );
3616 if ( uv.Coord( iPar ) > uvMap[ n ]->Coord( iPar ) ) {
3617 nodesNearSeam.insert( n );
3623 // for centroidalSmooth all element nodes must
3624 // be on one side of a seam
3625 if ( theSmoothMethod == CENTROIDAL && nbUseMap1 && nbUseMap2 ) {
3626 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
3628 while ( nn++ < nbn ) {
3629 const SMDS_MeshNode* n =
3630 static_cast<const SMDS_MeshNode*>( nIt->next() );
3631 setMovableNodes.erase( n );
3635 } // loop on nodes on seam
3636 } // loop on edge of a face
3637 } // if ( !face.IsNull() )
3639 if ( setMovableNodes.empty() ) {
3640 MESSAGE( "Face id : " << *fId << " - NO SMOOTHING: no nodes to move!!!");
3641 continue; // goto next face
3649 double maxRatio = -1., maxDisplacement = -1.;
3650 set<const SMDS_MeshNode*>::iterator nodeToMove;
3651 for ( it = 0; it < theNbIterations; it++ ) {
3652 maxDisplacement = 0.;
3653 nodeToMove = setMovableNodes.begin();
3654 for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
3655 const SMDS_MeshNode* node = (*nodeToMove);
3656 gp_XYZ aPrevPos ( node->X(), node->Y(), node->Z() );
3659 bool map2 = ( nodesNearSeam.find( node ) != nodesNearSeam.end() );
3660 if ( theSmoothMethod == LAPLACIAN )
3661 laplacianSmooth( node, surface, map2 ? uvMap2 : uvMap );
3663 centroidalSmooth( node, surface, map2 ? uvMap2 : uvMap );
3665 // node displacement
3666 gp_XYZ aNewPos ( node->X(), node->Y(), node->Z() );
3667 Standard_Real aDispl = (aPrevPos - aNewPos).SquareModulus();
3668 if ( aDispl > maxDisplacement )
3669 maxDisplacement = aDispl;
3671 // no node movement => exit
3672 //if ( maxDisplacement < 1.e-16 ) {
3673 if ( maxDisplacement < disttol ) {
3674 MESSAGE("-- no node movement --");
3678 // check elements quality
3680 list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
3681 for ( ; elemIt != elemsOnFace.end(); ++elemIt ) {
3682 const SMDS_MeshElement* elem = (*elemIt);
3683 if ( !elem || elem->GetType() != SMDSAbs_Face )
3685 SMESH::Controls::TSequenceOfXYZ aPoints;
3686 if ( aQualityFunc.GetPoints( elem, aPoints )) {
3687 double aValue = aQualityFunc.GetValue( aPoints );
3688 if ( aValue > maxRatio )
3692 if ( maxRatio <= theTgtAspectRatio ) {
3693 MESSAGE("-- quality achived --");
3696 if (it+1 == theNbIterations) {
3697 MESSAGE("-- Iteration limit exceeded --");
3699 } // smoothing iterations
3701 MESSAGE(" Face id: " << *fId <<
3702 " Nb iterstions: " << it <<
3703 " Displacement: " << maxDisplacement <<
3704 " Aspect Ratio " << maxRatio);
3706 // ---------------------------------------
3707 // new nodes positions are computed,
3708 // record movement in DS and set new UV
3709 // ---------------------------------------
3710 nodeToMove = setMovableNodes.begin();
3711 for ( ; nodeToMove != setMovableNodes.end(); nodeToMove++ ) {
3712 SMDS_MeshNode* node = const_cast< SMDS_MeshNode* > (*nodeToMove);
3713 aMesh->MoveNode( node, node->X(), node->Y(), node->Z() );
3714 map< const SMDS_MeshNode*, gp_XY* >::iterator node_uv = uvMap.find( node );
3715 if ( node_uv != uvMap.end() ) {
3716 gp_XY* uv = node_uv->second;
3718 ( SMDS_PositionPtr( new SMDS_FacePosition( uv->X(), uv->Y() )));
3722 // move medium nodes of quadratic elements
3725 SMESH_MesherHelper helper( *GetMesh() );
3726 helper.SetSubShape( face );
3727 vector<const SMDS_MeshNode*> nodes;
3729 list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin();
3730 for ( ; elemIt != elemsOnFace.end(); ++elemIt )
3732 const SMDS_MeshElement* QF = *elemIt;
3733 if ( QF->IsQuadratic() )
3735 nodes.assign( SMDS_MeshElement::iterator( QF->interlacedNodesElemIterator() ),
3736 SMDS_MeshElement::iterator() );
3737 nodes.push_back( nodes[0] );
3739 for (size_t i = 1; i < nodes.size(); i += 2 ) // i points to a medium node
3741 if ( !surface.IsNull() )
3743 gp_XY uv1 = helper.GetNodeUV( face, nodes[i-1], nodes[i+1], &checkUV );
3744 gp_XY uv2 = helper.GetNodeUV( face, nodes[i+1], nodes[i-1], &checkUV );
3745 gp_XY uv = helper.GetMiddleUV( surface, uv1, uv2 );
3746 xyz = surface->Value( uv.X(), uv.Y() );
3749 xyz = 0.5 * ( SMESH_TNodeXYZ( nodes[i-1] ) + SMESH_TNodeXYZ( nodes[i+1] ));
3751 if (( SMESH_TNodeXYZ( nodes[i] ) - xyz.XYZ() ).Modulus() > disttol )
3752 // we have to move a medium node
3753 aMesh->MoveNode( nodes[i], xyz.X(), xyz.Y(), xyz.Z() );
3759 } // loop on face ids
3763 //=======================================================================
3764 //function : isReverse
3765 //purpose : Return true if normal of prevNodes is not co-directied with
3766 // gp_Vec(prevNodes[iNotSame],nextNodes[iNotSame]).
3767 // iNotSame is where prevNodes and nextNodes are different.
3768 // If result is true then future volume orientation is OK
3769 //=======================================================================
3771 static bool isReverse(const SMDS_MeshElement* face,
3772 const vector<const SMDS_MeshNode*>& prevNodes,
3773 const vector<const SMDS_MeshNode*>& nextNodes,
3777 SMESH_TNodeXYZ pP = prevNodes[ iNotSame ];
3778 SMESH_TNodeXYZ pN = nextNodes[ iNotSame ];
3779 gp_XYZ extrDir( pN - pP ), faceNorm;
3780 SMESH_MeshAlgos::FaceNormal( face, faceNorm, /*normalized=*/false );
3782 return faceNorm * extrDir < 0.0;
3785 //=======================================================================
3787 * \brief Create elements by sweeping an element
3788 * \param elem - element to sweep
3789 * \param newNodesItVec - nodes generated from each node of the element
3790 * \param newElems - generated elements
3791 * \param nbSteps - number of sweeping steps
3792 * \param srcElements - to append elem for each generated element
3794 //=======================================================================
3796 void SMESH_MeshEditor::sweepElement(const SMDS_MeshElement* elem,
3797 const vector<TNodeOfNodeListMapItr> & newNodesItVec,
3798 list<const SMDS_MeshElement*>& newElems,
3800 SMESH_SequenceOfElemPtr& srcElements)
3802 //MESSAGE("sweepElement " << nbSteps);
3803 SMESHDS_Mesh* aMesh = GetMeshDS();
3805 const int nbNodes = elem->NbNodes();
3806 const int nbCorners = elem->NbCornerNodes();
3807 SMDSAbs_EntityType baseType = elem->GetEntityType(); /* it can change in case of
3808 polyhedron creation !!! */
3809 // Loop on elem nodes:
3810 // find new nodes and detect same nodes indices
3811 vector < list< const SMDS_MeshNode* >::const_iterator > itNN( nbNodes );
3812 vector<const SMDS_MeshNode*> prevNod( nbNodes );
3813 vector<const SMDS_MeshNode*> nextNod( nbNodes );
3814 vector<const SMDS_MeshNode*> midlNod( nbNodes );
3816 int iNode, nbSame = 0, nbDouble = 0, iNotSameNode = 0;
3817 vector<int> sames(nbNodes);
3818 vector<bool> isSingleNode(nbNodes);
3820 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
3821 TNodeOfNodeListMapItr nnIt = newNodesItVec[ iNode ];
3822 const SMDS_MeshNode* node = nnIt->first;
3823 const list< const SMDS_MeshNode* > & listNewNodes = nnIt->second;
3824 if ( listNewNodes.empty() )
3827 itNN [ iNode ] = listNewNodes.begin();
3828 prevNod[ iNode ] = node;
3829 nextNod[ iNode ] = listNewNodes.front();
3831 isSingleNode[iNode] = (listNewNodes.size()==nbSteps); /* medium node of quadratic or
3832 corner node of linear */
3833 if ( prevNod[ iNode ] != nextNod [ iNode ])
3834 nbDouble += !isSingleNode[iNode];
3836 if( iNode < nbCorners ) { // check corners only
3837 if ( prevNod[ iNode ] == nextNod [ iNode ])
3838 sames[nbSame++] = iNode;
3840 iNotSameNode = iNode;
3844 if ( nbSame == nbNodes || nbSame > 2) {
3845 MESSAGE( " Too many same nodes of element " << elem->GetID() );
3849 if ( elem->GetType() == SMDSAbs_Face && !isReverse( elem, prevNod, nextNod, iNotSameNode ))
3851 // fix nodes order to have bottom normal external
3852 if ( baseType == SMDSEntity_Polygon )
3854 std::reverse( itNN.begin(), itNN.end() );
3855 std::reverse( prevNod.begin(), prevNod.end() );
3856 std::reverse( midlNod.begin(), midlNod.end() );
3857 std::reverse( nextNod.begin(), nextNod.end() );
3858 std::reverse( isSingleNode.begin(), isSingleNode.end() );
3862 const vector<int>& ind = SMDS_MeshCell::reverseSmdsOrder( baseType );
3863 SMDS_MeshCell::applyInterlace( ind, itNN );
3864 SMDS_MeshCell::applyInterlace( ind, prevNod );
3865 SMDS_MeshCell::applyInterlace( ind, nextNod );
3866 SMDS_MeshCell::applyInterlace( ind, midlNod );
3867 SMDS_MeshCell::applyInterlace( ind, isSingleNode );
3870 sames[nbSame] = iNotSameNode;
3871 for ( int j = 0; j <= nbSame; ++j )
3872 for ( size_t i = 0; i < ind.size(); ++i )
3873 if ( ind[i] == sames[j] )
3878 iNotSameNode = sames[nbSame];
3883 int iSameNode = 0, iBeforeSame = 0, iAfterSame = 0, iOpposSame = 0;
3885 iSameNode = sames[ nbSame-1 ];
3886 iBeforeSame = ( iSameNode + nbCorners - 1 ) % nbCorners;
3887 iAfterSame = ( iSameNode + 1 ) % nbCorners;
3888 iOpposSame = ( iSameNode - 2 < 0 ? iSameNode + 2 : iSameNode - 2 );
3891 // make new elements
3892 for (int iStep = 0; iStep < nbSteps; iStep++ )
3895 for ( iNode = 0; iNode < nbNodes; iNode++ )
3897 midlNod[ iNode ] = isSingleNode[iNode] ? 0 : *itNN[ iNode ]++;
3898 nextNod[ iNode ] = *itNN[ iNode ]++;
3901 SMDS_MeshElement* aNewElem = 0;
3902 /*if(!elem->IsPoly())*/ {
3903 switch ( baseType ) {
3905 case SMDSEntity_Node: { // sweep NODE
3906 if ( nbSame == 0 ) {
3907 if ( isSingleNode[0] )
3908 aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ] );
3910 aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ], midlNod[ 0 ] );
3916 case SMDSEntity_Edge: { // sweep EDGE
3917 if ( nbDouble == 0 )
3919 if ( nbSame == 0 ) // ---> quadrangle
3920 aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
3921 nextNod[ 1 ], nextNod[ 0 ] );
3922 else // ---> triangle
3923 aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ],
3924 nextNod[ iNotSameNode ] );
3926 else // ---> polygon
3928 vector<const SMDS_MeshNode*> poly_nodes;
3929 poly_nodes.push_back( prevNod[0] );
3930 poly_nodes.push_back( prevNod[1] );
3931 if ( prevNod[1] != nextNod[1] )
3933 if ( midlNod[1]) poly_nodes.push_back( midlNod[1]);
3934 poly_nodes.push_back( nextNod[1] );
3936 if ( prevNod[0] != nextNod[0] )
3938 poly_nodes.push_back( nextNod[0] );
3939 if ( midlNod[0]) poly_nodes.push_back( midlNod[0]);
3941 switch ( poly_nodes.size() ) {
3943 aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ], poly_nodes[ 2 ]);
3946 aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ],
3947 poly_nodes[ 2 ], poly_nodes[ 3 ]);
3950 aNewElem = aMesh->AddPolygonalFace (poly_nodes);
3955 case SMDSEntity_Triangle: // TRIANGLE --->
3957 if ( nbDouble > 0 ) break;
3958 if ( nbSame == 0 ) // ---> pentahedron
3959 aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
3960 nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ] );
3962 else if ( nbSame == 1 ) // ---> pyramid
3963 aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
3964 nextNod[ iAfterSame ], nextNod[ iBeforeSame ],
3965 nextNod[ iSameNode ]);
3967 else // 2 same nodes: ---> tetrahedron
3968 aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ],
3969 nextNod[ iNotSameNode ]);
3972 case SMDSEntity_Quad_Edge: // sweep quadratic EDGE --->
3976 if ( nbDouble+nbSame == 2 )
3978 if(nbSame==0) { // ---> quadratic quadrangle
3979 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
3980 prevNod[2], midlNod[1], nextNod[2], midlNod[0]);
3982 else { //(nbSame==1) // ---> quadratic triangle
3984 return; // medium node on axis
3986 else if(sames[0]==0)
3987 aNewElem = aMesh->AddFace(prevNod[0], nextNod[1], prevNod[1],
3988 nextNod[2], midlNod[1], prevNod[2]);
3990 aNewElem = aMesh->AddFace(prevNod[0], nextNod[0], prevNod[1],
3991 midlNod[0], nextNod[2], prevNod[2]);
3994 else if ( nbDouble == 3 )
3996 if ( nbSame == 0 ) { // ---> bi-quadratic quadrangle
3997 aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0],
3998 prevNod[2], midlNod[1], nextNod[2], midlNod[0], midlNod[2]);
4005 case SMDSEntity_Quadrangle: { // sweep QUADRANGLE --->
4006 if ( nbDouble > 0 ) break;
4008 if ( nbSame == 0 ) // ---> hexahedron
4009 aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ], prevNod[ 3 ],
4010 nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ], nextNod[ 3 ]);
4012 else if ( nbSame == 1 ) { // ---> pyramid + pentahedron
4013 aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ],
4014 nextNod[ iAfterSame ], nextNod[ iBeforeSame ],
4015 nextNod[ iSameNode ]);
4016 newElems.push_back( aNewElem );
4017 aNewElem = aMesh->AddVolume (prevNod[ iAfterSame ], prevNod[ iOpposSame ],
4018 prevNod[ iBeforeSame ], nextNod[ iAfterSame ],
4019 nextNod[ iOpposSame ], nextNod[ iBeforeSame ] );
4021 else if ( nbSame == 2 ) { // ---> pentahedron
4022 if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] )
4023 // iBeforeSame is same too
4024 aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iOpposSame ],
4025 nextNod[ iOpposSame ], prevNod[ iSameNode ],
4026 prevNod[ iAfterSame ], nextNod[ iAfterSame ]);
4028 // iAfterSame is same too
4029 aNewElem = aMesh->AddVolume (prevNod[ iSameNode ], prevNod[ iBeforeSame ],
4030 nextNod[ iBeforeSame ], prevNod[ iAfterSame ],
4031 prevNod[ iOpposSame ], nextNod[ iOpposSame ]);
4035 case SMDSEntity_Quad_Triangle: // sweep (Bi)Quadratic TRIANGLE --->
4036 case SMDSEntity_BiQuad_Triangle: /* ??? */ {
4037 if ( nbDouble+nbSame != 3 ) break;
4039 // ---> pentahedron with 15 nodes
4040 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
4041 nextNod[0], nextNod[1], nextNod[2],
4042 prevNod[3], prevNod[4], prevNod[5],
4043 nextNod[3], nextNod[4], nextNod[5],
4044 midlNod[0], midlNod[1], midlNod[2]);
4046 else if(nbSame==1) {
4047 // ---> 2d order pyramid of 13 nodes
4048 int apex = iSameNode;
4049 int i0 = ( apex + 1 ) % nbCorners;
4050 int i1 = ( apex - 1 + nbCorners ) % nbCorners;
4054 aNewElem = aMesh->AddVolume(prevNod[i1], prevNod[i0],
4055 nextNod[i0], nextNod[i1], prevNod[apex],
4056 prevNod[i01], midlNod[i0],
4057 nextNod[i01], midlNod[i1],
4058 prevNod[i1a], prevNod[i0a],
4059 nextNod[i0a], nextNod[i1a]);
4061 else if(nbSame==2) {
4062 // ---> 2d order tetrahedron of 10 nodes
4063 int n1 = iNotSameNode;
4064 int n2 = ( n1 + 1 ) % nbCorners;
4065 int n3 = ( n1 + nbCorners - 1 ) % nbCorners;
4069 aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], prevNod[n3], nextNod[n1],
4070 prevNod[n12], prevNod[n23], prevNod[n31],
4071 midlNod[n1], nextNod[n12], nextNod[n31]);
4075 case SMDSEntity_Quad_Quadrangle: { // sweep Quadratic QUADRANGLE --->
4077 if ( nbDouble != 4 ) break;
4078 // ---> hexahedron with 20 nodes
4079 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
4080 nextNod[0], nextNod[1], nextNod[2], nextNod[3],
4081 prevNod[4], prevNod[5], prevNod[6], prevNod[7],
4082 nextNod[4], nextNod[5], nextNod[6], nextNod[7],
4083 midlNod[0], midlNod[1], midlNod[2], midlNod[3]);
4085 else if(nbSame==1) {
4086 // ---> pyramid + pentahedron - can not be created since it is needed
4087 // additional middle node at the center of face
4088 INFOS( " Sweep for face " << elem->GetID() << " can not be created" );
4091 else if( nbSame == 2 ) {
4092 if ( nbDouble != 2 ) break;
4093 // ---> 2d order Pentahedron with 15 nodes
4095 if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] ) {
4096 // iBeforeSame is same too
4103 // iAfterSame is same too
4113 aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], nextNod[n2],
4114 prevNod[n4], prevNod[n5], nextNod[n5],
4115 prevNod[n12], midlNod[n2], nextNod[n12],
4116 prevNod[n45], midlNod[n5], nextNod[n45],
4117 prevNod[n14], prevNod[n25], nextNod[n25]);
4121 case SMDSEntity_BiQuad_Quadrangle: { // sweep BiQuadratic QUADRANGLE --->
4123 if( nbSame == 0 && nbDouble == 9 ) {
4124 // ---> tri-quadratic hexahedron with 27 nodes
4125 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3],
4126 nextNod[0], nextNod[1], nextNod[2], nextNod[3],
4127 prevNod[4], prevNod[5], prevNod[6], prevNod[7],
4128 nextNod[4], nextNod[5], nextNod[6], nextNod[7],
4129 midlNod[0], midlNod[1], midlNod[2], midlNod[3],
4130 prevNod[8], // bottom center
4131 midlNod[4], midlNod[5], midlNod[6], midlNod[7],
4132 nextNod[8], // top center
4133 midlNod[8]);// elem center
4141 case SMDSEntity_Polygon: { // sweep POLYGON
4143 if ( nbNodes == 6 && nbSame == 0 && nbDouble == 0 ) {
4144 // ---> hexagonal prism
4145 aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2],
4146 prevNod[3], prevNod[4], prevNod[5],
4147 nextNod[0], nextNod[1], nextNod[2],
4148 nextNod[3], nextNod[4], nextNod[5]);
4152 case SMDSEntity_Ball:
4160 if ( !aNewElem && elem->GetType() == SMDSAbs_Face ) // try to create a polyherdal prism
4162 if ( baseType != SMDSEntity_Polygon )
4164 const std::vector<int>& ind = SMDS_MeshCell::interlacedSmdsOrder(baseType);
4165 SMDS_MeshCell::applyInterlace( ind, prevNod );
4166 SMDS_MeshCell::applyInterlace( ind, nextNod );
4167 SMDS_MeshCell::applyInterlace( ind, midlNod );
4168 SMDS_MeshCell::applyInterlace( ind, itNN );
4169 SMDS_MeshCell::applyInterlace( ind, isSingleNode );
4170 baseType = SMDSEntity_Polygon; // WARNING: change baseType !!!!
4172 vector<const SMDS_MeshNode*> polyedre_nodes (nbNodes*2 + 4*nbNodes);
4173 vector<int> quantities (nbNodes + 2);
4174 polyedre_nodes.clear();
4178 for (int inode = 0; inode < nbNodes; inode++)
4179 polyedre_nodes.push_back( prevNod[inode] );
4180 quantities.push_back( nbNodes );
4183 polyedre_nodes.push_back( nextNod[0] );
4184 for (int inode = nbNodes; inode-1; --inode )
4185 polyedre_nodes.push_back( nextNod[inode-1] );
4186 quantities.push_back( nbNodes );
4189 for (int iface = 0; iface < nbNodes; iface++)
4191 const int prevNbNodes = polyedre_nodes.size();
4192 int inextface = (iface+1) % nbNodes;
4193 polyedre_nodes.push_back( prevNod[inextface] );
4194 polyedre_nodes.push_back( prevNod[iface] );
4195 if ( prevNod[iface] != nextNod[iface] )
4197 if ( midlNod[ iface ]) polyedre_nodes.push_back( midlNod[ iface ]);
4198 polyedre_nodes.push_back( nextNod[iface] );
4200 if ( prevNod[inextface] != nextNod[inextface] )
4202 polyedre_nodes.push_back( nextNod[inextface] );
4203 if ( midlNod[ inextface ]) polyedre_nodes.push_back( midlNod[ inextface ]);
4205 const int nbFaceNodes = polyedre_nodes.size() - prevNbNodes;
4206 if ( nbFaceNodes > 2 )
4207 quantities.push_back( nbFaceNodes );
4208 else // degenerated face
4209 polyedre_nodes.resize( prevNbNodes );
4211 aNewElem = aMesh->AddPolyhedralVolume (polyedre_nodes, quantities);
4215 newElems.push_back( aNewElem );
4216 myLastCreatedElems.Append(aNewElem);
4217 srcElements.Append( elem );
4220 // set new prev nodes
4221 for ( iNode = 0; iNode < nbNodes; iNode++ )
4222 prevNod[ iNode ] = nextNod[ iNode ];
4227 //=======================================================================
4229 * \brief Create 1D and 2D elements around swept elements
4230 * \param mapNewNodes - source nodes and ones generated from them
4231 * \param newElemsMap - source elements and ones generated from them
4232 * \param elemNewNodesMap - nodes generated from each node of each element
4233 * \param elemSet - all swept elements
4234 * \param nbSteps - number of sweeping steps
4235 * \param srcElements - to append elem for each generated element
4237 //=======================================================================
4239 void SMESH_MeshEditor::makeWalls (TNodeOfNodeListMap & mapNewNodes,
4240 TElemOfElemListMap & newElemsMap,
4241 TElemOfVecOfNnlmiMap & elemNewNodesMap,
4242 TIDSortedElemSet& elemSet,
4244 SMESH_SequenceOfElemPtr& srcElements)
4246 ASSERT( newElemsMap.size() == elemNewNodesMap.size() );
4247 SMESHDS_Mesh* aMesh = GetMeshDS();
4249 // Find nodes belonging to only one initial element - sweep them into edges.
4251 TNodeOfNodeListMapItr nList = mapNewNodes.begin();
4252 for ( ; nList != mapNewNodes.end(); nList++ )
4254 const SMDS_MeshNode* node =
4255 static_cast<const SMDS_MeshNode*>( nList->first );
4256 if ( newElemsMap.count( node ))
4257 continue; // node was extruded into edge
4258 SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator();
4259 int nbInitElems = 0;
4260 const SMDS_MeshElement* el = 0;
4261 SMDSAbs_ElementType highType = SMDSAbs_Edge; // count most complex elements only
4262 while ( eIt->more() && nbInitElems < 2 ) {
4264 SMDSAbs_ElementType type = el->GetType();
4265 if ( type == SMDSAbs_Volume || type < highType ) continue;
4266 if ( type > highType ) {
4270 nbInitElems += elemSet.count(el);
4272 if ( nbInitElems < 2 ) {
4273 bool NotCreateEdge = el && el->IsMediumNode(node);
4274 if(!NotCreateEdge) {
4275 vector<TNodeOfNodeListMapItr> newNodesItVec( 1, nList );
4276 list<const SMDS_MeshElement*> newEdges;
4277 sweepElement( node, newNodesItVec, newEdges, nbSteps, srcElements );
4282 // Make a ceiling for each element ie an equal element of last new nodes.
4283 // Find free links of faces - make edges and sweep them into faces.
4285 TElemOfElemListMap::iterator itElem = newElemsMap.begin();
4286 TElemOfVecOfNnlmiMap::iterator itElemNodes = elemNewNodesMap.begin();
4287 for ( ; itElem != newElemsMap.end(); itElem++, itElemNodes++ )
4289 const SMDS_MeshElement* elem = itElem->first;
4290 vector<TNodeOfNodeListMapItr>& vecNewNodes = itElemNodes->second;
4292 if(itElem->second.size()==0) continue;
4294 const bool isQuadratic = elem->IsQuadratic();
4296 if ( elem->GetType() == SMDSAbs_Edge ) {
4297 // create a ceiling edge
4298 if ( !isQuadratic ) {
4299 if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
4300 vecNewNodes[ 1 ]->second.back())) {
4301 myLastCreatedElems.Append(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
4302 vecNewNodes[ 1 ]->second.back()));
4303 srcElements.Append( elem );
4307 if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(),
4308 vecNewNodes[ 1 ]->second.back(),
4309 vecNewNodes[ 2 ]->second.back())) {
4310 myLastCreatedElems.Append(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(),
4311 vecNewNodes[ 1 ]->second.back(),
4312 vecNewNodes[ 2 ]->second.back()));
4313 srcElements.Append( elem );
4317 if ( elem->GetType() != SMDSAbs_Face )
4320 bool hasFreeLinks = false;
4322 TIDSortedElemSet avoidSet;
4323 avoidSet.insert( elem );
4325 set<const SMDS_MeshNode*> aFaceLastNodes;
4326 int iNode, nbNodes = vecNewNodes.size();
4327 if ( !isQuadratic ) {
4328 // loop on the face nodes
4329 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4330 aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4331 // look for free links of the face
4332 int iNext = ( iNode + 1 == nbNodes ) ? 0 : iNode + 1;
4333 const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
4334 const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
4335 // check if a link n1-n2 is free
4336 if ( ! SMESH_MeshAlgos::FindFaceInSet ( n1, n2, elemSet, avoidSet )) {
4337 hasFreeLinks = true;
4338 // make a new edge and a ceiling for a new edge
4339 const SMDS_MeshElement* edge;
4340 if ( ! ( edge = aMesh->FindEdge( n1, n2 ))) {
4341 myLastCreatedElems.Append( edge = aMesh->AddEdge( n1, n2 )); // free link edge
4342 srcElements.Append( myLastCreatedElems.Last() );
4344 n1 = vecNewNodes[ iNode ]->second.back();
4345 n2 = vecNewNodes[ iNext ]->second.back();
4346 if ( !aMesh->FindEdge( n1, n2 )) {
4347 myLastCreatedElems.Append(aMesh->AddEdge( n1, n2 )); // new edge ceiling
4348 srcElements.Append( edge );
4353 else { // elem is quadratic face
4354 int nbn = nbNodes/2;
4355 for ( iNode = 0; iNode < nbn; iNode++ ) {
4356 aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4357 int iNext = ( iNode + 1 == nbn ) ? 0 : iNode + 1;
4358 const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first;
4359 const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first;
4360 const SMDS_MeshNode* n3 = vecNewNodes[ iNode+nbn ]->first;
4361 // check if a link is free
4362 if ( ! SMESH_MeshAlgos::FindFaceInSet ( n1, n2, elemSet, avoidSet ) &&
4363 ! SMESH_MeshAlgos::FindFaceInSet ( n1, n3, elemSet, avoidSet ) &&
4364 ! SMESH_MeshAlgos::FindFaceInSet ( n3, n2, elemSet, avoidSet ) ) {
4365 hasFreeLinks = true;
4366 // make an edge and a ceiling for a new edge
4368 if ( !aMesh->FindEdge( n1, n2, n3 )) {
4369 myLastCreatedElems.Append(aMesh->AddEdge( n1, n2, n3 )); // free link edge
4370 srcElements.Append( elem );
4372 n1 = vecNewNodes[ iNode ]->second.back();
4373 n2 = vecNewNodes[ iNext ]->second.back();
4374 n3 = vecNewNodes[ iNode+nbn ]->second.back();
4375 if ( !aMesh->FindEdge( n1, n2, n3 )) {
4376 myLastCreatedElems.Append(aMesh->AddEdge( n1, n2, n3 )); // ceiling edge
4377 srcElements.Append( elem );
4381 for ( iNode = nbn; iNode < nbNodes; iNode++ ) {
4382 aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() );
4386 // sweep free links into faces
4388 if ( hasFreeLinks ) {
4389 list<const SMDS_MeshElement*> & newVolumes = itElem->second;
4390 int iVol, volNb, nbVolumesByStep = newVolumes.size() / nbSteps;
4392 set<const SMDS_MeshNode*> initNodeSet, topNodeSet, faceNodeSet;
4393 set<const SMDS_MeshNode*> initNodeSetNoCenter/*, topNodeSetNoCenter*/;
4394 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
4395 initNodeSet.insert( vecNewNodes[ iNode ]->first );
4396 topNodeSet .insert( vecNewNodes[ iNode ]->second.back() );
4398 if ( isQuadratic && nbNodes % 2 ) { // node set for the case of a biquadratic
4399 initNodeSetNoCenter = initNodeSet; // swept face and a not biquadratic volume
4400 initNodeSetNoCenter.erase( vecNewNodes.back()->first );
4402 for ( volNb = 0; volNb < nbVolumesByStep; volNb++ ) {
4403 list<const SMDS_MeshElement*>::iterator v = newVolumes.begin();
4404 std::advance( v, volNb );
4405 // find indices of free faces of a volume and their source edges
4406 list< int > freeInd;
4407 list< const SMDS_MeshElement* > srcEdges; // source edges of free faces
4408 SMDS_VolumeTool vTool( *v, /*ignoreCentralNodes=*/false );
4409 int iF, nbF = vTool.NbFaces();
4410 for ( iF = 0; iF < nbF; iF ++ ) {
4411 if (vTool.IsFreeFace( iF ) &&
4412 vTool.GetFaceNodes( iF, faceNodeSet ) &&
4413 initNodeSet != faceNodeSet) // except an initial face
4415 if ( nbSteps == 1 && faceNodeSet == topNodeSet )
4417 if ( faceNodeSet == initNodeSetNoCenter )
4419 freeInd.push_back( iF );
4420 // find source edge of a free face iF
4421 vector<const SMDS_MeshNode*> commonNodes; // shared by the initial and free faces
4422 commonNodes.resize( initNodeSet.size(), NULL ); // avoid spoiling memory
4423 std::set_intersection( faceNodeSet.begin(), faceNodeSet.end(),
4424 initNodeSet.begin(), initNodeSet.end(),
4425 commonNodes.begin());
4426 if ( (*v)->IsQuadratic() )
4427 srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1],commonNodes[2]));
4429 srcEdges.push_back(aMesh->FindEdge (commonNodes[0],commonNodes[1]));
4431 if ( !srcEdges.back() )
4433 cout << "SMESH_MeshEditor::makeWalls(), no source edge found for a free face #"
4434 << iF << " of volume #" << vTool.ID() << endl;
4439 if ( freeInd.empty() )
4442 // create faces for all steps;
4443 // if such a face has been already created by sweep of edge,
4444 // assure that its orientation is OK
4445 for ( int iStep = 0; iStep < nbSteps; iStep++ ) {
4446 vTool.Set( *v, /*ignoreCentralNodes=*/false );
4447 vTool.SetExternalNormal();
4448 const int nextShift = vTool.IsForward() ? +1 : -1;
4449 list< int >::iterator ind = freeInd.begin();
4450 list< const SMDS_MeshElement* >::iterator srcEdge = srcEdges.begin();
4451 for ( ; ind != freeInd.end(); ++ind, ++srcEdge ) // loop on free faces
4453 const SMDS_MeshNode** nodes = vTool.GetFaceNodes( *ind );
4454 int nbn = vTool.NbFaceNodes( *ind );
4455 const SMDS_MeshElement * f = 0;
4456 if ( nbn == 3 ) ///// triangle
4458 f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ]);
4460 nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
4462 const SMDS_MeshNode* newOrder[3] = { nodes[ 1 - nextShift ],
4464 nodes[ 1 + nextShift ] };
4466 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4468 myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
4472 else if ( nbn == 4 ) ///// quadrangle
4474 f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ], nodes[ 3 ]);
4476 nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift ))
4478 const SMDS_MeshNode* newOrder[4] = { nodes[ 0 ], nodes[ 2-nextShift ],
4479 nodes[ 2 ], nodes[ 2+nextShift ] };
4481 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4483 myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ],
4484 newOrder[ 2 ], newOrder[ 3 ]));
4487 else if ( nbn == 6 && isQuadratic ) /////// quadratic triangle
4489 f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[1], nodes[3], nodes[5] );
4491 nodes[2] != f->GetNodeWrap( f->GetNodeIndex( nodes[0] ) + 2*nextShift ))
4493 const SMDS_MeshNode* newOrder[6] = { nodes[2 - 2*nextShift],
4495 nodes[2 + 2*nextShift],
4496 nodes[3 - 2*nextShift],
4498 nodes[3 + 2*nextShift]};
4500 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4502 myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ],
4510 else if ( nbn == 8 && isQuadratic ) /////// quadratic quadrangle
4512 f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[6],
4513 nodes[1], nodes[3], nodes[5], nodes[7] );
4515 nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
4517 const SMDS_MeshNode* newOrder[8] = { nodes[0],
4518 nodes[4 - 2*nextShift],
4520 nodes[4 + 2*nextShift],
4522 nodes[5 - 2*nextShift],
4524 nodes[5 + 2*nextShift] };
4526 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4528 myLastCreatedElems.Append(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
4529 newOrder[ 2 ], newOrder[ 3 ],
4530 newOrder[ 4 ], newOrder[ 5 ],
4531 newOrder[ 6 ], newOrder[ 7 ]));
4534 else if ( nbn == 9 && isQuadratic ) /////// bi-quadratic quadrangle
4536 f = aMesh->FindElement( vector<const SMDS_MeshNode*>( nodes, nodes+nbn ),
4537 SMDSAbs_Face, /*noMedium=*/false);
4539 nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift ))
4541 const SMDS_MeshNode* newOrder[9] = { nodes[0],
4542 nodes[4 - 2*nextShift],
4544 nodes[4 + 2*nextShift],
4546 nodes[5 - 2*nextShift],
4548 nodes[5 + 2*nextShift],
4551 aMesh->ChangeElementNodes( f, &newOrder[0], nbn );
4553 myLastCreatedElems.Append(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ],
4554 newOrder[ 2 ], newOrder[ 3 ],
4555 newOrder[ 4 ], newOrder[ 5 ],
4556 newOrder[ 6 ], newOrder[ 7 ],
4560 else //////// polygon
4562 vector<const SMDS_MeshNode*> polygon_nodes ( nodes, nodes+nbn );
4563 const SMDS_MeshFace * f = aMesh->FindFace( polygon_nodes );
4565 nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + nextShift ))
4567 if ( !vTool.IsForward() )
4568 std::reverse( polygon_nodes.begin(), polygon_nodes.end());
4570 aMesh->ChangeElementNodes( f, &polygon_nodes[0], nbn );
4572 AddElement(polygon_nodes, SMDSAbs_Face, polygon_nodes.size()>4);
4576 while ( srcElements.Length() < myLastCreatedElems.Length() )
4577 srcElements.Append( *srcEdge );
4579 } // loop on free faces
4581 // go to the next volume
4583 while ( iVol++ < nbVolumesByStep ) v++;
4586 } // loop on volumes of one step
4587 } // sweep free links into faces
4589 // Make a ceiling face with a normal external to a volume
4591 // use SMDS_VolumeTool to get a correctly ordered nodes of a ceiling face
4592 SMDS_VolumeTool lastVol( itElem->second.back(), /*ignoreCentralNodes=*/false );
4593 int iF = lastVol.GetFaceIndex( aFaceLastNodes );
4595 if ( iF < 0 && isQuadratic && nbNodes % 2 ) { // remove a central node of biquadratic
4596 aFaceLastNodes.erase( vecNewNodes.back()->second.back() );
4597 iF = lastVol.GetFaceIndex( aFaceLastNodes );
4600 lastVol.SetExternalNormal();
4601 const SMDS_MeshNode** nodes = lastVol.GetFaceNodes( iF );
4602 int nbn = lastVol.NbFaceNodes( iF );
4603 // we do not use this->AddElement() because nodes are interlaced
4604 vector<const SMDS_MeshNode*> nodeVec( nodes, nodes+nbn );
4605 if ( !hasFreeLinks ||
4606 !aMesh->FindElement( nodeVec, SMDSAbs_Face, /*noMedium=*/false) )
4609 myLastCreatedElems.Append(aMesh->AddFace( nodes[0], nodes[1], nodes[2] ));
4611 else if ( nbn == 4 )
4612 myLastCreatedElems.Append(aMesh->AddFace( nodes[0], nodes[1], nodes[2], nodes[3]));
4614 else if ( nbn == 6 && isQuadratic )
4615 myLastCreatedElems.Append(aMesh->AddFace( nodes[0], nodes[2], nodes[4],
4616 nodes[1], nodes[3], nodes[5]));
4617 else if ( nbn == 7 && isQuadratic )
4618 myLastCreatedElems.Append(aMesh->AddFace( nodes[0], nodes[2], nodes[4],
4619 nodes[1], nodes[3], nodes[5], nodes[6]));
4620 else if ( nbn == 8 && isQuadratic )
4621 myLastCreatedElems.Append(aMesh->AddFace( nodes[0], nodes[2], nodes[4], nodes[6],
4622 nodes[1], nodes[3], nodes[5], nodes[7]));
4623 else if ( nbn == 9 && isQuadratic )
4624 myLastCreatedElems.Append(aMesh->AddFace( nodes[0], nodes[2], nodes[4], nodes[6],
4625 nodes[1], nodes[3], nodes[5], nodes[7],
4628 myLastCreatedElems.Append(aMesh->AddPolygonalFace( nodeVec ));
4630 while ( srcElements.Length() < myLastCreatedElems.Length() )
4631 srcElements.Append( elem );
4634 } // loop on swept elements
4637 //=======================================================================
4638 //function : RotationSweep
4640 //=======================================================================
4642 SMESH_MeshEditor::PGroupIDs
4643 SMESH_MeshEditor::RotationSweep(TIDSortedElemSet & theElems,
4644 const gp_Ax1& theAxis,
4645 const double theAngle,
4646 const int theNbSteps,
4647 const double theTol,
4648 const bool theMakeGroups,
4649 const bool theMakeWalls)
4651 myLastCreatedElems.Clear();
4652 myLastCreatedNodes.Clear();
4654 // source elements for each generated one
4655 SMESH_SequenceOfElemPtr srcElems, srcNodes;
4657 MESSAGE( "RotationSweep()");
4659 aTrsf.SetRotation( theAxis, theAngle );
4661 aTrsf2.SetRotation( theAxis, theAngle/2. );
4663 gp_Lin aLine( theAxis );
4664 double aSqTol = theTol * theTol;
4666 SMESHDS_Mesh* aMesh = GetMeshDS();
4668 TNodeOfNodeListMap mapNewNodes;
4669 TElemOfVecOfNnlmiMap mapElemNewNodes;
4670 TElemOfElemListMap newElemsMap;
4672 const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
4673 myMesh->NbFaces(ORDER_QUADRATIC) +
4674 myMesh->NbVolumes(ORDER_QUADRATIC) );
4676 TIDSortedElemSet::iterator itElem;
4677 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
4678 const SMDS_MeshElement* elem = *itElem;
4679 if ( !elem || elem->GetType() == SMDSAbs_Volume )
4681 vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
4682 newNodesItVec.reserve( elem->NbNodes() );
4684 // loop on elem nodes
4685 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
4686 while ( itN->more() )
4688 // check if a node has been already sweeped
4689 const SMDS_MeshNode* node = cast2Node( itN->next() );
4691 gp_XYZ aXYZ( node->X(), node->Y(), node->Z() );
4693 aXYZ.Coord( coord[0], coord[1], coord[2] );
4694 bool isOnAxis = ( aLine.SquareDistance( aXYZ ) <= aSqTol );
4696 TNodeOfNodeListMapItr nIt =
4697 mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
4698 list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
4699 if ( listNewNodes.empty() )
4701 // check if we are to create medium nodes between corner ones
4702 bool needMediumNodes = false;
4703 if ( isQuadraticMesh )
4705 SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
4706 while (it->more() && !needMediumNodes )
4708 const SMDS_MeshElement* invElem = it->next();
4709 if ( invElem != elem && !theElems.count( invElem )) continue;
4710 needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
4711 if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
4712 needMediumNodes = true;
4717 const SMDS_MeshNode * newNode = node;
4718 for ( int i = 0; i < theNbSteps; i++ ) {
4720 if ( needMediumNodes ) // create a medium node
4722 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
4723 newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
4724 myLastCreatedNodes.Append(newNode);
4725 srcNodes.Append( node );
4726 listNewNodes.push_back( newNode );
4727 aTrsf2.Transforms( coord[0], coord[1], coord[2] );
4730 aTrsf.Transforms( coord[0], coord[1], coord[2] );
4732 // create a corner node
4733 newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
4734 myLastCreatedNodes.Append(newNode);
4735 srcNodes.Append( node );
4736 listNewNodes.push_back( newNode );
4739 listNewNodes.push_back( newNode );
4740 // if ( needMediumNodes )
4741 // listNewNodes.push_back( newNode );
4745 newNodesItVec.push_back( nIt );
4747 // make new elements
4748 sweepElement( elem, newNodesItVec, newElemsMap[elem], theNbSteps, srcElems );
4752 makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElems, theNbSteps, srcElems );
4754 PGroupIDs newGroupIDs;
4755 if ( theMakeGroups )
4756 newGroupIDs = generateGroups( srcNodes, srcElems, "rotated");
4762 //=======================================================================
4763 //function : CreateNode
4765 //=======================================================================
4766 const SMDS_MeshNode* SMESH_MeshEditor::CreateNode(const double x,
4769 const double tolnode,
4770 SMESH_SequenceOfNode& aNodes)
4772 // myLastCreatedElems.Clear();
4773 // myLastCreatedNodes.Clear();
4776 SMESHDS_Mesh * aMesh = myMesh->GetMeshDS();
4778 // try to search in sequence of existing nodes
4779 // if aNodes.Length()>0 we 'nave to use given sequence
4780 // else - use all nodes of mesh
4781 if(aNodes.Length()>0) {
4783 for(i=1; i<=aNodes.Length(); i++) {
4784 gp_Pnt P2(aNodes.Value(i)->X(),aNodes.Value(i)->Y(),aNodes.Value(i)->Z());
4785 if(P1.Distance(P2)<tolnode)
4786 return aNodes.Value(i);
4790 SMDS_NodeIteratorPtr itn = aMesh->nodesIterator();
4791 while(itn->more()) {
4792 const SMDS_MeshNode* aN = static_cast<const SMDS_MeshNode*> (itn->next());
4793 gp_Pnt P2(aN->X(),aN->Y(),aN->Z());
4794 if(P1.Distance(P2)<tolnode)
4799 // create new node and return it
4800 const SMDS_MeshNode* NewNode = aMesh->AddNode(x,y,z);
4801 //myLastCreatedNodes.Append(NewNode);
4806 //=======================================================================
4807 //function : ExtrusionSweep
4809 //=======================================================================
4811 SMESH_MeshEditor::PGroupIDs
4812 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet & theElems,
4813 const gp_Vec& theStep,
4814 const int theNbSteps,
4815 TElemOfElemListMap& newElemsMap,
4816 const bool theMakeGroups,
4818 const double theTolerance)
4820 ExtrusParam aParams;
4821 aParams.myDir = gp_Dir(theStep);
4822 aParams.myNodes.Clear();
4823 aParams.mySteps = new TColStd_HSequenceOfReal;
4825 for(i=1; i<=theNbSteps; i++)
4826 aParams.mySteps->Append(theStep.Magnitude());
4829 ExtrusionSweep(theElems,aParams,newElemsMap,theMakeGroups,theFlags,theTolerance);
4833 //=======================================================================
4834 //function : ExtrusionSweep
4836 //=======================================================================
4838 SMESH_MeshEditor::PGroupIDs
4839 SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet & theElems,
4840 ExtrusParam& theParams,
4841 TElemOfElemListMap& newElemsMap,
4842 const bool theMakeGroups,
4844 const double theTolerance)
4846 myLastCreatedElems.Clear();
4847 myLastCreatedNodes.Clear();
4849 // source elements for each generated one
4850 SMESH_SequenceOfElemPtr srcElems, srcNodes;
4852 SMESHDS_Mesh* aMesh = GetMeshDS();
4854 int nbsteps = theParams.mySteps->Length();
4856 TNodeOfNodeListMap mapNewNodes;
4857 //TNodeOfNodeVecMap mapNewNodes;
4858 TElemOfVecOfNnlmiMap mapElemNewNodes;
4859 //TElemOfVecOfMapNodesMap mapElemNewNodes;
4861 const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) +
4862 myMesh->NbFaces(ORDER_QUADRATIC) +
4863 myMesh->NbVolumes(ORDER_QUADRATIC) );
4865 TIDSortedElemSet::iterator itElem;
4866 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) {
4867 // check element type
4868 const SMDS_MeshElement* elem = *itElem;
4869 if ( !elem || elem->GetType() == SMDSAbs_Volume )
4872 vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
4873 newNodesItVec.reserve( elem->NbNodes() );
4875 // loop on elem nodes
4876 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
4877 while ( itN->more() )
4879 // check if a node has been already sweeped
4880 const SMDS_MeshNode* node = cast2Node( itN->next() );
4881 TNodeOfNodeListMap::iterator nIt =
4882 mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
4883 list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
4884 if ( listNewNodes.empty() )
4888 // check if we are to create medium nodes between corner ones
4889 bool needMediumNodes = false;
4890 if ( isQuadraticMesh )
4892 SMDS_ElemIteratorPtr it = node->GetInverseElementIterator();
4893 while (it->more() && !needMediumNodes )
4895 const SMDS_MeshElement* invElem = it->next();
4896 if ( invElem != elem && !theElems.count( invElem )) continue;
4897 needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) );
4898 if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle )
4899 needMediumNodes = true;
4903 double coord[] = { node->X(), node->Y(), node->Z() };
4904 for ( int i = 0; i < nbsteps; i++ )
4906 if ( needMediumNodes ) // create a medium node
4908 double x = coord[0] + theParams.myDir.X()*theParams.mySteps->Value(i+1)/2.;
4909 double y = coord[1] + theParams.myDir.Y()*theParams.mySteps->Value(i+1)/2.;
4910 double z = coord[2] + theParams.myDir.Z()*theParams.mySteps->Value(i+1)/2.;
4911 if( theFlags & EXTRUSION_FLAG_SEW ) {
4912 const SMDS_MeshNode * newNode = CreateNode(x, y, z,
4913 theTolerance, theParams.myNodes);
4914 listNewNodes.push_back( newNode );
4917 const SMDS_MeshNode * newNode = aMesh->AddNode(x, y, z);
4918 myLastCreatedNodes.Append(newNode);
4919 srcNodes.Append( node );
4920 listNewNodes.push_back( newNode );
4923 // create a corner node
4924 coord[0] = coord[0] + theParams.myDir.X()*theParams.mySteps->Value(i+1);
4925 coord[1] = coord[1] + theParams.myDir.Y()*theParams.mySteps->Value(i+1);
4926 coord[2] = coord[2] + theParams.myDir.Z()*theParams.mySteps->Value(i+1);
4927 if( theFlags & EXTRUSION_FLAG_SEW ) {
4928 const SMDS_MeshNode * newNode = CreateNode(coord[0], coord[1], coord[2],
4929 theTolerance, theParams.myNodes);
4930 listNewNodes.push_back( newNode );
4933 const SMDS_MeshNode * newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
4934 myLastCreatedNodes.Append(newNode);
4935 srcNodes.Append( node );
4936 listNewNodes.push_back( newNode );
4940 newNodesItVec.push_back( nIt );
4942 // make new elements
4943 sweepElement( elem, newNodesItVec, newElemsMap[elem], nbsteps, srcElems );
4946 if( theFlags & EXTRUSION_FLAG_BOUNDARY ) {
4947 makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElems, nbsteps, srcElems );
4949 PGroupIDs newGroupIDs;
4950 if ( theMakeGroups )
4951 newGroupIDs = generateGroups( srcNodes, srcElems, "extruded");
4956 //=======================================================================
4957 //function : ExtrusionAlongTrack
4959 //=======================================================================
4960 SMESH_MeshEditor::Extrusion_Error
4961 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet & theElements,
4962 SMESH_subMesh* theTrack,
4963 const SMDS_MeshNode* theN1,
4964 const bool theHasAngles,
4965 list<double>& theAngles,
4966 const bool theLinearVariation,
4967 const bool theHasRefPoint,
4968 const gp_Pnt& theRefPoint,
4969 const bool theMakeGroups)
4971 MESSAGE("ExtrusionAlongTrack");
4972 myLastCreatedElems.Clear();
4973 myLastCreatedNodes.Clear();
4976 std::list<double> aPrms;
4977 TIDSortedElemSet::iterator itElem;
4980 TopoDS_Edge aTrackEdge;
4981 TopoDS_Vertex aV1, aV2;
4983 SMDS_ElemIteratorPtr aItE;
4984 SMDS_NodeIteratorPtr aItN;
4985 SMDSAbs_ElementType aTypeE;
4987 TNodeOfNodeListMap mapNewNodes;
4990 aNbE = theElements.size();
4993 return EXTR_NO_ELEMENTS;
4995 // 1.1 Track Pattern
4998 SMESHDS_SubMesh* pSubMeshDS = theTrack->GetSubMeshDS();
5000 aItE = pSubMeshDS->GetElements();
5001 while ( aItE->more() ) {
5002 const SMDS_MeshElement* pE = aItE->next();
5003 aTypeE = pE->GetType();
5004 // Pattern must contain links only
5005 if ( aTypeE != SMDSAbs_Edge )
5006 return EXTR_PATH_NOT_EDGE;
5009 list<SMESH_MeshEditor_PathPoint> fullList;
5011 const TopoDS_Shape& aS = theTrack->GetSubShape();
5012 // Sub-shape for the Pattern must be an Edge or Wire
5013 if( aS.ShapeType() == TopAbs_EDGE ) {
5014 aTrackEdge = TopoDS::Edge( aS );
5015 // the Edge must not be degenerated
5016 if ( BRep_Tool::Degenerated( aTrackEdge ) )
5017 return EXTR_BAD_PATH_SHAPE;
5018 TopExp::Vertices( aTrackEdge, aV1, aV2 );
5019 aItN = theTrack->GetFather()->GetSubMesh( aV1 )->GetSubMeshDS()->GetNodes();
5020 const SMDS_MeshNode* aN1 = aItN->next();
5021 aItN = theTrack->GetFather()->GetSubMesh( aV2 )->GetSubMeshDS()->GetNodes();
5022 const SMDS_MeshNode* aN2 = aItN->next();
5023 // starting node must be aN1 or aN2
5024 if ( !( aN1 == theN1 || aN2 == theN1 ) )
5025 return EXTR_BAD_STARTING_NODE;
5026 aItN = pSubMeshDS->GetNodes();
5027 while ( aItN->more() ) {
5028 const SMDS_MeshNode* pNode = aItN->next();
5029 const SMDS_EdgePosition* pEPos =
5030 static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
5031 double aT = pEPos->GetUParameter();
5032 aPrms.push_back( aT );
5034 //Extrusion_Error err =
5035 MakeEdgePathPoints(aPrms, aTrackEdge, (aN1==theN1), fullList);
5036 } else if( aS.ShapeType() == TopAbs_WIRE ) {
5037 list< SMESH_subMesh* > LSM;
5038 TopTools_SequenceOfShape Edges;
5039 SMESH_subMeshIteratorPtr itSM = theTrack->getDependsOnIterator(false,true);
5040 while(itSM->more()) {
5041 SMESH_subMesh* SM = itSM->next();
5043 const TopoDS_Shape& aS = SM->GetSubShape();
5046 list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
5047 int startNid = theN1->GetID();
5048 TColStd_MapOfInteger UsedNums;
5050 int NbEdges = Edges.Length();
5052 for(; i<=NbEdges; i++) {
5054 list< SMESH_subMesh* >::iterator itLSM = LSM.begin();
5055 for(; itLSM!=LSM.end(); itLSM++) {
5057 if(UsedNums.Contains(k)) continue;
5058 aTrackEdge = TopoDS::Edge( Edges.Value(k) );
5059 SMESH_subMesh* locTrack = *itLSM;
5060 SMESHDS_SubMesh* locMeshDS = locTrack->GetSubMeshDS();
5061 TopExp::Vertices( aTrackEdge, aV1, aV2 );
5062 aItN = locTrack->GetFather()->GetSubMesh(aV1)->GetSubMeshDS()->GetNodes();
5063 const SMDS_MeshNode* aN1 = aItN->next();
5064 aItN = locTrack->GetFather()->GetSubMesh(aV2)->GetSubMeshDS()->GetNodes();
5065 const SMDS_MeshNode* aN2 = aItN->next();
5066 // starting node must be aN1 or aN2
5067 if ( !( aN1->GetID() == startNid || aN2->GetID() == startNid ) ) continue;
5068 // 2. Collect parameters on the track edge
5070 aItN = locMeshDS->GetNodes();
5071 while ( aItN->more() ) {
5072 const SMDS_MeshNode* pNode = aItN->next();
5073 const SMDS_EdgePosition* pEPos =
5074 static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
5075 double aT = pEPos->GetUParameter();
5076 aPrms.push_back( aT );
5078 list<SMESH_MeshEditor_PathPoint> LPP;
5079 //Extrusion_Error err =
5080 MakeEdgePathPoints(aPrms, aTrackEdge,(aN1->GetID()==startNid), LPP);
5081 LLPPs.push_back(LPP);
5083 // update startN for search following egde
5084 if( aN1->GetID() == startNid ) startNid = aN2->GetID();
5085 else startNid = aN1->GetID();
5089 list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
5090 list<SMESH_MeshEditor_PathPoint> firstList = *itLLPP;
5091 list<SMESH_MeshEditor_PathPoint>::iterator itPP = firstList.begin();
5092 for(; itPP!=firstList.end(); itPP++) {
5093 fullList.push_back( *itPP );
5095 SMESH_MeshEditor_PathPoint PP1 = fullList.back();
5096 fullList.pop_back();
5098 for(; itLLPP!=LLPPs.end(); itLLPP++) {
5099 list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
5100 itPP = currList.begin();
5101 SMESH_MeshEditor_PathPoint PP2 = currList.front();
5102 gp_Dir D1 = PP1.Tangent();
5103 gp_Dir D2 = PP2.Tangent();
5104 gp_Dir Dnew( gp_Vec( (D1.X()+D2.X())/2, (D1.Y()+D2.Y())/2,
5105 (D1.Z()+D2.Z())/2 ) );
5106 PP1.SetTangent(Dnew);
5107 fullList.push_back(PP1);
5109 for(; itPP!=firstList.end(); itPP++) {
5110 fullList.push_back( *itPP );
5112 PP1 = fullList.back();
5113 fullList.pop_back();
5115 // if wire not closed
5116 fullList.push_back(PP1);
5120 return EXTR_BAD_PATH_SHAPE;
5123 return MakeExtrElements(theElements, fullList, theHasAngles, theAngles, theLinearVariation,
5124 theHasRefPoint, theRefPoint, theMakeGroups);
5128 //=======================================================================
5129 //function : ExtrusionAlongTrack
5131 //=======================================================================
5132 SMESH_MeshEditor::Extrusion_Error
5133 SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet & theElements,
5134 SMESH_Mesh* theTrack,
5135 const SMDS_MeshNode* theN1,
5136 const bool theHasAngles,
5137 list<double>& theAngles,
5138 const bool theLinearVariation,
5139 const bool theHasRefPoint,
5140 const gp_Pnt& theRefPoint,
5141 const bool theMakeGroups)
5143 myLastCreatedElems.Clear();
5144 myLastCreatedNodes.Clear();
5147 std::list<double> aPrms;
5148 TIDSortedElemSet::iterator itElem;
5151 TopoDS_Edge aTrackEdge;
5152 TopoDS_Vertex aV1, aV2;
5154 SMDS_ElemIteratorPtr aItE;
5155 SMDS_NodeIteratorPtr aItN;
5156 SMDSAbs_ElementType aTypeE;
5158 TNodeOfNodeListMap mapNewNodes;
5161 aNbE = theElements.size();
5164 return EXTR_NO_ELEMENTS;
5166 // 1.1 Track Pattern
5169 SMESHDS_Mesh* pMeshDS = theTrack->GetMeshDS();
5171 aItE = pMeshDS->elementsIterator();
5172 while ( aItE->more() ) {
5173 const SMDS_MeshElement* pE = aItE->next();
5174 aTypeE = pE->GetType();
5175 // Pattern must contain links only
5176 if ( aTypeE != SMDSAbs_Edge )
5177 return EXTR_PATH_NOT_EDGE;
5180 list<SMESH_MeshEditor_PathPoint> fullList;
5182 const TopoDS_Shape& aS = theTrack->GetShapeToMesh();
5184 if ( !theTrack->HasShapeToMesh() ) {
5185 //Mesh without shape
5186 const SMDS_MeshNode* currentNode = NULL;
5187 const SMDS_MeshNode* prevNode = theN1;
5188 std::vector<const SMDS_MeshNode*> aNodesList;
5189 aNodesList.push_back(theN1);
5190 int nbEdges = 0, conn=0;
5191 const SMDS_MeshElement* prevElem = NULL;
5192 const SMDS_MeshElement* currentElem = NULL;
5193 int totalNbEdges = theTrack->NbEdges();
5194 SMDS_ElemIteratorPtr nIt;
5197 if( !theTrack->GetMeshDS()->Contains(theN1) ) {
5198 return EXTR_BAD_STARTING_NODE;
5201 conn = nbEdgeConnectivity(theN1);
5203 return EXTR_PATH_NOT_EDGE;
5205 aItE = theN1->GetInverseElementIterator();
5206 prevElem = aItE->next();
5207 currentElem = prevElem;
5209 if(totalNbEdges == 1 ) {
5210 nIt = currentElem->nodesIterator();
5211 currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
5212 if(currentNode == prevNode)
5213 currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
5214 aNodesList.push_back(currentNode);
5216 nIt = currentElem->nodesIterator();
5217 while( nIt->more() ) {
5218 currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
5219 if(currentNode == prevNode)
5220 currentNode = static_cast<const SMDS_MeshNode*>(nIt->next());
5221 aNodesList.push_back(currentNode);
5223 //case of the closed mesh
5224 if(currentNode == theN1) {
5229 conn = nbEdgeConnectivity(currentNode);
5231 return EXTR_PATH_NOT_EDGE;
5232 }else if( conn == 1 && nbEdges > 0 ) {
5237 prevNode = currentNode;
5238 aItE = currentNode->GetInverseElementIterator();
5239 currentElem = aItE->next();
5240 if( currentElem == prevElem)
5241 currentElem = aItE->next();
5242 nIt = currentElem->nodesIterator();
5243 prevElem = currentElem;
5249 if(nbEdges != totalNbEdges)
5250 return EXTR_PATH_NOT_EDGE;
5252 TopTools_SequenceOfShape Edges;
5253 double x1,x2,y1,y2,z1,z2;
5254 list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
5255 int startNid = theN1->GetID();
5256 for(int i = 1; i < aNodesList.size(); i++) {
5257 x1 = aNodesList[i-1]->X();x2 = aNodesList[i]->X();
5258 y1 = aNodesList[i-1]->Y();y2 = aNodesList[i]->Y();
5259 z1 = aNodesList[i-1]->Z();z2 = aNodesList[i]->Z();
5260 TopoDS_Edge e = BRepBuilderAPI_MakeEdge(gp_Pnt(x1,y1,z1),gp_Pnt(x2,y2,z2));
5261 list<SMESH_MeshEditor_PathPoint> LPP;
5263 MakeEdgePathPoints(aPrms, e, (aNodesList[i-1]->GetID()==startNid), LPP);
5264 LLPPs.push_back(LPP);
5265 if( aNodesList[i-1]->GetID() == startNid ) startNid = aNodesList[i]->GetID();
5266 else startNid = aNodesList[i-1]->GetID();
5270 list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
5271 list<SMESH_MeshEditor_PathPoint> firstList = *itLLPP;
5272 list<SMESH_MeshEditor_PathPoint>::iterator itPP = firstList.begin();
5273 for(; itPP!=firstList.end(); itPP++) {
5274 fullList.push_back( *itPP );
5277 SMESH_MeshEditor_PathPoint PP1 = fullList.back();
5278 SMESH_MeshEditor_PathPoint PP2;
5279 fullList.pop_back();
5281 for(; itLLPP!=LLPPs.end(); itLLPP++) {
5282 list<SMESH_MeshEditor_PathPoint> currList = *itLLPP;
5283 itPP = currList.begin();
5284 PP2 = currList.front();
5285 gp_Dir D1 = PP1.Tangent();
5286 gp_Dir D2 = PP2.Tangent();
5287 gp_Dir Dnew( gp_Vec( (D1.X()+D2.X())/2, (D1.Y()+D2.Y())/2,
5288 (D1.Z()+D2.Z())/2 ) );
5289 PP1.SetTangent(Dnew);
5290 fullList.push_back(PP1);
5292 for(; itPP!=currList.end(); itPP++) {
5293 fullList.push_back( *itPP );
5295 PP1 = fullList.back();
5296 fullList.pop_back();
5298 fullList.push_back(PP1);
5300 } // Sub-shape for the Pattern must be an Edge or Wire
5301 else if( aS.ShapeType() == TopAbs_EDGE ) {
5302 aTrackEdge = TopoDS::Edge( aS );
5303 // the Edge must not be degenerated
5304 if ( BRep_Tool::Degenerated( aTrackEdge ) )
5305 return EXTR_BAD_PATH_SHAPE;
5306 TopExp::Vertices( aTrackEdge, aV1, aV2 );
5307 const SMDS_MeshNode* aN1 = SMESH_Algo::VertexNode( aV1, pMeshDS );
5308 const SMDS_MeshNode* aN2 = SMESH_Algo::VertexNode( aV2, pMeshDS );
5309 // starting node must be aN1 or aN2
5310 if ( !( aN1 == theN1 || aN2 == theN1 ) )
5311 return EXTR_BAD_STARTING_NODE;
5312 aItN = pMeshDS->nodesIterator();
5313 while ( aItN->more() ) {
5314 const SMDS_MeshNode* pNode = aItN->next();
5315 if( pNode==aN1 || pNode==aN2 ) continue;
5316 const SMDS_EdgePosition* pEPos =
5317 static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
5318 double aT = pEPos->GetUParameter();
5319 aPrms.push_back( aT );
5321 //Extrusion_Error err =
5322 MakeEdgePathPoints(aPrms, aTrackEdge, (aN1==theN1), fullList);
5324 else if( aS.ShapeType() == TopAbs_WIRE ) {
5325 list< SMESH_subMesh* > LSM;
5326 TopTools_SequenceOfShape Edges;
5327 TopExp_Explorer eExp(aS, TopAbs_EDGE);
5328 for(; eExp.More(); eExp.Next()) {
5329 TopoDS_Edge E = TopoDS::Edge( eExp.Current() );
5330 if( BRep_Tool::Degenerated(E) ) continue;
5331 SMESH_subMesh* SM = theTrack->GetSubMesh(E);
5337 list< list<SMESH_MeshEditor_PathPoint> > LLPPs;
5338 TopoDS_Vertex aVprev;
5339 TColStd_MapOfInteger UsedNums;
5340 int NbEdges = Edges.Length();
5342 for(; i<=NbEdges; i++) {
5344 list< SMESH_subMesh* >::iterator itLSM = LSM.begin();
5345 for(; itLSM!=LSM.end(); itLSM++) {
5347 if(UsedNums.Contains(k)) continue;
5348 aTrackEdge = TopoDS::Edge( Edges.Value(k) );
5349 SMESH_subMesh* locTrack = *itLSM;
5350 SMESHDS_SubMesh* locMeshDS = locTrack->GetSubMeshDS();
5351 TopExp::Vertices( aTrackEdge, aV1, aV2 );
5352 bool aN1isOK = false, aN2isOK = false;
5353 if ( aVprev.IsNull() ) {
5354 // if previous vertex is not yet defined, it means that we in the beginning of wire
5355 // and we have to find initial vertex corresponding to starting node theN1
5356 const SMDS_MeshNode* aN1 = SMESH_Algo::VertexNode( aV1, pMeshDS );
5357 const SMDS_MeshNode* aN2 = SMESH_Algo::VertexNode( aV2, pMeshDS );
5358 // starting node must be aN1 or aN2
5359 aN1isOK = ( aN1 && aN1 == theN1 );
5360 aN2isOK = ( aN2 && aN2 == theN1 );
5363 // we have specified ending vertex of the previous edge on the previous iteration
5364 // and we have just to check that it corresponds to any vertex in current segment
5365 aN1isOK = aVprev.IsSame( aV1 );
5366 aN2isOK = aVprev.IsSame( aV2 );
5368 if ( !aN1isOK && !aN2isOK ) continue;
5369 // 2. Collect parameters on the track edge
5371 aItN = locMeshDS->GetNodes();
5372 while ( aItN->more() ) {
5373 const SMDS_MeshNode* pNode = aItN->next();
5374 const SMDS_EdgePosition* pEPos =
5375 static_cast<const SMDS_EdgePosition*>( pNode->GetPosition() );
5376 double aT = pEPos->GetUParameter();
5377 aPrms.push_back( aT );
5379 list<SMESH_MeshEditor_PathPoint> LPP;
5380 //Extrusion_Error err =
5381 MakeEdgePathPoints(aPrms, aTrackEdge, aN1isOK, LPP);
5382 LLPPs.push_back(LPP);
5384 // update startN for search following egde
5385 if ( aN1isOK ) aVprev = aV2;
5390 list< list<SMESH_MeshEditor_PathPoint> >::iterator itLLPP = LLPPs.begin();
5391 list<SMESH_MeshEditor_PathPoint>& firstList = *itLLPP;
5392 fullList.splice( fullList.end(), firstList );
5394 SMESH_MeshEditor_PathPoint PP1 = fullList.back();
5395 fullList.pop_back();
5397 for(; itLLPP!=LLPPs.end(); itLLPP++) {
5398 list<SMESH_MeshEditor_PathPoint>& currList = *itLLPP;
5399 SMESH_MeshEditor_PathPoint PP2 = currList.front();
5400 gp_Dir D1 = PP1.Tangent();
5401 gp_Dir D2 = PP2.Tangent();
5402 gp_Dir Dnew( ( D1.XYZ() + D2.XYZ() ) / 2 );
5403 PP1.SetTangent(Dnew);
5404 fullList.push_back(PP1);
5405 fullList.splice( fullList.end(), currList, ++currList.begin(), currList.end() );
5406 PP1 = fullList.back();
5407 fullList.pop_back();
5409 // if wire not closed
5410 fullList.push_back(PP1);
5414 return EXTR_BAD_PATH_SHAPE;
5417 return MakeExtrElements(theElements, fullList, theHasAngles, theAngles, theLinearVariation,
5418 theHasRefPoint, theRefPoint, theMakeGroups);
5422 //=======================================================================
5423 //function : MakeEdgePathPoints
5424 //purpose : auxilary for ExtrusionAlongTrack
5425 //=======================================================================
5426 SMESH_MeshEditor::Extrusion_Error
5427 SMESH_MeshEditor::MakeEdgePathPoints(std::list<double>& aPrms,
5428 const TopoDS_Edge& aTrackEdge,
5430 list<SMESH_MeshEditor_PathPoint>& LPP)
5432 Standard_Real aTx1, aTx2, aL2, aTolVec, aTolVec2;
5434 aTolVec2=aTolVec*aTolVec;
5436 TopoDS_Vertex aV1, aV2;
5437 TopExp::Vertices( aTrackEdge, aV1, aV2 );
5438 aT1=BRep_Tool::Parameter( aV1, aTrackEdge );
5439 aT2=BRep_Tool::Parameter( aV2, aTrackEdge );
5440 // 2. Collect parameters on the track edge
5441 aPrms.push_front( aT1 );
5442 aPrms.push_back( aT2 );
5445 if( FirstIsStart ) {
5456 SMESH_MeshEditor_PathPoint aPP;
5457 Handle(Geom_Curve) aC3D = BRep_Tool::Curve( aTrackEdge, aTx1, aTx2 );
5458 std::list<double>::iterator aItD = aPrms.begin();
5459 for(; aItD != aPrms.end(); ++aItD) {
5463 aC3D->D1( aT, aP3D, aVec );
5464 aL2 = aVec.SquareMagnitude();
5465 if ( aL2 < aTolVec2 )
5466 return EXTR_CANT_GET_TANGENT;
5467 gp_Dir aTgt( aVec );
5469 aPP.SetTangent( aTgt );
5470 aPP.SetParameter( aT );
5477 //=======================================================================
5478 //function : MakeExtrElements
5479 //purpose : auxilary for ExtrusionAlongTrack
5480 //=======================================================================
5481 SMESH_MeshEditor::Extrusion_Error
5482 SMESH_MeshEditor::MakeExtrElements(TIDSortedElemSet& theElements,
5483 list<SMESH_MeshEditor_PathPoint>& fullList,
5484 const bool theHasAngles,
5485 list<double>& theAngles,
5486 const bool theLinearVariation,
5487 const bool theHasRefPoint,
5488 const gp_Pnt& theRefPoint,
5489 const bool theMakeGroups)
5491 const int aNbTP = fullList.size();
5493 if( theHasAngles && !theAngles.empty() && theLinearVariation )
5494 LinearAngleVariation(aNbTP-1, theAngles);
5495 // fill vector of path points with angles
5496 vector<SMESH_MeshEditor_PathPoint> aPPs;
5497 list<SMESH_MeshEditor_PathPoint>::iterator itPP = fullList.begin();
5498 list<double>::iterator itAngles = theAngles.begin();
5499 aPPs.push_back( *itPP++ );
5500 for( ; itPP != fullList.end(); itPP++) {
5501 aPPs.push_back( *itPP );
5502 if ( theHasAngles && itAngles != theAngles.end() )
5503 aPPs.back().SetAngle( *itAngles );
5506 TNodeOfNodeListMap mapNewNodes;
5507 TElemOfVecOfNnlmiMap mapElemNewNodes;
5508 TElemOfElemListMap newElemsMap;
5509 TIDSortedElemSet::iterator itElem;
5510 // source elements for each generated one
5511 SMESH_SequenceOfElemPtr srcElems, srcNodes;
5513 // 3. Center of rotation aV0
5514 gp_Pnt aV0 = theRefPoint;
5515 if ( !theHasRefPoint )
5517 gp_XYZ aGC( 0.,0.,0. );
5518 TIDSortedElemSet newNodes;
5520 itElem = theElements.begin();
5521 for ( ; itElem != theElements.end(); itElem++ ) {
5522 const SMDS_MeshElement* elem = *itElem;
5524 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5525 while ( itN->more() ) {
5526 const SMDS_MeshElement* node = itN->next();
5527 if ( newNodes.insert( node ).second )
5528 aGC += SMESH_TNodeXYZ( node );
5531 aGC /= newNodes.size();
5533 } // if (!theHasRefPoint) {
5535 // 4. Processing the elements
5536 SMESHDS_Mesh* aMesh = GetMeshDS();
5538 for ( itElem = theElements.begin(); itElem != theElements.end(); itElem++ ) {
5539 // check element type
5540 const SMDS_MeshElement* elem = *itElem;
5541 SMDSAbs_ElementType aTypeE = elem->GetType();
5542 if ( !elem || ( aTypeE != SMDSAbs_Face && aTypeE != SMDSAbs_Edge ) )
5545 vector<TNodeOfNodeListMapItr> & newNodesItVec = mapElemNewNodes[ elem ];
5546 newNodesItVec.reserve( elem->NbNodes() );
5548 // loop on elem nodes
5550 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5551 while ( itN->more() )
5554 // check if a node has been already processed
5555 const SMDS_MeshNode* node =
5556 static_cast<const SMDS_MeshNode*>( itN->next() );
5557 TNodeOfNodeListMap::iterator nIt = mapNewNodes.find( node );
5558 if ( nIt == mapNewNodes.end() ) {
5559 nIt = mapNewNodes.insert( make_pair( node, list<const SMDS_MeshNode*>() )).first;
5560 list<const SMDS_MeshNode*>& listNewNodes = nIt->second;
5563 Standard_Real aAngle1x, aAngleT1T0, aTolAng;
5564 gp_Pnt aP0x, aP1x, aPN0, aPN1, aV0x, aV1x;
5565 gp_Ax1 anAx1, anAxT1T0;
5566 gp_Dir aDT1x, aDT0x, aDT1T0;
5571 aPN0 = SMESH_TNodeXYZ( node );
5573 const SMESH_MeshEditor_PathPoint& aPP0 = aPPs[0];
5575 aDT0x= aPP0.Tangent();
5576 //cout<<"j = 0 PP: Pnt("<<aP0x.X()<<","<<aP0x.Y()<<","<<aP0x.Z()<<")"<<endl;
5578 for ( int j = 1; j < aNbTP; ++j ) {
5579 const SMESH_MeshEditor_PathPoint& aPP1 = aPPs[j];
5581 aDT1x = aPP1.Tangent();
5582 aAngle1x = aPP1.Angle();
5584 gp_Trsf aTrsf, aTrsfRot, aTrsfRotT1T0;
5586 gp_Vec aV01x( aP0x, aP1x );
5587 aTrsf.SetTranslation( aV01x );
5590 aV1x = aV0x.Transformed( aTrsf );
5591 aPN1 = aPN0.Transformed( aTrsf );
5593 // rotation 1 [ T1,T0 ]
5594 aAngleT1T0=-aDT1x.Angle( aDT0x );
5595 if (fabs(aAngleT1T0) > aTolAng) {
5597 anAxT1T0.SetLocation( aV1x );
5598 anAxT1T0.SetDirection( aDT1T0 );
5599 aTrsfRotT1T0.SetRotation( anAxT1T0, aAngleT1T0 );
5601 aPN1 = aPN1.Transformed( aTrsfRotT1T0 );
5605 if ( theHasAngles ) {
5606 anAx1.SetLocation( aV1x );
5607 anAx1.SetDirection( aDT1x );
5608 aTrsfRot.SetRotation( anAx1, aAngle1x );
5610 aPN1 = aPN1.Transformed( aTrsfRot );
5614 //MESSAGE("elem->IsQuadratic " << elem->IsQuadratic() << " " << elem->IsMediumNode(node));
5615 if( elem->IsQuadratic() && !elem->IsMediumNode(node) ) {
5616 // create additional node
5617 double x = ( aPN1.X() + aPN0.X() )/2.;
5618 double y = ( aPN1.Y() + aPN0.Y() )/2.;
5619 double z = ( aPN1.Z() + aPN0.Z() )/2.;
5620 const SMDS_MeshNode* newNode = aMesh->AddNode(x,y,z);
5621 myLastCreatedNodes.Append(newNode);
5622 srcNodes.Append( node );
5623 listNewNodes.push_back( newNode );
5625 const SMDS_MeshNode* newNode = aMesh->AddNode( aPN1.X(), aPN1.Y(), aPN1.Z() );
5626 myLastCreatedNodes.Append(newNode);
5627 srcNodes.Append( node );
5628 listNewNodes.push_back( newNode );
5638 // if current elem is quadratic and current node is not medium
5639 // we have to check - may be it is needed to insert additional nodes
5640 if( elem->IsQuadratic() && !elem->IsMediumNode(node) ) {
5641 list< const SMDS_MeshNode* > & listNewNodes = nIt->second;
5642 if(listNewNodes.size()==aNbTP-1) {
5643 vector<const SMDS_MeshNode*> aNodes(2*(aNbTP-1));
5644 gp_XYZ P(node->X(), node->Y(), node->Z());
5645 list< const SMDS_MeshNode* >::iterator it = listNewNodes.begin();
5647 for(i=0; i<aNbTP-1; i++) {
5648 const SMDS_MeshNode* N = *it;
5649 double x = ( N->X() + P.X() )/2.;
5650 double y = ( N->Y() + P.Y() )/2.;
5651 double z = ( N->Z() + P.Z() )/2.;
5652 const SMDS_MeshNode* newN = aMesh->AddNode(x,y,z);
5653 srcNodes.Append( node );
5654 myLastCreatedNodes.Append(newN);
5657 P = gp_XYZ(N->X(),N->Y(),N->Z());
5659 listNewNodes.clear();
5660 for(i=0; i<2*(aNbTP-1); i++) {
5661 listNewNodes.push_back(aNodes[i]);
5667 newNodesItVec.push_back( nIt );
5669 // make new elements
5670 //sweepElement( aMesh, elem, newNodesItVec, newElemsMap[elem],
5671 // newNodesItVec[0]->second.size(), myLastCreatedElems );
5672 sweepElement( elem, newNodesItVec, newElemsMap[elem], aNbTP-1, srcElems );
5675 makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElements, aNbTP-1, srcElems );
5677 if ( theMakeGroups )
5678 generateGroups( srcNodes, srcElems, "extruded");
5684 //=======================================================================
5685 //function : LinearAngleVariation
5686 //purpose : auxilary for ExtrusionAlongTrack
5687 //=======================================================================
5688 void SMESH_MeshEditor::LinearAngleVariation(const int nbSteps,
5689 list<double>& Angles)
5691 int nbAngles = Angles.size();
5692 if( nbSteps > nbAngles ) {
5693 vector<double> theAngles(nbAngles);
5694 list<double>::iterator it = Angles.begin();
5696 for(; it!=Angles.end(); it++) {
5698 theAngles[i] = (*it);
5701 double rAn2St = double( nbAngles ) / double( nbSteps );
5702 double angPrev = 0, angle;
5703 for ( int iSt = 0; iSt < nbSteps; ++iSt ) {
5704 double angCur = rAn2St * ( iSt+1 );
5705 double angCurFloor = floor( angCur );
5706 double angPrevFloor = floor( angPrev );
5707 if ( angPrevFloor == angCurFloor )
5708 angle = rAn2St * theAngles[ int( angCurFloor ) ];
5710 int iP = int( angPrevFloor );
5711 double angPrevCeil = ceil(angPrev);
5712 angle = ( angPrevCeil - angPrev ) * theAngles[ iP ];
5714 int iC = int( angCurFloor );
5715 if ( iC < nbAngles )
5716 angle += ( angCur - angCurFloor ) * theAngles[ iC ];
5718 iP = int( angPrevCeil );
5720 angle += theAngles[ iC ];
5722 res.push_back(angle);
5727 for(; it!=res.end(); it++)
5728 Angles.push_back( *it );
5733 //================================================================================
5735 * \brief Move or copy theElements applying theTrsf to their nodes
5736 * \param theElems - elements to transform, if theElems is empty then apply to all mesh nodes
5737 * \param theTrsf - transformation to apply
5738 * \param theCopy - if true, create translated copies of theElems
5739 * \param theMakeGroups - if true and theCopy, create translated groups
5740 * \param theTargetMesh - mesh to copy translated elements into
5741 * \return SMESH_MeshEditor::PGroupIDs - list of ids of created groups
5743 //================================================================================
5745 SMESH_MeshEditor::PGroupIDs
5746 SMESH_MeshEditor::Transform (TIDSortedElemSet & theElems,
5747 const gp_Trsf& theTrsf,
5749 const bool theMakeGroups,
5750 SMESH_Mesh* theTargetMesh)
5752 myLastCreatedElems.Clear();
5753 myLastCreatedNodes.Clear();
5755 bool needReverse = false;
5756 string groupPostfix;
5757 switch ( theTrsf.Form() ) {
5759 MESSAGE("gp_PntMirror");
5761 groupPostfix = "mirrored";
5764 MESSAGE("gp_Ax1Mirror");
5765 groupPostfix = "mirrored";
5768 MESSAGE("gp_Ax2Mirror");
5770 groupPostfix = "mirrored";
5773 MESSAGE("gp_Rotation");
5774 groupPostfix = "rotated";
5776 case gp_Translation:
5777 MESSAGE("gp_Translation");
5778 groupPostfix = "translated";
5781 MESSAGE("gp_Scale");
5782 groupPostfix = "scaled";
5784 case gp_CompoundTrsf: // different scale by axis
5785 MESSAGE("gp_CompoundTrsf");
5786 groupPostfix = "scaled";
5790 needReverse = false;
5791 groupPostfix = "transformed";
5794 SMESH_MeshEditor targetMeshEditor( theTargetMesh );
5795 SMESHDS_Mesh* aTgtMesh = theTargetMesh ? theTargetMesh->GetMeshDS() : 0;
5796 SMESHDS_Mesh* aMesh = GetMeshDS();
5799 // map old node to new one
5800 TNodeNodeMap nodeMap;
5802 // elements sharing moved nodes; those of them which have all
5803 // nodes mirrored but are not in theElems are to be reversed
5804 TIDSortedElemSet inverseElemSet;
5806 // source elements for each generated one
5807 SMESH_SequenceOfElemPtr srcElems, srcNodes;
5809 // issue 021015: EDF 1578 SMESH: Free nodes are removed when translating a mesh
5810 TIDSortedElemSet orphanNode;
5812 if ( theElems.empty() ) // transform the whole mesh
5815 SMDS_ElemIteratorPtr eIt = aMesh->elementsIterator();
5816 while ( eIt->more() ) theElems.insert( eIt->next() );
5818 SMDS_NodeIteratorPtr nIt = aMesh->nodesIterator();
5819 while ( nIt->more() )
5821 const SMDS_MeshNode* node = nIt->next();
5822 if ( node->NbInverseElements() == 0)
5823 orphanNode.insert( node );
5827 // loop on elements to transform nodes : first orphan nodes then elems
5828 TIDSortedElemSet::iterator itElem;
5829 TIDSortedElemSet *elements[] = {&orphanNode, &theElems };
5830 for (int i=0; i<2; i++)
5831 for ( itElem = elements[i]->begin(); itElem != elements[i]->end(); itElem++ ) {
5832 const SMDS_MeshElement* elem = *itElem;
5836 // loop on elem nodes
5837 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5838 while ( itN->more() ) {
5840 const SMDS_MeshNode* node = cast2Node( itN->next() );
5841 // check if a node has been already transformed
5842 pair<TNodeNodeMap::iterator,bool> n2n_isnew =
5843 nodeMap.insert( make_pair ( node, node ));
5844 if ( !n2n_isnew.second )
5848 coord[0] = node->X();
5849 coord[1] = node->Y();
5850 coord[2] = node->Z();
5851 theTrsf.Transforms( coord[0], coord[1], coord[2] );
5852 if ( theTargetMesh ) {
5853 const SMDS_MeshNode * newNode = aTgtMesh->AddNode( coord[0], coord[1], coord[2] );
5854 n2n_isnew.first->second = newNode;
5855 myLastCreatedNodes.Append(newNode);
5856 srcNodes.Append( node );
5858 else if ( theCopy ) {
5859 const SMDS_MeshNode * newNode = aMesh->AddNode( coord[0], coord[1], coord[2] );
5860 n2n_isnew.first->second = newNode;
5861 myLastCreatedNodes.Append(newNode);
5862 srcNodes.Append( node );
5865 aMesh->MoveNode( node, coord[0], coord[1], coord[2] );
5866 // node position on shape becomes invalid
5867 const_cast< SMDS_MeshNode* > ( node )->SetPosition
5868 ( SMDS_SpacePosition::originSpacePosition() );
5871 // keep inverse elements
5872 if ( !theCopy && !theTargetMesh && needReverse ) {
5873 SMDS_ElemIteratorPtr invElemIt = node->GetInverseElementIterator();
5874 while ( invElemIt->more() ) {
5875 const SMDS_MeshElement* iel = invElemIt->next();
5876 inverseElemSet.insert( iel );
5882 // either create new elements or reverse mirrored ones
5883 if ( !theCopy && !needReverse && !theTargetMesh )
5886 TIDSortedElemSet::iterator invElemIt = inverseElemSet.begin();
5887 for ( ; invElemIt != inverseElemSet.end(); invElemIt++ )
5888 theElems.insert( *invElemIt );
5890 // Replicate or reverse elements
5892 std::vector<int> iForw;
5893 for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ )
5895 const SMDS_MeshElement* elem = *itElem;
5896 if ( !elem ) continue;
5898 SMDSAbs_GeometryType geomType = elem->GetGeomType();
5899 int nbNodes = elem->NbNodes();
5900 if ( geomType == SMDSGeom_NONE ) continue; // node
5902 switch ( geomType ) {
5904 case SMDSGeom_POLYGON: // ---------------------- polygon
5906 vector<const SMDS_MeshNode*> poly_nodes (nbNodes);
5908 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
5909 while (itN->more()) {
5910 const SMDS_MeshNode* node =
5911 static_cast<const SMDS_MeshNode*>(itN->next());
5912 TNodeNodeMap::iterator nodeMapIt = nodeMap.find(node);
5913 if (nodeMapIt == nodeMap.end())
5914 break; // not all nodes transformed
5916 // reverse mirrored faces and volumes
5917 poly_nodes[nbNodes - iNode - 1] = (*nodeMapIt).second;
5919 poly_nodes[iNode] = (*nodeMapIt).second;
5923 if ( iNode != nbNodes )
5924 continue; // not all nodes transformed
5926 if ( theTargetMesh ) {
5927 myLastCreatedElems.Append(aTgtMesh->AddPolygonalFace(poly_nodes));
5928 srcElems.Append( elem );
5930 else if ( theCopy ) {
5931 myLastCreatedElems.Append(aMesh->AddPolygonalFace(poly_nodes));
5932 srcElems.Append( elem );
5935 aMesh->ChangePolygonNodes(elem, poly_nodes);
5940 case SMDSGeom_POLYHEDRA: // ------------------ polyhedral volume
5942 const SMDS_VtkVolume* aPolyedre =
5943 dynamic_cast<const SMDS_VtkVolume*>( elem );
5945 MESSAGE("Warning: bad volumic element");
5949 vector<const SMDS_MeshNode*> poly_nodes; poly_nodes.reserve( nbNodes );
5950 vector<int> quantities; quantities.reserve( nbNodes );
5952 bool allTransformed = true;
5953 int nbFaces = aPolyedre->NbFaces();
5954 for (int iface = 1; iface <= nbFaces && allTransformed; iface++) {
5955 int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
5956 for (int inode = 1; inode <= nbFaceNodes && allTransformed; inode++) {
5957 const SMDS_MeshNode* node = aPolyedre->GetFaceNode(iface, inode);
5958 TNodeNodeMap::iterator nodeMapIt = nodeMap.find(node);
5959 if (nodeMapIt == nodeMap.end()) {
5960 allTransformed = false; // not all nodes transformed
5962 poly_nodes.push_back((*nodeMapIt).second);
5964 if ( needReverse && allTransformed )
5965 std::reverse( poly_nodes.end() - nbFaceNodes, poly_nodes.end() );
5967 quantities.push_back(nbFaceNodes);
5969 if ( !allTransformed )
5970 continue; // not all nodes transformed
5972 if ( theTargetMesh ) {
5973 myLastCreatedElems.Append(aTgtMesh->AddPolyhedralVolume(poly_nodes, quantities));
5974 srcElems.Append( elem );
5976 else if ( theCopy ) {
5977 myLastCreatedElems.Append(aMesh->AddPolyhedralVolume(poly_nodes, quantities));
5978 srcElems.Append( elem );
5981 aMesh->ChangePolyhedronNodes(elem, poly_nodes, quantities);
5986 case SMDSGeom_BALL: // -------------------- Ball
5988 if ( !theCopy && !theTargetMesh ) continue;
5990 TNodeNodeMap::iterator nodeMapIt = nodeMap.find( elem->GetNode(0) );
5991 if (nodeMapIt == nodeMap.end())
5992 continue; // not all nodes transformed
5994 double diameter = static_cast<const SMDS_BallElement*>(elem)->GetDiameter();
5995 if ( theTargetMesh ) {
5996 myLastCreatedElems.Append(aTgtMesh->AddBall( nodeMapIt->second, diameter ));
5997 srcElems.Append( elem );
6000 myLastCreatedElems.Append(aMesh->AddBall( nodeMapIt->second, diameter ));
6001 srcElems.Append( elem );
6006 default: // ----------------------- Regular elements
6008 while ( iForw.size() < nbNodes ) iForw.push_back( iForw.size() );
6009 const std::vector<int>& iRev = SMDS_MeshCell::reverseSmdsOrder( elem->GetEntityType() );
6010 const std::vector<int>& i = needReverse ? iRev : iForw;
6012 // find transformed nodes
6013 vector<const SMDS_MeshNode*> nodes(nbNodes);
6015 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6016 while ( itN->more() ) {
6017 const SMDS_MeshNode* node =
6018 static_cast<const SMDS_MeshNode*>( itN->next() );
6019 TNodeNodeMap::iterator nodeMapIt = nodeMap.find( node );
6020 if ( nodeMapIt == nodeMap.end() )
6021 break; // not all nodes transformed
6022 nodes[ i [ iNode++ ]] = (*nodeMapIt).second;
6024 if ( iNode != nbNodes )
6025 continue; // not all nodes transformed
6027 if ( theTargetMesh ) {
6028 if ( SMDS_MeshElement* copy =
6029 targetMeshEditor.AddElement( nodes, elem->GetType(), elem->IsPoly() )) {
6030 myLastCreatedElems.Append( copy );
6031 srcElems.Append( elem );
6034 else if ( theCopy ) {
6035 if ( AddElement( nodes, elem->GetType(), elem->IsPoly() ))
6036 srcElems.Append( elem );
6039 // reverse element as it was reversed by transformation
6041 aMesh->ChangeElementNodes( elem, &nodes[0], nbNodes );
6043 } // switch ( geomType )
6045 } // loop on elements
6047 PGroupIDs newGroupIDs;
6049 if ( ( theMakeGroups && theCopy ) ||
6050 ( theMakeGroups && theTargetMesh ) )
6051 newGroupIDs = generateGroups( srcNodes, srcElems, groupPostfix, theTargetMesh );
6056 //=======================================================================
6058 * \brief Create groups of elements made during transformation
6059 * \param nodeGens - nodes making corresponding myLastCreatedNodes
6060 * \param elemGens - elements making corresponding myLastCreatedElems
6061 * \param postfix - to append to names of new groups
6063 //=======================================================================
6065 SMESH_MeshEditor::PGroupIDs
6066 SMESH_MeshEditor::generateGroups(const SMESH_SequenceOfElemPtr& nodeGens,
6067 const SMESH_SequenceOfElemPtr& elemGens,
6068 const std::string& postfix,
6069 SMESH_Mesh* targetMesh)
6071 PGroupIDs newGroupIDs( new list<int> );
6072 SMESH_Mesh* mesh = targetMesh ? targetMesh : GetMesh();
6074 // Sort existing groups by types and collect their names
6076 // to store an old group and a generated new ones
6078 using boost::make_tuple;
6079 typedef tuple< SMESHDS_GroupBase*, SMESHDS_Group*, SMESHDS_Group* > TOldNewGroup;
6080 vector< list< TOldNewGroup > > groupsByType( SMDSAbs_NbElementTypes );
6081 vector< TOldNewGroup* > orderedOldNewGroups; // in order of old groups
6083 set< string > groupNames;
6085 SMESH_Mesh::GroupIteratorPtr groupIt = GetMesh()->GetGroups();
6086 if ( !groupIt->more() ) return newGroupIDs;
6088 int newGroupID = mesh->GetGroupIds().back()+1;
6089 while ( groupIt->more() )
6091 SMESH_Group * group = groupIt->next();
6092 if ( !group ) continue;
6093 SMESHDS_GroupBase* groupDS = group->GetGroupDS();
6094 if ( !groupDS || groupDS->IsEmpty() ) continue;
6095 groupNames.insert ( group->GetName() );
6096 groupDS->SetStoreName( group->GetName() );
6097 const SMDSAbs_ElementType type = groupDS->GetType();
6098 SMESHDS_Group* newGroup = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
6099 SMESHDS_Group* newTopGroup = new SMESHDS_Group( newGroupID++, mesh->GetMeshDS(), type );
6100 groupsByType[ type ].push_back( make_tuple( groupDS, newGroup, newTopGroup ));
6101 orderedOldNewGroups.push_back( & groupsByType[ type ].back() );
6104 // Loop on nodes and elements to add them in new groups
6106 for ( int isNodes = 0; isNodes < 2; ++isNodes )
6108 const SMESH_SequenceOfElemPtr& gens = isNodes ? nodeGens : elemGens;
6109 const SMESH_SequenceOfElemPtr& elems = isNodes ? myLastCreatedNodes : myLastCreatedElems;
6110 if ( gens.Length() != elems.Length() )
6111 throw SALOME_Exception("SMESH_MeshEditor::generateGroups(): invalid args");
6113 // loop on created elements
6114 for (int iElem = 1; iElem <= elems.Length(); ++iElem )
6116 const SMDS_MeshElement* sourceElem = gens( iElem );
6117 if ( !sourceElem ) {
6118 MESSAGE("generateGroups(): NULL source element");
6121 list< TOldNewGroup > & groupsOldNew = groupsByType[ sourceElem->GetType() ];
6122 if ( groupsOldNew.empty() ) { // no groups of this type at all
6123 while ( iElem < gens.Length() && gens( iElem+1 ) == sourceElem )
6124 ++iElem; // skip all elements made by sourceElem
6127 // collect all elements made by the iElem-th sourceElem
6128 list< const SMDS_MeshElement* > resultElems;
6129 if ( const SMDS_MeshElement* resElem = elems( iElem ))
6130 if ( resElem != sourceElem )
6131 resultElems.push_back( resElem );
6132 while ( iElem < gens.Length() && gens( iElem+1 ) == sourceElem )
6133 if ( const SMDS_MeshElement* resElem = elems( ++iElem ))
6134 if ( resElem != sourceElem )
6135 resultElems.push_back( resElem );
6137 // there must be a top element
6138 const SMDS_MeshElement* topElem = 0;
6141 topElem = resultElems.back();
6142 resultElems.pop_back();
6146 list< const SMDS_MeshElement* >::reverse_iterator resElemIt = resultElems.rbegin();
6147 for ( ; resElemIt != resultElems.rend() ; ++resElemIt )
6148 if ( (*resElemIt)->GetType() == sourceElem->GetType() )
6150 topElem = *resElemIt;
6151 resultElems.erase( --(resElemIt.base()) ); // erase *resElemIt
6156 // add resultElems to groups originted from ones the sourceElem belongs to
6157 list< TOldNewGroup >::iterator gOldNew, gLast = groupsOldNew.end();
6158 for ( gOldNew = groupsOldNew.begin(); gOldNew != gLast; ++gOldNew )
6160 SMESHDS_GroupBase* oldGroup = gOldNew->get<0>();
6161 if ( oldGroup->Contains( sourceElem )) // sourceElem is in oldGroup
6163 // fill in a new group
6164 SMDS_MeshGroup & newGroup = gOldNew->get<1>()->SMDSGroup();
6165 list< const SMDS_MeshElement* >::iterator resLast = resultElems.end(), resElemIt;
6166 for ( resElemIt = resultElems.begin(); resElemIt != resLast; ++resElemIt )
6167 newGroup.Add( *resElemIt );
6169 // fill a "top" group
6172 SMDS_MeshGroup & newTopGroup = gOldNew->get<2>()->SMDSGroup();
6173 newTopGroup.Add( topElem );
6177 } // loop on created elements
6178 }// loop on nodes and elements
6180 // Create new SMESH_Groups from SMESHDS_Groups and remove empty SMESHDS_Groups
6182 list<int> topGrouIds;
6183 for ( size_t i = 0; i < orderedOldNewGroups.size(); ++i )
6185 SMESHDS_GroupBase* oldGroupDS = orderedOldNewGroups[i]->get<0>();
6186 SMESHDS_Group* newGroups[2] = { orderedOldNewGroups[i]->get<1>(),
6187 orderedOldNewGroups[i]->get<2>() };
6188 const int nbNewGroups = !newGroups[0]->IsEmpty() + !newGroups[1]->IsEmpty();
6189 for ( int is2nd = 0; is2nd < 2; ++is2nd )
6191 SMESHDS_Group* newGroupDS = newGroups[ is2nd ];
6192 if ( newGroupDS->IsEmpty() )
6194 mesh->GetMeshDS()->RemoveGroup( newGroupDS );
6199 newGroupDS->SetType( newGroupDS->GetElements()->next()->GetType() );
6202 const bool isTop = ( nbNewGroups == 2 &&
6203 newGroupDS->GetType() == oldGroupDS->GetType() &&
6206 string name = oldGroupDS->GetStoreName();
6207 if ( !targetMesh ) {
6208 string suffix = ( isTop ? "top": postfix.c_str() );
6212 while ( !groupNames.insert( name ).second ) // name exists
6213 name = SMESH_Comment( oldGroupDS->GetStoreName() ) << "_" << suffix << "_" << nb++;
6218 newGroupDS->SetStoreName( name.c_str() );
6220 // make a SMESH_Groups
6221 mesh->AddGroup( newGroupDS );
6223 topGrouIds.push_back( newGroupDS->GetID() );
6225 newGroupIDs->push_back( newGroupDS->GetID() );
6229 newGroupIDs->splice( newGroupIDs->end(), topGrouIds );
6234 //================================================================================
6236 * \brief Return list of group of nodes close to each other within theTolerance
6237 * Search among theNodes or in the whole mesh if theNodes is empty using
6238 * an Octree algorithm
6240 //================================================================================
6242 void SMESH_MeshEditor::FindCoincidentNodes (TIDSortedNodeSet & theNodes,
6243 const double theTolerance,
6244 TListOfListOfNodes & theGroupsOfNodes)
6246 myLastCreatedElems.Clear();
6247 myLastCreatedNodes.Clear();
6249 if ( theNodes.empty() )
6250 { // get all nodes in the mesh
6251 SMDS_NodeIteratorPtr nIt = GetMeshDS()->nodesIterator(/*idInceasingOrder=*/true);
6252 while ( nIt->more() )
6253 theNodes.insert( theNodes.end(),nIt->next());
6256 SMESH_OctreeNode::FindCoincidentNodes ( theNodes, &theGroupsOfNodes, theTolerance);
6259 //=======================================================================
6260 //function : SimplifyFace
6262 //=======================================================================
6264 int SMESH_MeshEditor::SimplifyFace (const vector<const SMDS_MeshNode *>& faceNodes,
6265 vector<const SMDS_MeshNode *>& poly_nodes,
6266 vector<int>& quantities) const
6268 int nbNodes = faceNodes.size();
6273 set<const SMDS_MeshNode*> nodeSet;
6275 // get simple seq of nodes
6276 //const SMDS_MeshNode* simpleNodes[ nbNodes ];
6277 vector<const SMDS_MeshNode*> simpleNodes( nbNodes );
6278 int iSimple = 0, nbUnique = 0;
6280 simpleNodes[iSimple++] = faceNodes[0];
6282 for (int iCur = 1; iCur < nbNodes; iCur++) {
6283 if (faceNodes[iCur] != simpleNodes[iSimple - 1]) {
6284 simpleNodes[iSimple++] = faceNodes[iCur];
6285 if (nodeSet.insert( faceNodes[iCur] ).second)
6289 int nbSimple = iSimple;
6290 if (simpleNodes[nbSimple - 1] == simpleNodes[0]) {
6300 bool foundLoop = (nbSimple > nbUnique);
6303 set<const SMDS_MeshNode*> loopSet;
6304 for (iSimple = 0; iSimple < nbSimple && !foundLoop; iSimple++) {
6305 const SMDS_MeshNode* n = simpleNodes[iSimple];
6306 if (!loopSet.insert( n ).second) {
6310 int iC = 0, curLast = iSimple;
6311 for (; iC < curLast; iC++) {
6312 if (simpleNodes[iC] == n) break;
6314 int loopLen = curLast - iC;
6316 // create sub-element
6318 quantities.push_back(loopLen);
6319 for (; iC < curLast; iC++) {
6320 poly_nodes.push_back(simpleNodes[iC]);
6323 // shift the rest nodes (place from the first loop position)
6324 for (iC = curLast + 1; iC < nbSimple; iC++) {
6325 simpleNodes[iC - loopLen] = simpleNodes[iC];
6327 nbSimple -= loopLen;
6330 } // for (iSimple = 0; iSimple < nbSimple; iSimple++)
6331 } // while (foundLoop)
6335 quantities.push_back(iSimple);
6336 for (int i = 0; i < iSimple; i++)
6337 poly_nodes.push_back(simpleNodes[i]);
6343 //=======================================================================
6344 //function : MergeNodes
6345 //purpose : In each group, the cdr of nodes are substituted by the first one
6347 //=======================================================================
6349 void SMESH_MeshEditor::MergeNodes (TListOfListOfNodes & theGroupsOfNodes)
6351 MESSAGE("MergeNodes");
6352 myLastCreatedElems.Clear();
6353 myLastCreatedNodes.Clear();
6355 SMESHDS_Mesh* aMesh = GetMeshDS();
6357 TNodeNodeMap nodeNodeMap; // node to replace - new node
6358 set<const SMDS_MeshElement*> elems; // all elements with changed nodes
6359 list< int > rmElemIds, rmNodeIds;
6361 // Fill nodeNodeMap and elems
6363 TListOfListOfNodes::iterator grIt = theGroupsOfNodes.begin();
6364 for ( ; grIt != theGroupsOfNodes.end(); grIt++ ) {
6365 list<const SMDS_MeshNode*>& nodes = *grIt;
6366 list<const SMDS_MeshNode*>::iterator nIt = nodes.begin();
6367 const SMDS_MeshNode* nToKeep = *nIt;
6368 //MESSAGE("node to keep " << nToKeep->GetID());
6369 for ( ++nIt; nIt != nodes.end(); nIt++ ) {
6370 const SMDS_MeshNode* nToRemove = *nIt;
6371 nodeNodeMap.insert( TNodeNodeMap::value_type( nToRemove, nToKeep ));
6372 if ( nToRemove != nToKeep ) {
6373 //MESSAGE(" node to remove " << nToRemove->GetID());
6374 rmNodeIds.push_back( nToRemove->GetID() );
6375 AddToSameGroups( nToKeep, nToRemove, aMesh );
6376 // set _alwaysComputed to a sub-mesh of VERTEX to enable mesh computing
6377 // after MergeNodes() w/o creating node in place of merged ones.
6378 const SMDS_PositionPtr& pos = nToRemove->GetPosition();
6379 if ( pos && pos->GetTypeOfPosition() == SMDS_TOP_VERTEX )
6380 if ( SMESH_subMesh* sm = myMesh->GetSubMeshContaining( nToRemove->getshapeId() ))
6381 sm->SetIsAlwaysComputed( true );
6384 SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
6385 while ( invElemIt->more() ) {
6386 const SMDS_MeshElement* elem = invElemIt->next();
6391 // Change element nodes or remove an element
6393 set<const SMDS_MeshElement*>::iterator eIt = elems.begin();
6394 for ( ; eIt != elems.end(); eIt++ ) {
6395 const SMDS_MeshElement* elem = *eIt;
6396 //MESSAGE(" ---- inverse elem on node to remove " << elem->GetID());
6397 int nbNodes = elem->NbNodes();
6398 int aShapeId = FindShape( elem );
6400 set<const SMDS_MeshNode*> nodeSet;
6401 vector< const SMDS_MeshNode*> curNodes( nbNodes ), uniqueNodes( nbNodes );
6402 int iUnique = 0, iCur = 0, nbRepl = 0;
6403 vector<int> iRepl( nbNodes );
6405 // get new seq of nodes
6406 SMDS_ElemIteratorPtr itN = elem->nodesIterator();
6407 while ( itN->more() ) {
6408 const SMDS_MeshNode* n =
6409 static_cast<const SMDS_MeshNode*>( itN->next() );
6411 TNodeNodeMap::iterator nnIt = nodeNodeMap.find( n );
6412 if ( nnIt != nodeNodeMap.end() ) { // n sticks
6414 // BUG 0020185: begin
6416 bool stopRecur = false;
6417 set<const SMDS_MeshNode*> nodesRecur;
6418 nodesRecur.insert(n);
6419 while (!stopRecur) {
6420 TNodeNodeMap::iterator nnIt_i = nodeNodeMap.find( n );
6421 if ( nnIt_i != nodeNodeMap.end() ) { // n sticks
6422 n = (*nnIt_i).second;
6423 if (!nodesRecur.insert(n).second) {
6424 // error: recursive dependancy
6434 curNodes[ iCur ] = n;
6435 bool isUnique = nodeSet.insert( n ).second;
6437 uniqueNodes[ iUnique++ ] = n;
6439 iRepl[ nbRepl++ ] = iCur;
6443 // Analyse element topology after replacement
6446 int nbUniqueNodes = nodeSet.size();
6447 //MESSAGE("nbNodes nbUniqueNodes " << nbNodes << " " << nbUniqueNodes);
6448 if ( nbNodes != nbUniqueNodes ) { // some nodes stick
6449 // Polygons and Polyhedral volumes
6450 if (elem->IsPoly()) {
6452 if (elem->GetType() == SMDSAbs_Face) {
6454 vector<const SMDS_MeshNode *> face_nodes (nbNodes);
6456 for (; inode < nbNodes; inode++) {
6457 face_nodes[inode] = curNodes[inode];
6460 vector<const SMDS_MeshNode *> polygons_nodes;
6461 vector<int> quantities;
6462 int nbNew = SimplifyFace(face_nodes, polygons_nodes, quantities);
6465 for (int iface = 0; iface < nbNew; iface++) {
6466 int nbNodes = quantities[iface];
6467 vector<const SMDS_MeshNode *> poly_nodes (nbNodes);
6468 for (int ii = 0; ii < nbNodes; ii++, inode++) {
6469 poly_nodes[ii] = polygons_nodes[inode];
6471 SMDS_MeshElement* newElem = aMesh->AddPolygonalFace(poly_nodes);
6472 myLastCreatedElems.Append(newElem);
6474 aMesh->SetMeshElementOnShape(newElem, aShapeId);
6477 MESSAGE("ChangeElementNodes MergeNodes Polygon");
6478 //aMesh->ChangeElementNodes(elem, &polygons_nodes[inode], quantities[nbNew - 1]);
6479 vector<const SMDS_MeshNode *> polynodes(polygons_nodes.begin()+inode,polygons_nodes.end());
6481 if (nbNew > 0) quid = nbNew - 1;
6482 vector<int> newquant(quantities.begin()+quid, quantities.end());
6483 const SMDS_MeshElement* newElem = 0;
6484 newElem = aMesh->AddPolyhedralVolume(polynodes, newquant);
6485 myLastCreatedElems.Append(newElem);
6486 if ( aShapeId && newElem )
6487 aMesh->SetMeshElementOnShape( newElem, aShapeId );
6488 rmElemIds.push_back(elem->GetID());
6491 rmElemIds.push_back(elem->GetID());
6495 else if (elem->GetType() == SMDSAbs_Volume) {
6496 // Polyhedral volume
6497 if (nbUniqueNodes < 4) {
6498 rmElemIds.push_back(elem->GetID());
6501 // each face has to be analyzed in order to check volume validity
6502 const SMDS_VtkVolume* aPolyedre =
6503 dynamic_cast<const SMDS_VtkVolume*>( elem );
6505 int nbFaces = aPolyedre->NbFaces();
6507 vector<const SMDS_MeshNode *> poly_nodes;
6508 vector<int> quantities;
6510 for (int iface = 1; iface <= nbFaces; iface++) {
6511 int nbFaceNodes = aPolyedre->NbFaceNodes(iface);
6512 vector<const SMDS_MeshNode *> faceNodes (nbFaceNodes);
6514 for (int inode = 1; inode <= nbFaceNodes; inode++) {
6515 const SMDS_MeshNode * faceNode = aPolyedre->GetFaceNode(iface, inode);
6516 TNodeNodeMap::iterator nnIt = nodeNodeMap.find(faceNode);
6517 if (nnIt != nodeNodeMap.end()) { // faceNode sticks
6518 faceNode = (*nnIt).second;
6520 faceNodes[inode - 1] = faceNode;
6523 SimplifyFace(faceNodes, poly_nodes, quantities);
6526 if (quantities.size() > 3) {
6527 // to be done: remove coincident faces
6530 if (quantities.size() > 3)
6532 MESSAGE("ChangeElementNodes MergeNodes Polyhedron");
6533 //aMesh->ChangePolyhedronNodes(elem, poly_nodes, quantities);
6534 const SMDS_MeshElement* newElem = 0;
6535 newElem = aMesh->AddPolyhedralVolume(poly_nodes, quantities);
6536 myLastCreatedElems.Append(newElem);
6537 if ( aShapeId && newElem )
6538 aMesh->SetMeshElementOnShape( newElem, aShapeId );
6539 rmElemIds.push_back(elem->GetID());
6543 rmElemIds.push_back(elem->GetID());
6554 // TODO not all the possible cases are solved. Find something more generic?
6555 switch ( nbNodes ) {
6556 case 2: ///////////////////////////////////// EDGE
6557 isOk = false; break;
6558 case 3: ///////////////////////////////////// TRIANGLE
6559 isOk = false; break;
6561 if ( elem->GetType() == SMDSAbs_Volume ) // TETRAHEDRON
6563 else { //////////////////////////////////// QUADRANGLE
6564 if ( nbUniqueNodes < 3 )
6566 else if ( nbRepl == 2 && iRepl[ 1 ] - iRepl[ 0 ] == 2 )
6567 isOk = false; // opposite nodes stick
6568 //MESSAGE("isOk " << isOk);
6571 case 6: ///////////////////////////////////// PENTAHEDRON
6572 if ( nbUniqueNodes == 4 ) {
6573 // ---------------------------------> tetrahedron
6575 iRepl[ 0 ] > 2 && iRepl[ 1 ] > 2 && iRepl[ 2 ] > 2 ) {
6576 // all top nodes stick: reverse a bottom
6577 uniqueNodes[ 0 ] = curNodes [ 1 ];
6578 uniqueNodes[ 1 ] = curNodes [ 0 ];
6580 else if (nbRepl == 3 &&
6581 iRepl[ 0 ] < 3 && iRepl[ 1 ] < 3 && iRepl[ 2 ] < 3 ) {
6582 // all bottom nodes stick: set a top before
6583 uniqueNodes[ 3 ] = uniqueNodes [ 0 ];
6584 uniqueNodes[ 0 ] = curNodes [ 3 ];
6585 uniqueNodes[ 1 ] = curNodes [ 4 ];
6586 uniqueNodes[ 2 ] = curNodes [ 5 ];
6588 else if (nbRepl == 4 &&
6589 iRepl[ 2 ] - iRepl [ 0 ] == 3 && iRepl[ 3 ] - iRepl [ 1 ] == 3 ) {
6590 // a lateral face turns into a line: reverse a bottom
6591 uniqueNodes[ 0 ] = curNodes [ 1 ];
6592 uniqueNodes[ 1 ] = curNodes [ 0 ];
6597 else if ( nbUniqueNodes == 5 ) {
6598 // PENTAHEDRON --------------------> 2 tetrahedrons
6599 if ( nbRepl == 2 && iRepl[ 1 ] - iRepl [ 0 ] == 3 ) {
6600 // a bottom node sticks with a linked top one
6602 SMDS_MeshElement* newElem =
6603 aMesh->AddVolume(curNodes[ 3 ],
6606 curNodes[ iRepl[ 0 ] == 2 ? 1 : 2 ]);
6607 myLastCreatedElems.Append(newElem);
6609 aMesh->SetMeshElementOnShape( newElem, aShapeId );
6610 // 2. : reverse a bottom
6611 uniqueNodes[ 0 ] = curNodes [ 1 ];
6612 uniqueNodes[ 1 ] = curNodes [ 0 ];
6622 if(elem->IsQuadratic()) { // Quadratic quadrangle
6634 MESSAGE("nbRepl=2: " << iRepl[0] << " " << iRepl[1]);
6637 MESSAGE("nbRepl=3: " << iRepl[0] << " " << iRepl[1] << " " << iRepl[2]);
6639 if( iRepl[0]==0 && iRepl[1]==1 && iRepl[2]==4 ) {
6640 uniqueNodes[0] = curNodes[0];
6641 uniqueNodes[1] = curNodes[2];
6642 uniqueNodes[2] = curNodes[3];
6643 uniqueNodes[3] = curNodes[5];
6644 uniqueNodes[4] = curNodes[6];
6645 uniqueNodes[5] = curNodes[7];
6648 if( iRepl[0]==0 && iRepl[1]==3 && iRepl[2]==7 ) {
6649 uniqueNodes[0] = curNodes[0];
6650 uniqueNodes[1] = curNodes[1];
6651 uniqueNodes[2] = curNodes[2];
6652 uniqueNodes[3] = curNodes[4];
6653 uniqueNodes[4] = curNodes[5];
6654 uniqueNodes[5] = curNodes[6];
6657 if( iRepl[0]==0 && iRepl[1]==4 && iRepl[2]==7 ) {
6658 uniqueNodes[0] = curNodes[1];
6659 uniqueNodes[1] = curNodes[2];
6660 uniqueNodes[2] = curNodes[3];
6661 uniqueNodes[3] = curNodes[5];
6662 uniqueNodes[4] = curNodes[6];
6663 uniqueNodes[5] = curNodes[0];
6666 if( iRepl[0]==1 && iRepl[1]==2 && iRepl[2]==5 ) {
6667 uniqueNodes[0] = curNodes[0];
6668 uniqueNodes[1] = curNodes[1];
6669 uniqueNodes[2] = curNodes[3];
6670 uniqueNodes[3] = curNodes[4];
6671 uniqueNodes[4] = curNodes[6];
6672 uniqueNodes[5] = curNodes[7];
6675 if( iRepl[0]==1 && iRepl[1]==4 && iRepl[2]==5 ) {
6676 uniqueNodes[0] = curNodes[0];
6677 uniqueNodes[1] = curNodes[2];
6678 uniqueNodes[2] = curNodes[3];
6679 uniqueNodes[3] = curNodes[1];
6680 uniqueNodes[4] = curNodes[6];
6681 uniqueNodes[5] = curNodes[7];
6684 if( iRepl[0]==2 && iRepl[1]==3 && iRepl[2]==6 ) {
6685 uniqueNodes[0] = curNodes[0];
6686 uniqueNodes[1] = curNodes[1];
6687 uniqueNodes[2] = curNodes[2];
6688 uniqueNodes[3] = curNodes[4];
6689 uniqueNodes[4] = curNodes[5];
6690 uniqueNodes[5] = curNodes[7];
6693 if( iRepl[0]==2 && iRepl[1]==5 && iRepl[2]==6 ) {
6694 uniqueNodes[0] = curNodes[0];
6695 uniqueNodes[1] = curNodes[1];
6696 uniqueNodes[2] = curNodes[3];
6697 uniqueNodes[3] = curNodes[4];
6698 uniqueNodes[4] = curNodes[2];
6699 uniqueNodes[5] = curNodes[7];
6702 if( iRepl[0]==3 && iRepl[1]==6 && iRepl[2]==7 ) {
6703 uniqueNodes[0] = curNodes[0];
6704 uniqueNodes[1] = curNodes[1];
6705 uniqueNodes[2] = curNodes[2];
6706 uniqueNodes[3] = curNodes[4];
6707 uniqueNodes[4] = curNodes[5];
6708 uniqueNodes[5] = curNodes[3];
6713 MESSAGE("nbRepl=4: " << iRepl[0] << " " << iRepl[1] << " " << iRepl[2] << " " << iRepl[3]);
6716 MESSAGE("nbRepl=5: " << iRepl[0] << " " << iRepl[1] << " " << iRepl[2] << " " << iRepl[3] << " " << iRepl[4]);
6720 //////////////////////////////////// HEXAHEDRON
6722 SMDS_VolumeTool hexa (elem);
6723 hexa.SetExternalNormal();
6724 if ( nbUniqueNodes == 4 && nbRepl == 4 ) {
6725 //////////////////////// HEX ---> 1 tetrahedron
6726 for ( int iFace = 0; iFace < 6; iFace++ ) {
6727 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
6728 if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
6729 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
6730 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
6731 // one face turns into a point ...
6732 int iOppFace = hexa.GetOppFaceIndex( iFace );
6733 ind = hexa.GetFaceNodesIndices( iOppFace );
6735 for ( iCur = 0; iCur < 4 && nbStick < 2; iCur++ ) {
6736 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
6739 if ( nbStick == 1 ) {
6740 // ... and the opposite one - into a triangle.
6742 ind = hexa.GetFaceNodesIndices( iFace );
6743 uniqueNodes[ 3 ] = curNodes[ind[ 0 ]];
6750 else if ( nbUniqueNodes == 6 && nbRepl == 2 ) {
6751 //////////////////////// HEX ---> 1 prism
6752 int nbTria = 0, iTria[3];
6753 const int *ind; // indices of face nodes
6754 // look for triangular faces
6755 for ( int iFace = 0; iFace < 6 && nbTria < 3; iFace++ ) {
6756 ind = hexa.GetFaceNodesIndices( iFace );
6757 TIDSortedNodeSet faceNodes;
6758 for ( iCur = 0; iCur < 4; iCur++ )
6759 faceNodes.insert( curNodes[ind[iCur]] );
6760 if ( faceNodes.size() == 3 )
6761 iTria[ nbTria++ ] = iFace;
6763 // check if triangles are opposite
6764 if ( nbTria == 2 && iTria[0] == hexa.GetOppFaceIndex( iTria[1] ))
6767 // set nodes of the bottom triangle
6768 ind = hexa.GetFaceNodesIndices( iTria[ 0 ]);
6770 for ( iCur = 0; iCur < 4; iCur++ )
6771 if ( ind[iCur] != iRepl[0] && ind[iCur] != iRepl[1])
6772 indB.push_back( ind[iCur] );
6773 if ( !hexa.IsForward() )
6774 std::swap( indB[0], indB[2] );
6775 for ( iCur = 0; iCur < 3; iCur++ )
6776 uniqueNodes[ iCur ] = curNodes[indB[iCur]];
6777 // set nodes of the top triangle
6778 const int *indT = hexa.GetFaceNodesIndices( iTria[ 1 ]);
6779 for ( iCur = 0; iCur < 3; ++iCur )
6780 for ( int j = 0; j < 4; ++j )
6781 if ( hexa.IsLinked( indB[ iCur ], indT[ j ] ))
6783 uniqueNodes[ iCur + 3 ] = curNodes[ indT[ j ]];
6789 else if (nbUniqueNodes == 5 && nbRepl == 4 ) {
6790 //////////////////// HEXAHEDRON ---> 2 tetrahedrons
6791 for ( int iFace = 0; iFace < 6; iFace++ ) {
6792 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
6793 if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] &&
6794 curNodes[ind[ 0 ]] == curNodes[ind[ 2 ]] &&
6795 curNodes[ind[ 0 ]] == curNodes[ind[ 3 ]] ) {
6796 // one face turns into a point ...
6797 int iOppFace = hexa.GetOppFaceIndex( iFace );
6798 ind = hexa.GetFaceNodesIndices( iOppFace );
6800 iUnique = 2; // reverse a tetrahedron 1 bottom
6801 for ( iCur = 0; iCur < 4 && nbStick == 0; iCur++ ) {
6802 if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] )
6804 else if ( iUnique >= 0 )
6805 uniqueNodes[ iUnique-- ] = curNodes[ind[ iCur ]];
6807 if ( nbStick == 0 ) {
6808 // ... and the opposite one is a quadrangle
6810 const int* indTop = hexa.GetFaceNodesIndices( iFace );
6811 uniqueNodes[ 3 ] = curNodes[indTop[ 0 ]];
6814 SMDS_MeshElement* newElem =
6815 aMesh->AddVolume(curNodes[ind[ 0 ]],
6818 curNodes[indTop[ 0 ]]);
6819 myLastCreatedElems.Append(newElem);
6821 aMesh->SetMeshElementOnShape( newElem, aShapeId );
6828 else if ( nbUniqueNodes == 6 && nbRepl == 4 ) {
6829 ////////////////// HEXAHEDRON ---> 2 tetrahedrons or 1 prism
6830 // find indices of quad and tri faces
6831 int iQuadFace[ 6 ], iTriFace[ 6 ], nbQuad = 0, nbTri = 0, iFace;
6832 for ( iFace = 0; iFace < 6; iFace++ ) {
6833 const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes
6835 for ( iCur = 0; iCur < 4; iCur++ )
6836 nodeSet.insert( curNodes[ind[ iCur ]] );
6837 nbUniqueNodes = nodeSet.size();
6838 if ( nbUniqueNodes == 3 )
6839 iTriFace[ nbTri++ ] = iFace;
6840 else if ( nbUniqueNodes == 4 )
6841 iQuadFace[ nbQuad++ ] = iFace;
6843 if (nbQuad == 2 && nbTri == 4 &&
6844 hexa.GetOppFaceIndex( iQuadFace[ 0 ] ) == iQuadFace[ 1 ]) {
6845 // 2 opposite quadrangles stuck with a diagonal;
6846 // sample groups of merged indices: (0-4)(2-6)
6847 // --------------------------------------------> 2 tetrahedrons
6848 const int *ind1 = hexa.GetFaceNodesIndices( iQuadFace[ 0 ]); // indices of quad1 nodes
6849 const int *ind2 = hexa.GetFaceNodesIndices( iQuadFace[ 1 ]);
6850 int i0, i1d, i2, i3d, i0t, i2t; // d-daigonal, t-top
6851 if (curNodes[ind1[ 0 ]] == curNodes[ind2[ 0 ]] &&
6852 curNodes[ind1[ 2 ]] == curNodes[ind2[ 2 ]]) {
6853 // stuck with 0-2 diagonal
6861 else if (curNodes[ind1[ 1 ]] == curNodes[ind2[ 3 ]] &&
6862 curNodes[ind1[ 3 ]] == curNodes[ind2[ 1 ]]) {
6863 // stuck with 1-3 diagonal
6875 uniqueNodes[ 0 ] = curNodes [ i0 ];
6876 uniqueNodes[ 1 ] = curNodes [ i1d ];
6877 uniqueNodes[ 2 ] = curNodes [ i3d ];
6878 uniqueNodes[ 3 ] = curNodes [ i0t ];
6881 SMDS_MeshElement* newElem = aMesh->AddVolume(curNodes[ i1d ],
6885 myLastCreatedElems.Append(newElem);
6887 aMesh->SetMeshElementOnShape( newElem, aShapeId );
6890 else if (( nbTri == 2 && nbQuad == 3 ) || // merged (0-4)(1-5)
6891 ( nbTri == 4 && nbQuad == 2 )) { // merged (7-4)(1-5)
6892 // --------------------------------------------> prism
6893 // find 2 opposite triangles
6895 for ( iFace = 0; iFace + 1 < nbTri; iFace++ ) {
6896 if ( hexa.GetOppFaceIndex( iTriFace[ iFace ] ) == iTriFace[ iFace + 1 ]) {
6897 // find indices of kept and replaced nodes
6898 // and fill unique nodes of 2 opposite triangles
6899 const int *ind1 = hexa.GetFaceNodesIndices( iTriFace[ iFace ]);
6900 const int *ind2 = hexa.GetFaceNodesIndices( iTriFace[ iFace + 1 ]);
6901 const SMDS_MeshNode** hexanodes = hexa.GetNodes();
6902 // fill unique nodes
6905 for ( iCur = 0; iCur < 4 && isOk; iCur++ ) {
6906 const SMDS_MeshNode* n = curNodes[ind1[ iCur ]];
6907 const SMDS_MeshNode* nInit = hexanodes[ind1[ iCur ]];
6909 // iCur of a linked node of the opposite face (make normals co-directed):
6910 int iCurOpp = ( iCur == 1 || iCur == 3 ) ? 4 - iCur : iCur;
6911 // check that correspondent corners of triangles are linked
6912 if ( !hexa.IsLinked( ind1[ iCur ], ind2[ iCurOpp ] ))
6915 uniqueNodes[ iUnique ] = n;
6916 uniqueNodes[ iUnique + 3 ] = curNodes[ind2[ iCurOpp ]];
6925 } // if ( nbUniqueNodes == 6 && nbRepl == 4 )
6928 MESSAGE("MergeNodes() removes hexahedron "<< elem);
6935 } // switch ( nbNodes )
6937 } // if ( nbNodes != nbUniqueNodes ) // some nodes stick
6939 if ( isOk ) { // the elem remains valid after sticking nodes
6940 if (elem->IsPoly() && elem->GetType() == SMDSAbs_Volume)
6942 // Change nodes of polyedre
6943 const SMDS_VtkVolume* aPolyedre =
6944 dynamic_cast<const SMDS_VtkVolume*>( elem );
6946 int nbFaces = aPolyedre->NbFaces();
6948 vector<const SMDS_MeshNode *> poly_nodes;
6949 vector<int> quantities (nbFaces);
6951 for (int iface = 1; iface <= nbFaces; iface++) {
6952 int inode, nbFaceNodes = aPolyedre->NbFaceNodes(iface);
6953 quantities[iface - 1] = nbFaceNodes;
6955 for (inode = 1; inode <= nbFaceNodes; inode++) {
6956 const SMDS_MeshNode* curNode = aPolyedre->GetFaceNode(iface, inode);
6958 TNodeNodeMap::iterator nnIt = nodeNodeMap.find( curNode );
6959 if (nnIt != nodeNodeMap.end()) { // curNode sticks
6960 curNode = (*nnIt).second;
6962 poly_nodes.push_back(curNode);
6965 aMesh->ChangePolyhedronNodes( elem, poly_nodes, quantities );
6968 else // replace non-polyhedron elements
6970 const SMDSAbs_ElementType etyp = elem->GetType();
6971 const int elemId = elem->GetID();
6972 const bool isPoly = (elem->GetEntityType() == SMDSEntity_Polygon);
6973 uniqueNodes.resize(nbUniqueNodes);
6975 SMESHDS_SubMesh * sm = aShapeId > 0 ? aMesh->MeshElements(aShapeId) : 0;
6977 aMesh->RemoveFreeElement(elem, sm, /*fromGroups=*/false);
6978 SMDS_MeshElement* newElem = this->AddElement(uniqueNodes, etyp, isPoly, elemId);
6979 if ( sm && newElem )
6980 sm->AddElement( newElem );
6981 if ( elem != newElem )
6982 ReplaceElemInGroups( elem, newElem, aMesh );
6986 // Remove invalid regular element or invalid polygon
6987 rmElemIds.push_back( elem->GetID() );
6990 } // loop on elements
6992 // Remove bad elements, then equal nodes (order important)
6994 Remove( rmElemIds, false );
6995 Remove( rmNodeIds, true );
7000 // ========================================================
7001 // class : SortableElement
7002 // purpose : allow sorting elements basing on their nodes
7003 // ========================================================
7004 class SortableElement : public set <const SMDS_MeshElement*>
7008 SortableElement( const SMDS_MeshElement* theElem )
7011 SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator();
7012 while ( nodeIt->more() )
7013 this->insert( nodeIt->next() );
7016 const SMDS_MeshElement* Get() const
7019 void Set(const SMDS_MeshElement* e) const
7024 mutable const SMDS_MeshElement* myElem;
7027 //=======================================================================
7028 //function : FindEqualElements
7029 //purpose : Return list of group of elements built on the same nodes.
7030 // Search among theElements or in the whole mesh if theElements is empty
7031 //=======================================================================
7033 void SMESH_MeshEditor::FindEqualElements(TIDSortedElemSet & theElements,
7034 TListOfListOfElementsID & theGroupsOfElementsID)
7036 myLastCreatedElems.Clear();
7037 myLastCreatedNodes.Clear();
7039 typedef map< SortableElement, int > TMapOfNodeSet;
7040 typedef list<int> TGroupOfElems;
7042 if ( theElements.empty() )
7043 { // get all elements in the mesh
7044 SMDS_ElemIteratorPtr eIt = GetMeshDS()->elementsIterator();
7045 while ( eIt->more() )
7046 theElements.insert( theElements.end(), eIt->next());
7049 vector< TGroupOfElems > arrayOfGroups;
7050 TGroupOfElems groupOfElems;
7051 TMapOfNodeSet mapOfNodeSet;
7053 TIDSortedElemSet::iterator elemIt = theElements.begin();
7054 for ( int i = 0, j=0; elemIt != theElements.end(); ++elemIt, ++j ) {
7055 const SMDS_MeshElement* curElem = *elemIt;
7056 SortableElement SE(curElem);
7059 pair< TMapOfNodeSet::iterator, bool> pp = mapOfNodeSet.insert(make_pair(SE, i));
7060 if( !(pp.second) ) {
7061 TMapOfNodeSet::iterator& itSE = pp.first;
7062 ind = (*itSE).second;
7063 arrayOfGroups[ind].push_back(curElem->GetID());
7066 groupOfElems.clear();
7067 groupOfElems.push_back(curElem->GetID());
7068 arrayOfGroups.push_back(groupOfElems);
7073 vector< TGroupOfElems >::iterator groupIt = arrayOfGroups.begin();
7074 for ( ; groupIt != arrayOfGroups.end(); ++groupIt ) {
7075 groupOfElems = *groupIt;
7076 if ( groupOfElems.size() > 1 ) {
7077 groupOfElems.sort();
7078 theGroupsOfElementsID.push_back(groupOfElems);
7083 //=======================================================================
7084 //function : MergeElements
7085 //purpose : In each given group, substitute all elements by the first one.
7086 //=======================================================================
7088 void SMESH_MeshEditor::MergeElements(TListOfListOfElementsID & theGroupsOfElementsID)
7090 myLastCreatedElems.Clear();
7091 myLastCreatedNodes.Clear();
7093 typedef list<int> TListOfIDs;
7094 TListOfIDs rmElemIds; // IDs of elems to remove
7096 SMESHDS_Mesh* aMesh = GetMeshDS();
7098 TListOfListOfElementsID::iterator groupsIt = theGroupsOfElementsID.begin();
7099 while ( groupsIt != theGroupsOfElementsID.end() ) {
7100 TListOfIDs& aGroupOfElemID = *groupsIt;
7101 aGroupOfElemID.sort();
7102 int elemIDToKeep = aGroupOfElemID.front();
7103 const SMDS_MeshElement* elemToKeep = aMesh->FindElement(elemIDToKeep);
7104 aGroupOfElemID.pop_front();
7105 TListOfIDs::iterator idIt = aGroupOfElemID.begin();
7106 while ( idIt != aGroupOfElemID.end() ) {
7107 int elemIDToRemove = *idIt;
7108 const SMDS_MeshElement* elemToRemove = aMesh->FindElement(elemIDToRemove);
7109 // add the kept element in groups of removed one (PAL15188)
7110 AddToSameGroups( elemToKeep, elemToRemove, aMesh );
7111 rmElemIds.push_back( elemIDToRemove );
7117 Remove( rmElemIds, false );
7120 //=======================================================================
7121 //function : MergeEqualElements
7122 //purpose : Remove all but one of elements built on the same nodes.
7123 //=======================================================================
7125 void SMESH_MeshEditor::MergeEqualElements()
7127 TIDSortedElemSet aMeshElements; /* empty input ==
7128 to merge equal elements in the whole mesh */
7129 TListOfListOfElementsID aGroupsOfElementsID;
7130 FindEqualElements(aMeshElements, aGroupsOfElementsID);
7131 MergeElements(aGroupsOfElementsID);
7134 //=======================================================================
7135 //function : findAdjacentFace
7137 //=======================================================================
7139 static const SMDS_MeshElement* findAdjacentFace(const SMDS_MeshNode* n1,
7140 const SMDS_MeshNode* n2,
7141 const SMDS_MeshElement* elem)
7143 TIDSortedElemSet elemSet, avoidSet;
7145 avoidSet.insert ( elem );
7146 return SMESH_MeshAlgos::FindFaceInSet( n1, n2, elemSet, avoidSet );
7149 //=======================================================================
7150 //function : FindFreeBorder
7152 //=======================================================================
7154 #define ControlFreeBorder SMESH::Controls::FreeEdges::IsFreeEdge
7156 bool SMESH_MeshEditor::FindFreeBorder (const SMDS_MeshNode* theFirstNode,
7157 const SMDS_MeshNode* theSecondNode,
7158 const SMDS_MeshNode* theLastNode,
7159 list< const SMDS_MeshNode* > & theNodes,
7160 list< const SMDS_MeshElement* >& theFaces)
7162 if ( !theFirstNode || !theSecondNode )
7164 // find border face between theFirstNode and theSecondNode
7165 const SMDS_MeshElement* curElem = findAdjacentFace( theFirstNode, theSecondNode, 0 );
7169 theFaces.push_back( curElem );
7170 theNodes.push_back( theFirstNode );
7171 theNodes.push_back( theSecondNode );
7173 //vector<const SMDS_MeshNode*> nodes;
7174 const SMDS_MeshNode *nIgnore = theFirstNode, *nStart = theSecondNode;
7175 TIDSortedElemSet foundElems;
7176 bool needTheLast = ( theLastNode != 0 );
7178 while ( nStart != theLastNode ) {
7179 if ( nStart == theFirstNode )
7180 return !needTheLast;
7182 // find all free border faces sharing form nStart
7184 list< const SMDS_MeshElement* > curElemList;
7185 list< const SMDS_MeshNode* > nStartList;
7186 SMDS_ElemIteratorPtr invElemIt = nStart->GetInverseElementIterator(SMDSAbs_Face);
7187 while ( invElemIt->more() ) {
7188 const SMDS_MeshElement* e = invElemIt->next();
7189 if ( e == curElem || foundElems.insert( e ).second ) {
7191 int iNode = 0, nbNodes = e->NbNodes();
7192 //const SMDS_MeshNode* nodes[nbNodes+1];
7193 vector<const SMDS_MeshNode*> nodes(nbNodes+1);
7195 if(e->IsQuadratic()) {
7196 const SMDS_VtkFace* F =
7197 dynamic_cast<const SMDS_VtkFace*>(e);
7198 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
7199 // use special nodes iterator
7200 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
7201 while( anIter->more() ) {
7202 nodes[ iNode++ ] = cast2Node(anIter->next());
7206 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
7207 while ( nIt->more() )
7208 nodes[ iNode++ ] = static_cast<const SMDS_MeshNode*>( nIt->next() );
7210 nodes[ iNode ] = nodes[ 0 ];
7212 for ( iNode = 0; iNode < nbNodes; iNode++ )
7213 if (((nodes[ iNode ] == nStart && nodes[ iNode + 1] != nIgnore ) ||
7214 (nodes[ iNode + 1] == nStart && nodes[ iNode ] != nIgnore )) &&
7215 ControlFreeBorder( &nodes[ iNode ], e->GetID() ))
7217 nStartList.push_back( nodes[ iNode + ( nodes[ iNode ] == nStart ? 1 : 0 )]);
7218 curElemList.push_back( e );
7222 // analyse the found
7224 int nbNewBorders = curElemList.size();
7225 if ( nbNewBorders == 0 ) {
7226 // no free border furthermore
7227 return !needTheLast;
7229 else if ( nbNewBorders == 1 ) {
7230 // one more element found
7232 nStart = nStartList.front();
7233 curElem = curElemList.front();
7234 theFaces.push_back( curElem );
7235 theNodes.push_back( nStart );
7238 // several continuations found
7239 list< const SMDS_MeshElement* >::iterator curElemIt;
7240 list< const SMDS_MeshNode* >::iterator nStartIt;
7241 // check if one of them reached the last node
7242 if ( needTheLast ) {
7243 for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
7244 curElemIt!= curElemList.end();
7245 curElemIt++, nStartIt++ )
7246 if ( *nStartIt == theLastNode ) {
7247 theFaces.push_back( *curElemIt );
7248 theNodes.push_back( *nStartIt );
7252 // find the best free border by the continuations
7253 list<const SMDS_MeshNode*> contNodes[ 2 ], *cNL;
7254 list<const SMDS_MeshElement*> contFaces[ 2 ], *cFL;
7255 for (curElemIt = curElemList.begin(), nStartIt = nStartList.begin();
7256 curElemIt!= curElemList.end();
7257 curElemIt++, nStartIt++ )
7259 cNL = & contNodes[ contNodes[0].empty() ? 0 : 1 ];
7260 cFL = & contFaces[ contFaces[0].empty() ? 0 : 1 ];
7261 // find one more free border
7262 if ( ! SMESH_MeshEditor::FindFreeBorder( nStart, *nStartIt, theLastNode, *cNL, *cFL )) {
7266 else if ( !contNodes[0].empty() && !contNodes[1].empty() ) {
7267 // choice: clear a worse one
7268 int iLongest = ( contNodes[0].size() < contNodes[1].size() ? 1 : 0 );
7269 int iWorse = ( needTheLast ? 1 - iLongest : iLongest );
7270 contNodes[ iWorse ].clear();
7271 contFaces[ iWorse ].clear();
7274 if ( contNodes[0].empty() && contNodes[1].empty() )
7277 // append the best free border
7278 cNL = & contNodes[ contNodes[0].empty() ? 1 : 0 ];
7279 cFL = & contFaces[ contFaces[0].empty() ? 1 : 0 ];
7280 theNodes.pop_back(); // remove nIgnore
7281 theNodes.pop_back(); // remove nStart
7282 theFaces.pop_back(); // remove curElem
7283 list< const SMDS_MeshNode* >::iterator nIt = cNL->begin();
7284 list< const SMDS_MeshElement* >::iterator fIt = cFL->begin();
7285 for ( ; nIt != cNL->end(); nIt++ ) theNodes.push_back( *nIt );
7286 for ( ; fIt != cFL->end(); fIt++ ) theFaces.push_back( *fIt );
7289 } // several continuations found
7290 } // while ( nStart != theLastNode )
7295 //=======================================================================
7296 //function : CheckFreeBorderNodes
7297 //purpose : Return true if the tree nodes are on a free border
7298 //=======================================================================
7300 bool SMESH_MeshEditor::CheckFreeBorderNodes(const SMDS_MeshNode* theNode1,
7301 const SMDS_MeshNode* theNode2,
7302 const SMDS_MeshNode* theNode3)
7304 list< const SMDS_MeshNode* > nodes;
7305 list< const SMDS_MeshElement* > faces;
7306 return FindFreeBorder( theNode1, theNode2, theNode3, nodes, faces);
7309 //=======================================================================
7310 //function : SewFreeBorder
7312 //=======================================================================
7314 SMESH_MeshEditor::Sew_Error
7315 SMESH_MeshEditor::SewFreeBorder (const SMDS_MeshNode* theBordFirstNode,
7316 const SMDS_MeshNode* theBordSecondNode,
7317 const SMDS_MeshNode* theBordLastNode,
7318 const SMDS_MeshNode* theSideFirstNode,
7319 const SMDS_MeshNode* theSideSecondNode,
7320 const SMDS_MeshNode* theSideThirdNode,
7321 const bool theSideIsFreeBorder,
7322 const bool toCreatePolygons,
7323 const bool toCreatePolyedrs)
7325 myLastCreatedElems.Clear();
7326 myLastCreatedNodes.Clear();
7328 MESSAGE("::SewFreeBorder()");
7329 Sew_Error aResult = SEW_OK;
7331 // ====================================
7332 // find side nodes and elements
7333 // ====================================
7335 list< const SMDS_MeshNode* > nSide[ 2 ];
7336 list< const SMDS_MeshElement* > eSide[ 2 ];
7337 list< const SMDS_MeshNode* >::iterator nIt[ 2 ];
7338 list< const SMDS_MeshElement* >::iterator eIt[ 2 ];
7342 if (!FindFreeBorder(theBordFirstNode,theBordSecondNode,theBordLastNode,
7343 nSide[0], eSide[0])) {
7344 MESSAGE(" Free Border 1 not found " );
7345 aResult = SEW_BORDER1_NOT_FOUND;
7347 if (theSideIsFreeBorder) {
7350 if (!FindFreeBorder(theSideFirstNode, theSideSecondNode, theSideThirdNode,
7351 nSide[1], eSide[1])) {
7352 MESSAGE(" Free Border 2 not found " );
7353 aResult = ( aResult != SEW_OK ? SEW_BOTH_BORDERS_NOT_FOUND : SEW_BORDER2_NOT_FOUND );
7356 if ( aResult != SEW_OK )
7359 if (!theSideIsFreeBorder) {
7363 // -------------------------------------------------------------------------
7365 // 1. If nodes to merge are not coincident, move nodes of the free border
7366 // from the coord sys defined by the direction from the first to last
7367 // nodes of the border to the correspondent sys of the side 2
7368 // 2. On the side 2, find the links most co-directed with the correspondent
7369 // links of the free border
7370 // -------------------------------------------------------------------------
7372 // 1. Since sewing may break if there are volumes to split on the side 2,
7373 // we wont move nodes but just compute new coordinates for them
7374 typedef map<const SMDS_MeshNode*, gp_XYZ> TNodeXYZMap;
7375 TNodeXYZMap nBordXYZ;
7376 list< const SMDS_MeshNode* >& bordNodes = nSide[ 0 ];
7377 list< const SMDS_MeshNode* >::iterator nBordIt;
7379 gp_XYZ Pb1( theBordFirstNode->X(), theBordFirstNode->Y(), theBordFirstNode->Z() );
7380 gp_XYZ Pb2( theBordLastNode->X(), theBordLastNode->Y(), theBordLastNode->Z() );
7381 gp_XYZ Ps1( theSideFirstNode->X(), theSideFirstNode->Y(), theSideFirstNode->Z() );
7382 gp_XYZ Ps2( theSideSecondNode->X(), theSideSecondNode->Y(), theSideSecondNode->Z() );
7383 double tol2 = 1.e-8;
7384 gp_Vec Vbs1( Pb1 - Ps1 ),Vbs2( Pb2 - Ps2 );
7385 if ( Vbs1.SquareMagnitude() > tol2 || Vbs2.SquareMagnitude() > tol2 ) {
7386 // Need node movement.
7388 // find X and Z axes to create trsf
7389 gp_Vec Zb( Pb1 - Pb2 ), Zs( Ps1 - Ps2 );
7391 if ( X.SquareMagnitude() <= gp::Resolution() * gp::Resolution() )
7393 X = gp_Ax2( gp::Origin(), Zb ).XDirection();
7396 gp_Ax3 toBordAx( Pb1, Zb, X );
7397 gp_Ax3 fromSideAx( Ps1, Zs, X );
7398 gp_Ax3 toGlobalAx( gp::Origin(), gp::DZ(), gp::DX() );
7400 gp_Trsf toBordSys, fromSide2Sys;
7401 toBordSys.SetTransformation( toBordAx );
7402 fromSide2Sys.SetTransformation( fromSideAx, toGlobalAx );
7403 fromSide2Sys.SetScaleFactor( Zs.Magnitude() / Zb.Magnitude() );
7406 for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
7407 const SMDS_MeshNode* n = *nBordIt;
7408 gp_XYZ xyz( n->X(),n->Y(),n->Z() );
7409 toBordSys.Transforms( xyz );
7410 fromSide2Sys.Transforms( xyz );
7411 nBordXYZ.insert( TNodeXYZMap::value_type( n, xyz ));
7415 // just insert nodes XYZ in the nBordXYZ map
7416 for ( nBordIt = bordNodes.begin(); nBordIt != bordNodes.end(); nBordIt++ ) {
7417 const SMDS_MeshNode* n = *nBordIt;
7418 nBordXYZ.insert( TNodeXYZMap::value_type( n, gp_XYZ( n->X(),n->Y(),n->Z() )));
7422 // 2. On the side 2, find the links most co-directed with the correspondent
7423 // links of the free border
7425 list< const SMDS_MeshElement* >& sideElems = eSide[ 1 ];
7426 list< const SMDS_MeshNode* >& sideNodes = nSide[ 1 ];
7427 sideNodes.push_back( theSideFirstNode );
7429 bool hasVolumes = false;
7430 LinkID_Gen aLinkID_Gen( GetMeshDS() );
7431 set<long> foundSideLinkIDs, checkedLinkIDs;
7432 SMDS_VolumeTool volume;
7433 //const SMDS_MeshNode* faceNodes[ 4 ];
7435 const SMDS_MeshNode* sideNode;
7436 const SMDS_MeshElement* sideElem;
7437 const SMDS_MeshNode* prevSideNode = theSideFirstNode;
7438 const SMDS_MeshNode* prevBordNode = theBordFirstNode;
7439 nBordIt = bordNodes.begin();
7441 // border node position and border link direction to compare with
7442 gp_XYZ bordPos = nBordXYZ[ *nBordIt ];
7443 gp_XYZ bordDir = bordPos - nBordXYZ[ prevBordNode ];
7444 // choose next side node by link direction or by closeness to
7445 // the current border node:
7446 bool searchByDir = ( *nBordIt != theBordLastNode );
7448 // find the next node on the Side 2
7450 double maxDot = -DBL_MAX, minDist = DBL_MAX;
7452 checkedLinkIDs.clear();
7453 gp_XYZ prevXYZ( prevSideNode->X(), prevSideNode->Y(), prevSideNode->Z() );
7455 // loop on inverse elements of current node (prevSideNode) on the Side 2
7456 SMDS_ElemIteratorPtr invElemIt = prevSideNode->GetInverseElementIterator();
7457 while ( invElemIt->more() )
7459 const SMDS_MeshElement* elem = invElemIt->next();
7460 // prepare data for a loop on links coming to prevSideNode, of a face or a volume
7461 int iPrevNode, iNode = 0, nbNodes = elem->NbNodes();
7462 vector< const SMDS_MeshNode* > faceNodes( nbNodes, (const SMDS_MeshNode*)0 );
7463 bool isVolume = volume.Set( elem );
7464 const SMDS_MeshNode** nodes = isVolume ? volume.GetNodes() : & faceNodes[0];
7465 if ( isVolume ) // --volume
7467 else if ( elem->GetType()==SMDSAbs_Face ) { // --face
7468 // retrieve all face nodes and find iPrevNode - an index of the prevSideNode
7469 if(elem->IsQuadratic()) {
7470 const SMDS_VtkFace* F =
7471 dynamic_cast<const SMDS_VtkFace*>(elem);
7472 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
7473 // use special nodes iterator
7474 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
7475 while( anIter->more() ) {
7476 nodes[ iNode ] = cast2Node(anIter->next());
7477 if ( nodes[ iNode++ ] == prevSideNode )
7478 iPrevNode = iNode - 1;
7482 SMDS_ElemIteratorPtr nIt = elem->nodesIterator();
7483 while ( nIt->more() ) {
7484 nodes[ iNode ] = cast2Node( nIt->next() );
7485 if ( nodes[ iNode++ ] == prevSideNode )
7486 iPrevNode = iNode - 1;
7489 // there are 2 links to check
7494 // loop on links, to be precise, on the second node of links
7495 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
7496 const SMDS_MeshNode* n = nodes[ iNode ];
7498 if ( !volume.IsLinked( n, prevSideNode ))
7502 if ( iNode ) // a node before prevSideNode
7503 n = nodes[ iPrevNode == 0 ? elem->NbNodes() - 1 : iPrevNode - 1 ];
7504 else // a node after prevSideNode
7505 n = nodes[ iPrevNode + 1 == elem->NbNodes() ? 0 : iPrevNode + 1 ];
7507 // check if this link was already used
7508 long iLink = aLinkID_Gen.GetLinkID( prevSideNode, n );
7509 bool isJustChecked = !checkedLinkIDs.insert( iLink ).second;
7510 if (!isJustChecked &&
7511 foundSideLinkIDs.find( iLink ) == foundSideLinkIDs.end() )
7513 // test a link geometrically
7514 gp_XYZ nextXYZ ( n->X(), n->Y(), n->Z() );
7515 bool linkIsBetter = false;
7516 double dot = 0.0, dist = 0.0;
7517 if ( searchByDir ) { // choose most co-directed link
7518 dot = bordDir * ( nextXYZ - prevXYZ ).Normalized();
7519 linkIsBetter = ( dot > maxDot );
7521 else { // choose link with the node closest to bordPos
7522 dist = ( nextXYZ - bordPos ).SquareModulus();
7523 linkIsBetter = ( dist < minDist );
7525 if ( linkIsBetter ) {
7534 } // loop on inverse elements of prevSideNode
7537 MESSAGE(" Cant find path by links of the Side 2 ");
7538 return SEW_BAD_SIDE_NODES;
7540 sideNodes.push_back( sideNode );
7541 sideElems.push_back( sideElem );
7542 foundSideLinkIDs.insert ( linkID );
7543 prevSideNode = sideNode;
7545 if ( *nBordIt == theBordLastNode )
7546 searchByDir = false;
7548 // find the next border link to compare with
7549 gp_XYZ sidePos( sideNode->X(), sideNode->Y(), sideNode->Z() );
7550 searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
7551 // move to next border node if sideNode is before forward border node (bordPos)
7552 while ( *nBordIt != theBordLastNode && !searchByDir ) {
7553 prevBordNode = *nBordIt;
7555 bordPos = nBordXYZ[ *nBordIt ];
7556 bordDir = bordPos - nBordXYZ[ prevBordNode ];
7557 searchByDir = ( bordDir * ( sidePos - bordPos ) <= 0 );
7561 while ( sideNode != theSideSecondNode );
7563 if ( hasVolumes && sideNodes.size () != bordNodes.size() && !toCreatePolyedrs) {
7564 MESSAGE("VOLUME SPLITTING IS FORBIDDEN");
7565 return SEW_VOLUMES_TO_SPLIT; // volume splitting is forbidden
7567 } // end nodes search on the side 2
7569 // ============================
7570 // sew the border to the side 2
7571 // ============================
7573 int nbNodes[] = { nSide[0].size(), nSide[1].size() };
7574 int maxNbNodes = Max( nbNodes[0], nbNodes[1] );
7576 TListOfListOfNodes nodeGroupsToMerge;
7577 if ( nbNodes[0] == nbNodes[1] ||
7578 ( theSideIsFreeBorder && !theSideThirdNode)) {
7580 // all nodes are to be merged
7582 for (nIt[0] = nSide[0].begin(), nIt[1] = nSide[1].begin();
7583 nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end();
7584 nIt[0]++, nIt[1]++ )
7586 nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
7587 nodeGroupsToMerge.back().push_back( *nIt[1] ); // to keep
7588 nodeGroupsToMerge.back().push_back( *nIt[0] ); // to remove
7593 // insert new nodes into the border and the side to get equal nb of segments
7595 // get normalized parameters of nodes on the borders
7596 //double param[ 2 ][ maxNbNodes ];
7598 param[0] = new double [ maxNbNodes ];
7599 param[1] = new double [ maxNbNodes ];
7601 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
7602 list< const SMDS_MeshNode* >& nodes = nSide[ iBord ];
7603 list< const SMDS_MeshNode* >::iterator nIt = nodes.begin();
7604 const SMDS_MeshNode* nPrev = *nIt;
7605 double bordLength = 0;
7606 for ( iNode = 0; nIt != nodes.end(); nIt++, iNode++ ) { // loop on border nodes
7607 const SMDS_MeshNode* nCur = *nIt;
7608 gp_XYZ segment (nCur->X() - nPrev->X(),
7609 nCur->Y() - nPrev->Y(),
7610 nCur->Z() - nPrev->Z());
7611 double segmentLen = segment.Modulus();
7612 bordLength += segmentLen;
7613 param[ iBord ][ iNode ] = bordLength;
7616 // normalize within [0,1]
7617 for ( iNode = 0; iNode < nbNodes[ iBord ]; iNode++ ) {
7618 param[ iBord ][ iNode ] /= bordLength;
7622 // loop on border segments
7623 const SMDS_MeshNode *nPrev[ 2 ] = { 0, 0 };
7624 int i[ 2 ] = { 0, 0 };
7625 nIt[0] = nSide[0].begin(); eIt[0] = eSide[0].begin();
7626 nIt[1] = nSide[1].begin(); eIt[1] = eSide[1].begin();
7628 TElemOfNodeListMap insertMap;
7629 TElemOfNodeListMap::iterator insertMapIt;
7631 // key: elem to insert nodes into
7632 // value: 2 nodes to insert between + nodes to be inserted
7634 bool next[ 2 ] = { false, false };
7636 // find min adjacent segment length after sewing
7637 double nextParam = 10., prevParam = 0;
7638 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
7639 if ( i[ iBord ] + 1 < nbNodes[ iBord ])
7640 nextParam = Min( nextParam, param[iBord][ i[iBord] + 1 ]);
7641 if ( i[ iBord ] > 0 )
7642 prevParam = Max( prevParam, param[iBord][ i[iBord] - 1 ]);
7644 double minParam = Min( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
7645 double maxParam = Max( param[ 0 ][ i[0] ], param[ 1 ][ i[1] ]);
7646 double minSegLen = Min( nextParam - minParam, maxParam - prevParam );
7648 // choose to insert or to merge nodes
7649 double du = param[ 1 ][ i[1] ] - param[ 0 ][ i[0] ];
7650 if ( Abs( du ) <= minSegLen * 0.2 ) {
7653 nodeGroupsToMerge.push_back( list<const SMDS_MeshNode*>() );
7654 const SMDS_MeshNode* n0 = *nIt[0];
7655 const SMDS_MeshNode* n1 = *nIt[1];
7656 nodeGroupsToMerge.back().push_back( n1 );
7657 nodeGroupsToMerge.back().push_back( n0 );
7658 // position of node of the border changes due to merge
7659 param[ 0 ][ i[0] ] += du;
7660 // move n1 for the sake of elem shape evaluation during insertion.
7661 // n1 will be removed by MergeNodes() anyway
7662 const_cast<SMDS_MeshNode*>( n0 )->setXYZ( n1->X(), n1->Y(), n1->Z() );
7663 next[0] = next[1] = true;
7668 int intoBord = ( du < 0 ) ? 0 : 1;
7669 const SMDS_MeshElement* elem = *eIt[ intoBord ];
7670 const SMDS_MeshNode* n1 = nPrev[ intoBord ];
7671 const SMDS_MeshNode* n2 = *nIt[ intoBord ];
7672 const SMDS_MeshNode* nIns = *nIt[ 1 - intoBord ];
7673 if ( intoBord == 1 ) {
7674 // move node of the border to be on a link of elem of the side
7675 gp_XYZ p1 (n1->X(), n1->Y(), n1->Z());
7676 gp_XYZ p2 (n2->X(), n2->Y(), n2->Z());
7677 double ratio = du / ( param[ 1 ][ i[1] ] - param[ 1 ][ i[1]-1 ]);
7678 gp_XYZ p = p2 * ( 1 - ratio ) + p1 * ratio;
7679 GetMeshDS()->MoveNode( nIns, p.X(), p.Y(), p.Z() );
7681 insertMapIt = insertMap.find( elem );
7682 bool notFound = ( insertMapIt == insertMap.end() );
7683 bool otherLink = ( !notFound && (*insertMapIt).second.front() != n1 );
7685 // insert into another link of the same element:
7686 // 1. perform insertion into the other link of the elem
7687 list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
7688 const SMDS_MeshNode* n12 = nodeList.front(); nodeList.pop_front();
7689 const SMDS_MeshNode* n22 = nodeList.front(); nodeList.pop_front();
7690 InsertNodesIntoLink( elem, n12, n22, nodeList, toCreatePolygons );
7691 // 2. perform insertion into the link of adjacent faces
7693 const SMDS_MeshElement* adjElem = findAdjacentFace( n12, n22, elem );
7695 InsertNodesIntoLink( adjElem, n12, n22, nodeList, toCreatePolygons );
7699 if (toCreatePolyedrs) {
7700 // perform insertion into the links of adjacent volumes
7701 UpdateVolumes(n12, n22, nodeList);
7703 // 3. find an element appeared on n1 and n2 after the insertion
7704 insertMap.erase( elem );
7705 elem = findAdjacentFace( n1, n2, 0 );
7707 if ( notFound || otherLink ) {
7708 // add element and nodes of the side into the insertMap
7709 insertMapIt = insertMap.insert
7710 ( TElemOfNodeListMap::value_type( elem, list<const SMDS_MeshNode*>() )).first;
7711 (*insertMapIt).second.push_back( n1 );
7712 (*insertMapIt).second.push_back( n2 );
7714 // add node to be inserted into elem
7715 (*insertMapIt).second.push_back( nIns );
7716 next[ 1 - intoBord ] = true;
7719 // go to the next segment
7720 for ( iBord = 0; iBord < 2; iBord++ ) { // loop on 2 borders
7721 if ( next[ iBord ] ) {
7722 if ( i[ iBord ] != 0 && eIt[ iBord ] != eSide[ iBord ].end())
7724 nPrev[ iBord ] = *nIt[ iBord ];
7725 nIt[ iBord ]++; i[ iBord ]++;
7729 while ( nIt[0] != nSide[0].end() && nIt[1] != nSide[1].end());
7731 // perform insertion of nodes into elements
7733 for (insertMapIt = insertMap.begin();
7734 insertMapIt != insertMap.end();
7737 const SMDS_MeshElement* elem = (*insertMapIt).first;
7738 list<const SMDS_MeshNode*> & nodeList = (*insertMapIt).second;
7739 const SMDS_MeshNode* n1 = nodeList.front(); nodeList.pop_front();
7740 const SMDS_MeshNode* n2 = nodeList.front(); nodeList.pop_front();
7742 InsertNodesIntoLink( elem, n1, n2, nodeList, toCreatePolygons );
7744 if ( !theSideIsFreeBorder ) {
7745 // look for and insert nodes into the faces adjacent to elem
7747 const SMDS_MeshElement* adjElem = findAdjacentFace( n1, n2, elem );
7749 InsertNodesIntoLink( adjElem, n1, n2, nodeList, toCreatePolygons );
7754 if (toCreatePolyedrs) {
7755 // perform insertion into the links of adjacent volumes
7756 UpdateVolumes(n1, n2, nodeList);
7762 } // end: insert new nodes
7764 MergeNodes ( nodeGroupsToMerge );
7769 //=======================================================================
7770 //function : InsertNodesIntoLink
7771 //purpose : insert theNodesToInsert into theFace between theBetweenNode1
7772 // and theBetweenNode2 and split theElement
7773 //=======================================================================
7775 void SMESH_MeshEditor::InsertNodesIntoLink(const SMDS_MeshElement* theFace,
7776 const SMDS_MeshNode* theBetweenNode1,
7777 const SMDS_MeshNode* theBetweenNode2,
7778 list<const SMDS_MeshNode*>& theNodesToInsert,
7779 const bool toCreatePoly)
7781 if ( theFace->GetType() != SMDSAbs_Face ) return;
7783 // find indices of 2 link nodes and of the rest nodes
7784 int iNode = 0, il1, il2, i3, i4;
7785 il1 = il2 = i3 = i4 = -1;
7786 //const SMDS_MeshNode* nodes[ theFace->NbNodes() ];
7787 vector<const SMDS_MeshNode*> nodes( theFace->NbNodes() );
7789 if(theFace->IsQuadratic()) {
7790 const SMDS_VtkFace* F =
7791 dynamic_cast<const SMDS_VtkFace*>(theFace);
7792 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
7793 // use special nodes iterator
7794 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
7795 while( anIter->more() ) {
7796 const SMDS_MeshNode* n = cast2Node(anIter->next());
7797 if ( n == theBetweenNode1 )
7799 else if ( n == theBetweenNode2 )
7805 nodes[ iNode++ ] = n;
7809 SMDS_ElemIteratorPtr nodeIt = theFace->nodesIterator();
7810 while ( nodeIt->more() ) {
7811 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
7812 if ( n == theBetweenNode1 )
7814 else if ( n == theBetweenNode2 )
7820 nodes[ iNode++ ] = n;
7823 if ( il1 < 0 || il2 < 0 || i3 < 0 )
7826 // arrange link nodes to go one after another regarding the face orientation
7827 bool reverse = ( Abs( il2 - il1 ) == 1 ? il2 < il1 : il1 < il2 );
7828 list<const SMDS_MeshNode *> aNodesToInsert = theNodesToInsert;
7833 aNodesToInsert.reverse();
7835 // check that not link nodes of a quadrangles are in good order
7836 int nbFaceNodes = theFace->NbNodes();
7837 if ( nbFaceNodes == 4 && i4 - i3 != 1 ) {
7843 if (toCreatePoly || theFace->IsPoly()) {
7846 vector<const SMDS_MeshNode *> poly_nodes (nbFaceNodes + aNodesToInsert.size());
7848 // add nodes of face up to first node of link
7851 if(theFace->IsQuadratic()) {
7852 const SMDS_VtkFace* F =
7853 dynamic_cast<const SMDS_VtkFace*>(theFace);
7854 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
7855 // use special nodes iterator
7856 SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator();
7857 while( anIter->more() && !isFLN ) {
7858 const SMDS_MeshNode* n = cast2Node(anIter->next());
7859 poly_nodes[iNode++] = n;
7860 if (n == nodes[il1]) {
7864 // add nodes to insert
7865 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
7866 for (; nIt != aNodesToInsert.end(); nIt++) {
7867 poly_nodes[iNode++] = *nIt;
7869 // add nodes of face starting from last node of link
7870 while ( anIter->more() ) {
7871 poly_nodes[iNode++] = cast2Node(anIter->next());
7875 SMDS_ElemIteratorPtr nodeIt = theFace->nodesIterator();
7876 while ( nodeIt->more() && !isFLN ) {
7877 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
7878 poly_nodes[iNode++] = n;
7879 if (n == nodes[il1]) {
7883 // add nodes to insert
7884 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
7885 for (; nIt != aNodesToInsert.end(); nIt++) {
7886 poly_nodes[iNode++] = *nIt;
7888 // add nodes of face starting from last node of link
7889 while ( nodeIt->more() ) {
7890 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
7891 poly_nodes[iNode++] = n;
7895 // edit or replace the face
7896 SMESHDS_Mesh *aMesh = GetMeshDS();
7898 if (theFace->IsPoly()) {
7899 aMesh->ChangePolygonNodes(theFace, poly_nodes);
7902 int aShapeId = FindShape( theFace );
7904 SMDS_MeshElement* newElem = aMesh->AddPolygonalFace(poly_nodes);
7905 myLastCreatedElems.Append(newElem);
7906 if ( aShapeId && newElem )
7907 aMesh->SetMeshElementOnShape( newElem, aShapeId );
7909 aMesh->RemoveElement(theFace);
7914 SMESHDS_Mesh *aMesh = GetMeshDS();
7915 if( !theFace->IsQuadratic() ) {
7917 // put aNodesToInsert between theBetweenNode1 and theBetweenNode2
7918 int nbLinkNodes = 2 + aNodesToInsert.size();
7919 //const SMDS_MeshNode* linkNodes[ nbLinkNodes ];
7920 vector<const SMDS_MeshNode*> linkNodes( nbLinkNodes );
7921 linkNodes[ 0 ] = nodes[ il1 ];
7922 linkNodes[ nbLinkNodes - 1 ] = nodes[ il2 ];
7923 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
7924 for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
7925 linkNodes[ iNode++ ] = *nIt;
7927 // decide how to split a quadrangle: compare possible variants
7928 // and choose which of splits to be a quadrangle
7929 int i1, i2, iSplit, nbSplits = nbLinkNodes - 1, iBestQuad;
7930 if ( nbFaceNodes == 3 ) {
7931 iBestQuad = nbSplits;
7934 else if ( nbFaceNodes == 4 ) {
7935 SMESH::Controls::NumericalFunctorPtr aCrit( new SMESH::Controls::AspectRatio);
7936 double aBestRate = DBL_MAX;
7937 for ( int iQuad = 0; iQuad < nbSplits; iQuad++ ) {
7939 double aBadRate = 0;
7940 // evaluate elements quality
7941 for ( iSplit = 0; iSplit < nbSplits; iSplit++ ) {
7942 if ( iSplit == iQuad ) {
7943 SMDS_FaceOfNodes quad (linkNodes[ i1++ ],
7947 aBadRate += getBadRate( &quad, aCrit );
7950 SMDS_FaceOfNodes tria (linkNodes[ i1++ ],
7952 nodes[ iSplit < iQuad ? i4 : i3 ]);
7953 aBadRate += getBadRate( &tria, aCrit );
7957 if ( aBadRate < aBestRate ) {
7959 aBestRate = aBadRate;
7964 // create new elements
7965 int aShapeId = FindShape( theFace );
7968 for ( iSplit = 0; iSplit < nbSplits - 1; iSplit++ ) {
7969 SMDS_MeshElement* newElem = 0;
7970 if ( iSplit == iBestQuad )
7971 newElem = aMesh->AddFace (linkNodes[ i1++ ],
7976 newElem = aMesh->AddFace (linkNodes[ i1++ ],
7978 nodes[ iSplit < iBestQuad ? i4 : i3 ]);
7979 myLastCreatedElems.Append(newElem);
7980 if ( aShapeId && newElem )
7981 aMesh->SetMeshElementOnShape( newElem, aShapeId );
7984 // change nodes of theFace
7985 const SMDS_MeshNode* newNodes[ 4 ];
7986 newNodes[ 0 ] = linkNodes[ i1 ];
7987 newNodes[ 1 ] = linkNodes[ i2 ];
7988 newNodes[ 2 ] = nodes[ iSplit >= iBestQuad ? i3 : i4 ];
7989 newNodes[ 3 ] = nodes[ i4 ];
7990 //aMesh->ChangeElementNodes( theFace, newNodes, iSplit == iBestQuad ? 4 : 3 );
7991 const SMDS_MeshElement* newElem = 0;
7992 if (iSplit == iBestQuad)
7993 newElem = aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2], newNodes[3] );
7995 newElem = aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2] );
7996 myLastCreatedElems.Append(newElem);
7997 if ( aShapeId && newElem )
7998 aMesh->SetMeshElementOnShape( newElem, aShapeId );
7999 } // end if(!theFace->IsQuadratic())
8000 else { // theFace is quadratic
8001 // we have to split theFace on simple triangles and one simple quadrangle
8003 int nbshift = tmp*2;
8004 // shift nodes in nodes[] by nbshift
8006 for(i=0; i<nbshift; i++) {
8007 const SMDS_MeshNode* n = nodes[0];
8008 for(j=0; j<nbFaceNodes-1; j++) {
8009 nodes[j] = nodes[j+1];
8011 nodes[nbFaceNodes-1] = n;
8013 il1 = il1 - nbshift;
8014 // now have to insert nodes between n0 and n1 or n1 and n2 (see below)
8015 // n0 n1 n2 n0 n1 n2
8016 // +-----+-----+ +-----+-----+
8025 // create new elements
8026 int aShapeId = FindShape( theFace );
8029 if(nbFaceNodes==6) { // quadratic triangle
8030 SMDS_MeshElement* newElem =
8031 aMesh->AddFace(nodes[3],nodes[4],nodes[5]);
8032 myLastCreatedElems.Append(newElem);
8033 if ( aShapeId && newElem )
8034 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8035 if(theFace->IsMediumNode(nodes[il1])) {
8036 // create quadrangle
8037 newElem = aMesh->AddFace(nodes[0],nodes[1],nodes[3],nodes[5]);
8038 myLastCreatedElems.Append(newElem);
8039 if ( aShapeId && newElem )
8040 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8046 // create quadrangle
8047 newElem = aMesh->AddFace(nodes[1],nodes[2],nodes[3],nodes[5]);
8048 myLastCreatedElems.Append(newElem);
8049 if ( aShapeId && newElem )
8050 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8056 else { // nbFaceNodes==8 - quadratic quadrangle
8057 SMDS_MeshElement* newElem =
8058 aMesh->AddFace(nodes[3],nodes[4],nodes[5]);
8059 myLastCreatedElems.Append(newElem);
8060 if ( aShapeId && newElem )
8061 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8062 newElem = aMesh->AddFace(nodes[5],nodes[6],nodes[7]);
8063 myLastCreatedElems.Append(newElem);
8064 if ( aShapeId && newElem )
8065 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8066 newElem = aMesh->AddFace(nodes[5],nodes[7],nodes[3]);
8067 myLastCreatedElems.Append(newElem);
8068 if ( aShapeId && newElem )
8069 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8070 if(theFace->IsMediumNode(nodes[il1])) {
8071 // create quadrangle
8072 newElem = aMesh->AddFace(nodes[0],nodes[1],nodes[3],nodes[7]);
8073 myLastCreatedElems.Append(newElem);
8074 if ( aShapeId && newElem )
8075 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8081 // create quadrangle
8082 newElem = aMesh->AddFace(nodes[1],nodes[2],nodes[3],nodes[7]);
8083 myLastCreatedElems.Append(newElem);
8084 if ( aShapeId && newElem )
8085 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8091 // create needed triangles using n1,n2,n3 and inserted nodes
8092 int nbn = 2 + aNodesToInsert.size();
8093 //const SMDS_MeshNode* aNodes[nbn];
8094 vector<const SMDS_MeshNode*> aNodes(nbn);
8095 aNodes[0] = nodes[n1];
8096 aNodes[nbn-1] = nodes[n2];
8097 list<const SMDS_MeshNode*>::iterator nIt = aNodesToInsert.begin();
8098 for ( iNode = 1; nIt != aNodesToInsert.end(); nIt++ ) {
8099 aNodes[iNode++] = *nIt;
8101 for(i=1; i<nbn; i++) {
8102 SMDS_MeshElement* newElem =
8103 aMesh->AddFace(aNodes[i-1],aNodes[i],nodes[n3]);
8104 myLastCreatedElems.Append(newElem);
8105 if ( aShapeId && newElem )
8106 aMesh->SetMeshElementOnShape( newElem, aShapeId );
8110 aMesh->RemoveElement(theFace);
8113 //=======================================================================
8114 //function : UpdateVolumes
8116 //=======================================================================
8117 void SMESH_MeshEditor::UpdateVolumes (const SMDS_MeshNode* theBetweenNode1,
8118 const SMDS_MeshNode* theBetweenNode2,
8119 list<const SMDS_MeshNode*>& theNodesToInsert)
8121 myLastCreatedElems.Clear();
8122 myLastCreatedNodes.Clear();
8124 SMDS_ElemIteratorPtr invElemIt = theBetweenNode1->GetInverseElementIterator(SMDSAbs_Volume);
8125 while (invElemIt->more()) { // loop on inverse elements of theBetweenNode1
8126 const SMDS_MeshElement* elem = invElemIt->next();
8128 // check, if current volume has link theBetweenNode1 - theBetweenNode2
8129 SMDS_VolumeTool aVolume (elem);
8130 if (!aVolume.IsLinked(theBetweenNode1, theBetweenNode2))
8133 // insert new nodes in all faces of the volume, sharing link theBetweenNode1 - theBetweenNode2
8134 int iface, nbFaces = aVolume.NbFaces();
8135 vector<const SMDS_MeshNode *> poly_nodes;
8136 vector<int> quantities (nbFaces);
8138 for (iface = 0; iface < nbFaces; iface++) {
8139 int nbFaceNodes = aVolume.NbFaceNodes(iface), nbInserted = 0;
8140 // faceNodes will contain (nbFaceNodes + 1) nodes, last = first
8141 const SMDS_MeshNode** faceNodes = aVolume.GetFaceNodes(iface);
8143 for (int inode = 0; inode < nbFaceNodes; inode++) {
8144 poly_nodes.push_back(faceNodes[inode]);
8146 if (nbInserted == 0) {
8147 if (faceNodes[inode] == theBetweenNode1) {
8148 if (faceNodes[inode + 1] == theBetweenNode2) {
8149 nbInserted = theNodesToInsert.size();
8151 // add nodes to insert
8152 list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.begin();
8153 for (; nIt != theNodesToInsert.end(); nIt++) {
8154 poly_nodes.push_back(*nIt);
8158 else if (faceNodes[inode] == theBetweenNode2) {
8159 if (faceNodes[inode + 1] == theBetweenNode1) {
8160 nbInserted = theNodesToInsert.size();
8162 // add nodes to insert in reversed order
8163 list<const SMDS_MeshNode*>::iterator nIt = theNodesToInsert.end();
8165 for (; nIt != theNodesToInsert.begin(); nIt--) {
8166 poly_nodes.push_back(*nIt);
8168 poly_nodes.push_back(*nIt);
8175 quantities[iface] = nbFaceNodes + nbInserted;
8178 // Replace or update the volume
8179 SMESHDS_Mesh *aMesh = GetMeshDS();
8181 if (elem->IsPoly()) {
8182 aMesh->ChangePolyhedronNodes(elem, poly_nodes, quantities);
8186 int aShapeId = FindShape( elem );
8188 SMDS_MeshElement* newElem =
8189 aMesh->AddPolyhedralVolume(poly_nodes, quantities);
8190 myLastCreatedElems.Append(newElem);
8191 if (aShapeId && newElem)
8192 aMesh->SetMeshElementOnShape(newElem, aShapeId);
8194 aMesh->RemoveElement(elem);
8201 //================================================================================
8203 * \brief Transform any volume into data of SMDSEntity_Polyhedra
8205 //================================================================================
8207 void volumeToPolyhedron( const SMDS_MeshElement* elem,
8208 vector<const SMDS_MeshNode *> & nodes,
8209 vector<int> & nbNodeInFaces )
8212 nbNodeInFaces.clear();
8213 SMDS_VolumeTool vTool ( elem );
8214 for ( int iF = 0; iF < vTool.NbFaces(); ++iF )
8216 const SMDS_MeshNode** fNodes = vTool.GetFaceNodes( iF );
8217 nodes.insert( nodes.end(), fNodes, fNodes + vTool.NbFaceNodes( iF ));
8218 nbNodeInFaces.push_back( vTool.NbFaceNodes( iF ));
8223 //=======================================================================
8225 * \brief Convert elements contained in a submesh to quadratic
8226 * \return int - nb of checked elements
8228 //=======================================================================
8230 int SMESH_MeshEditor::convertElemToQuadratic(SMESHDS_SubMesh * theSm,
8231 SMESH_MesherHelper& theHelper,
8232 const bool theForce3d)
8235 if( !theSm ) return nbElem;
8237 vector<int> nbNodeInFaces;
8238 vector<const SMDS_MeshNode *> nodes;
8239 SMDS_ElemIteratorPtr ElemItr = theSm->GetElements();
8240 while(ElemItr->more())
8243 const SMDS_MeshElement* elem = ElemItr->next();
8244 if( !elem ) continue;
8246 // analyse a necessity of conversion
8247 const SMDSAbs_ElementType aType = elem->GetType();
8248 if ( aType < SMDSAbs_Edge || aType > SMDSAbs_Volume )
8250 const SMDSAbs_EntityType aGeomType = elem->GetEntityType();
8251 bool hasCentralNodes = false;
8252 if ( elem->IsQuadratic() )
8255 switch ( aGeomType ) {
8256 case SMDSEntity_Quad_Triangle:
8257 case SMDSEntity_Quad_Quadrangle:
8258 case SMDSEntity_Quad_Hexa:
8259 alreadyOK = !theHelper.GetIsBiQuadratic(); break;
8261 case SMDSEntity_BiQuad_Triangle:
8262 case SMDSEntity_BiQuad_Quadrangle:
8263 case SMDSEntity_TriQuad_Hexa:
8264 alreadyOK = theHelper.GetIsBiQuadratic();
8265 hasCentralNodes = true;
8270 // take into account already present modium nodes
8272 case SMDSAbs_Volume:
8273 theHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( elem )); break;
8275 theHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( elem )); break;
8277 theHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( elem )); break;
8283 // get elem data needed to re-create it
8285 const int id = elem->GetID();
8286 const int nbNodes = elem->NbCornerNodes();
8287 nodes.assign(elem->begin_nodes(), elem->end_nodes());
8288 if ( aGeomType == SMDSEntity_Polyhedra )
8289 nbNodeInFaces = static_cast<const SMDS_VtkVolume* >( elem )->GetQuantities();
8290 else if ( aGeomType == SMDSEntity_Hexagonal_Prism )
8291 volumeToPolyhedron( elem, nodes, nbNodeInFaces );
8293 // remove a linear element
8294 GetMeshDS()->RemoveFreeElement(elem, theSm, /*fromGroups=*/false);
8296 // remove central nodes of biquadratic elements (biquad->quad convertion)
8297 if ( hasCentralNodes )
8298 for ( size_t i = nbNodes * 2; i < nodes.size(); ++i )
8299 if ( nodes[i]->NbInverseElements() == 0 )
8300 GetMeshDS()->RemoveFreeNode( nodes[i], theSm, /*fromGroups=*/true );
8302 const SMDS_MeshElement* NewElem = 0;
8308 NewElem = theHelper.AddEdge(nodes[0], nodes[1], id, theForce3d);
8316 NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
8319 NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
8322 NewElem = theHelper.AddPolygonalFace(nodes, id, theForce3d);
8326 case SMDSAbs_Volume :
8330 case SMDSEntity_Tetra:
8331 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
8333 case SMDSEntity_Pyramid:
8334 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], id, theForce3d);
8336 case SMDSEntity_Penta:
8337 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], nodes[5], id, theForce3d);
8339 case SMDSEntity_Hexa:
8340 case SMDSEntity_Quad_Hexa:
8341 case SMDSEntity_TriQuad_Hexa:
8342 NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
8343 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
8345 case SMDSEntity_Hexagonal_Prism:
8347 NewElem = theHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
8354 ReplaceElemInGroups( elem, NewElem, GetMeshDS());
8355 if( NewElem && NewElem->getshapeId() < 1 )
8356 theSm->AddElement( NewElem );
8360 //=======================================================================
8361 //function : ConvertToQuadratic
8363 //=======================================================================
8365 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d, const bool theToBiQuad)
8367 SMESHDS_Mesh* meshDS = GetMeshDS();
8369 SMESH_MesherHelper aHelper(*myMesh);
8371 aHelper.SetIsQuadratic( true );
8372 aHelper.SetIsBiQuadratic( theToBiQuad );
8373 aHelper.SetElementsOnShape(true);
8375 // convert elements assigned to sub-meshes
8376 int nbCheckedElems = 0;
8377 if ( myMesh->HasShapeToMesh() )
8379 if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
8381 SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
8382 while ( smIt->more() ) {
8383 SMESH_subMesh* sm = smIt->next();
8384 if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() ) {
8385 aHelper.SetSubShape( sm->GetSubShape() );
8386 nbCheckedElems += convertElemToQuadratic(smDS, aHelper, theForce3d);
8392 // convert elements NOT assigned to sub-meshes
8393 int totalNbElems = meshDS->NbEdges() + meshDS->NbFaces() + meshDS->NbVolumes();
8394 if ( nbCheckedElems < totalNbElems ) // not all elements are in sub-meshes
8396 aHelper.SetElementsOnShape(false);
8397 SMESHDS_SubMesh *smDS = 0;
8400 SMDS_EdgeIteratorPtr aEdgeItr = meshDS->edgesIterator();
8401 while( aEdgeItr->more() )
8403 const SMDS_MeshEdge* edge = aEdgeItr->next();
8404 if ( !edge->IsQuadratic() )
8406 int id = edge->GetID();
8407 const SMDS_MeshNode* n1 = edge->GetNode(0);
8408 const SMDS_MeshNode* n2 = edge->GetNode(1);
8410 meshDS->RemoveFreeElement(edge, smDS, /*fromGroups=*/false);
8412 const SMDS_MeshEdge* NewEdge = aHelper.AddEdge(n1, n2, id, theForce3d);
8413 ReplaceElemInGroups( edge, NewEdge, GetMeshDS());
8417 aHelper.AddTLinks( static_cast< const SMDS_MeshEdge* >( edge ));
8422 SMDS_FaceIteratorPtr aFaceItr = meshDS->facesIterator();
8423 while( aFaceItr->more() )
8425 const SMDS_MeshFace* face = aFaceItr->next();
8426 if ( !face ) continue;
8428 const SMDSAbs_EntityType type = face->GetEntityType();
8432 case SMDSEntity_Quad_Triangle:
8433 case SMDSEntity_Quad_Quadrangle:
8434 alreadyOK = !theToBiQuad;
8435 aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
8437 case SMDSEntity_BiQuad_Triangle:
8438 case SMDSEntity_BiQuad_Quadrangle:
8439 alreadyOK = theToBiQuad;
8440 aHelper.AddTLinks( static_cast< const SMDS_MeshFace* >( face ));
8442 default: alreadyOK = false;
8447 const int id = face->GetID();
8448 vector<const SMDS_MeshNode *> nodes ( face->begin_nodes(), face->end_nodes());
8450 meshDS->RemoveFreeElement(face, smDS, /*fromGroups=*/false);
8452 SMDS_MeshFace * NewFace = 0;
8455 case SMDSEntity_Triangle:
8456 case SMDSEntity_Quad_Triangle:
8457 case SMDSEntity_BiQuad_Triangle:
8458 NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d);
8459 if ( nodes.size() == 7 && nodes[6]->NbInverseElements() == 0 ) // rm a central node
8460 GetMeshDS()->RemoveFreeNode( nodes[6], /*sm=*/0, /*fromGroups=*/true );
8463 case SMDSEntity_Quadrangle:
8464 case SMDSEntity_Quad_Quadrangle:
8465 case SMDSEntity_BiQuad_Quadrangle:
8466 NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
8467 if ( nodes.size() == 9 && nodes[8]->NbInverseElements() == 0 ) // rm a central node
8468 GetMeshDS()->RemoveFreeNode( nodes[8], /*sm=*/0, /*fromGroups=*/true );
8472 NewFace = aHelper.AddPolygonalFace(nodes, id, theForce3d);
8474 ReplaceElemInGroups( face, NewFace, GetMeshDS());
8478 vector<int> nbNodeInFaces;
8479 SMDS_VolumeIteratorPtr aVolumeItr = meshDS->volumesIterator();
8480 while(aVolumeItr->more())
8482 const SMDS_MeshVolume* volume = aVolumeItr->next();
8483 if ( !volume ) continue;
8485 const SMDSAbs_EntityType type = volume->GetEntityType();
8486 if (( theToBiQuad && type == SMDSEntity_TriQuad_Hexa ) ||
8487 ( !theToBiQuad && type == SMDSEntity_Quad_Hexa ))
8489 aHelper.AddTLinks( static_cast< const SMDS_MeshVolume* >( volume ));
8492 const int id = volume->GetID();
8493 vector<const SMDS_MeshNode *> nodes (volume->begin_nodes(), volume->end_nodes());
8494 if ( type == SMDSEntity_Polyhedra )
8495 nbNodeInFaces = static_cast<const SMDS_VtkVolume* >(volume)->GetQuantities();
8496 else if ( type == SMDSEntity_Hexagonal_Prism )
8497 volumeToPolyhedron( volume, nodes, nbNodeInFaces );
8499 meshDS->RemoveFreeElement(volume, smDS, /*fromGroups=*/false);
8501 SMDS_MeshVolume * NewVolume = 0;
8504 case SMDSEntity_Tetra:
8505 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d );
8507 case SMDSEntity_Hexa:
8508 case SMDSEntity_Quad_Hexa:
8509 case SMDSEntity_TriQuad_Hexa:
8510 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
8511 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
8512 for ( size_t i = 20; i < nodes.size(); ++i ) // rm central nodes
8513 if ( nodes[i]->NbInverseElements() == 0 )
8514 GetMeshDS()->RemoveFreeNode( nodes[i], /*sm=*/0, /*fromGroups=*/true );
8516 case SMDSEntity_Pyramid:
8517 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
8518 nodes[3], nodes[4], id, theForce3d);
8520 case SMDSEntity_Penta:
8521 NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2],
8522 nodes[3], nodes[4], nodes[5], id, theForce3d);
8524 case SMDSEntity_Hexagonal_Prism:
8526 NewVolume = aHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d);
8528 ReplaceElemInGroups(volume, NewVolume, meshDS);
8533 { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
8534 // aHelper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
8535 // aHelper.FixQuadraticElements(myError);
8536 SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
8540 //================================================================================
8542 * \brief Makes given elements quadratic
8543 * \param theForce3d - if true, the medium nodes will be placed in the middle of link
8544 * \param theElements - elements to make quadratic
8546 //================================================================================
8548 void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d,
8549 TIDSortedElemSet& theElements,
8550 const bool theToBiQuad)
8552 if ( theElements.empty() ) return;
8554 // we believe that all theElements are of the same type
8555 const SMDSAbs_ElementType elemType = (*theElements.begin())->GetType();
8557 // get all nodes shared by theElements
8558 TIDSortedNodeSet allNodes;
8559 TIDSortedElemSet::iterator eIt = theElements.begin();
8560 for ( ; eIt != theElements.end(); ++eIt )
8561 allNodes.insert( (*eIt)->begin_nodes(), (*eIt)->end_nodes() );
8563 // complete theElements with elements of lower dim whose all nodes are in allNodes
8565 TIDSortedElemSet quadAdjacentElems [ SMDSAbs_NbElementTypes ]; // quadratic adjacent elements
8566 TIDSortedElemSet checkedAdjacentElems [ SMDSAbs_NbElementTypes ];
8567 TIDSortedNodeSet::iterator nIt = allNodes.begin();
8568 for ( ; nIt != allNodes.end(); ++nIt )
8570 const SMDS_MeshNode* n = *nIt;
8571 SMDS_ElemIteratorPtr invIt = n->GetInverseElementIterator();
8572 while ( invIt->more() )
8574 const SMDS_MeshElement* e = invIt->next();
8575 const SMDSAbs_ElementType type = e->GetType();
8576 if ( e->IsQuadratic() )
8578 quadAdjacentElems[ type ].insert( e );
8581 switch ( e->GetEntityType() ) {
8582 case SMDSEntity_Quad_Triangle:
8583 case SMDSEntity_Quad_Quadrangle:
8584 case SMDSEntity_Quad_Hexa: alreadyOK = !theToBiQuad; break;
8585 case SMDSEntity_BiQuad_Triangle:
8586 case SMDSEntity_BiQuad_Quadrangle:
8587 case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; break;
8588 default: alreadyOK = true;
8593 if ( type >= elemType )
8594 continue; // same type or more complex linear element
8596 if ( !checkedAdjacentElems[ type ].insert( e ).second )
8597 continue; // e is already checked
8601 SMDS_NodeIteratorPtr nodeIt = e->nodeIterator();
8602 while ( nodeIt->more() && allIn )
8603 allIn = allNodes.count( nodeIt->next() );
8605 theElements.insert(e );
8609 SMESH_MesherHelper helper(*myMesh);
8610 helper.SetIsQuadratic( true );
8611 helper.SetIsBiQuadratic( theToBiQuad );
8613 // add links of quadratic adjacent elements to the helper
8615 if ( !quadAdjacentElems[SMDSAbs_Edge].empty() )
8616 for ( eIt = quadAdjacentElems[SMDSAbs_Edge].begin();
8617 eIt != quadAdjacentElems[SMDSAbs_Edge].end(); ++eIt )
8619 helper.AddTLinks( static_cast< const SMDS_MeshEdge*> (*eIt) );
8621 if ( !quadAdjacentElems[SMDSAbs_Face].empty() )
8622 for ( eIt = quadAdjacentElems[SMDSAbs_Face].begin();
8623 eIt != quadAdjacentElems[SMDSAbs_Face].end(); ++eIt )
8625 helper.AddTLinks( static_cast< const SMDS_MeshFace*> (*eIt) );
8627 if ( !quadAdjacentElems[SMDSAbs_Volume].empty() )
8628 for ( eIt = quadAdjacentElems[SMDSAbs_Volume].begin();
8629 eIt != quadAdjacentElems[SMDSAbs_Volume].end(); ++eIt )
8631 helper.AddTLinks( static_cast< const SMDS_MeshVolume*> (*eIt) );
8634 // make quadratic (or bi-tri-quadratic) elements instead of linear ones
8636 SMESHDS_Mesh* meshDS = GetMeshDS();
8637 SMESHDS_SubMesh* smDS = 0;
8638 for ( eIt = theElements.begin(); eIt != theElements.end(); ++eIt )
8640 const SMDS_MeshElement* elem = *eIt;
8643 int nbCentralNodes = 0;
8644 switch ( elem->GetEntityType() ) {
8645 // linear convertible
8646 case SMDSEntity_Edge:
8647 case SMDSEntity_Triangle:
8648 case SMDSEntity_Quadrangle:
8649 case SMDSEntity_Tetra:
8650 case SMDSEntity_Pyramid:
8651 case SMDSEntity_Hexa:
8652 case SMDSEntity_Penta: alreadyOK = false; nbCentralNodes = 0; break;
8653 // quadratic that can become bi-quadratic
8654 case SMDSEntity_Quad_Triangle:
8655 case SMDSEntity_Quad_Quadrangle:
8656 case SMDSEntity_Quad_Hexa: alreadyOK =!theToBiQuad; nbCentralNodes = 0; break;
8658 case SMDSEntity_BiQuad_Triangle:
8659 case SMDSEntity_BiQuad_Quadrangle: alreadyOK = theToBiQuad; nbCentralNodes = 1; break;
8660 case SMDSEntity_TriQuad_Hexa: alreadyOK = theToBiQuad; nbCentralNodes = 7; break;
8662 default: alreadyOK = true;
8664 if ( alreadyOK ) continue;
8666 const SMDSAbs_ElementType type = elem->GetType();
8667 const int id = elem->GetID();
8668 const int nbNodes = elem->NbCornerNodes();
8669 vector<const SMDS_MeshNode *> nodes ( elem->begin_nodes(), elem->end_nodes());
8671 helper.SetSubShape( elem->getshapeId() );
8673 if ( !smDS || !smDS->Contains( elem ))
8674 smDS = meshDS->MeshElements( elem->getshapeId() );
8675 meshDS->RemoveFreeElement(elem, smDS, /*fromGroups=*/false);
8677 SMDS_MeshElement * newElem = 0;
8680 case 4: // cases for most frequently used element types go first (for optimization)
8681 if ( type == SMDSAbs_Volume )
8682 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
8684 newElem = helper.AddFace (nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d);
8687 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
8688 nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d);
8691 newElem = helper.AddFace (nodes[0], nodes[1], nodes[2], id, theForce3d);
8694 newElem = helper.AddEdge(nodes[0], nodes[1], id, theForce3d);
8697 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
8698 nodes[4], id, theForce3d);
8701 newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3],
8702 nodes[4], nodes[5], id, theForce3d);
8706 ReplaceElemInGroups( elem, newElem, meshDS);
8707 if( newElem && smDS )
8708 smDS->AddElement( newElem );
8710 // remove central nodes
8711 for ( size_t i = nodes.size() - nbCentralNodes; i < nodes.size(); ++i )
8712 if ( nodes[i]->NbInverseElements() == 0 )
8713 meshDS->RemoveFreeNode( nodes[i], smDS, /*fromGroups=*/true );
8715 } // loop on theElements
8718 { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion
8719 // helper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh
8720 // helper.FixQuadraticElements( myError );
8721 SMESH_MesherHelper( *myMesh ).FixQuadraticElements(myError);
8725 //=======================================================================
8727 * \brief Convert quadratic elements to linear ones and remove quadratic nodes
8728 * \return int - nb of checked elements
8730 //=======================================================================
8732 int SMESH_MeshEditor::removeQuadElem(SMESHDS_SubMesh * theSm,
8733 SMDS_ElemIteratorPtr theItr,
8734 const int theShapeID)
8737 SMESHDS_Mesh* meshDS = GetMeshDS();
8739 while( theItr->more() )
8741 const SMDS_MeshElement* elem = theItr->next();
8743 if( elem && elem->IsQuadratic())
8745 int id = elem->GetID();
8746 int nbCornerNodes = elem->NbCornerNodes();
8747 SMDSAbs_ElementType aType = elem->GetType();
8749 vector<const SMDS_MeshNode *> nodes( elem->begin_nodes(), elem->end_nodes() );
8751 //remove a quadratic element
8752 if ( !theSm || !theSm->Contains( elem ))
8753 theSm = meshDS->MeshElements( elem->getshapeId() );
8754 meshDS->RemoveFreeElement( elem, theSm, /*fromGroups=*/false );
8756 // remove medium nodes
8757 for ( unsigned i = nbCornerNodes; i < nodes.size(); ++i )
8758 if ( nodes[i]->NbInverseElements() == 0 )
8759 meshDS->RemoveFreeNode( nodes[i], theSm );
8761 // add a linear element
8762 nodes.resize( nbCornerNodes );
8763 SMDS_MeshElement * newElem = AddElement( nodes, aType, false, id );
8764 ReplaceElemInGroups(elem, newElem, meshDS);
8765 if( theSm && newElem )
8766 theSm->AddElement( newElem );
8772 //=======================================================================
8773 //function : ConvertFromQuadratic
8775 //=======================================================================
8777 bool SMESH_MeshEditor::ConvertFromQuadratic()
8779 int nbCheckedElems = 0;
8780 if ( myMesh->HasShapeToMesh() )
8782 if ( SMESH_subMesh *aSubMesh = myMesh->GetSubMeshContaining(myMesh->GetShapeToMesh()))
8784 SMESH_subMeshIteratorPtr smIt = aSubMesh->getDependsOnIterator(true,false);
8785 while ( smIt->more() ) {
8786 SMESH_subMesh* sm = smIt->next();
8787 if ( SMESHDS_SubMesh *smDS = sm->GetSubMeshDS() )
8788 nbCheckedElems += removeQuadElem( smDS, smDS->GetElements(), sm->GetId() );
8794 GetMeshDS()->NbEdges() + GetMeshDS()->NbFaces() + GetMeshDS()->NbVolumes();
8795 if ( nbCheckedElems < totalNbElems ) // not all elements are in submeshes
8797 SMESHDS_SubMesh *aSM = 0;
8798 removeQuadElem( aSM, GetMeshDS()->elementsIterator(), 0 );
8806 //================================================================================
8808 * \brief Return true if all medium nodes of the element are in the node set
8810 //================================================================================
8812 bool allMediumNodesIn(const SMDS_MeshElement* elem, TIDSortedNodeSet& nodeSet )
8814 for ( int i = elem->NbCornerNodes(); i < elem->NbNodes(); ++i )
8815 if ( !nodeSet.count( elem->GetNode(i) ))
8821 //================================================================================
8823 * \brief Makes given elements linear
8825 //================================================================================
8827 void SMESH_MeshEditor::ConvertFromQuadratic(TIDSortedElemSet& theElements)
8829 if ( theElements.empty() ) return;
8831 // collect IDs of medium nodes of theElements; some of these nodes will be removed
8832 set<int> mediumNodeIDs;
8833 TIDSortedElemSet::iterator eIt = theElements.begin();
8834 for ( ; eIt != theElements.end(); ++eIt )
8836 const SMDS_MeshElement* e = *eIt;
8837 for ( int i = e->NbCornerNodes(); i < e->NbNodes(); ++i )
8838 mediumNodeIDs.insert( e->GetNode(i)->GetID() );
8841 // replace given elements by linear ones
8842 SMDS_ElemIteratorPtr elemIt = elemSetIterator( theElements );
8843 removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
8845 // we need to convert remaining elements whose all medium nodes are in mediumNodeIDs
8846 // except those elements sharing medium nodes of quadratic element whose medium nodes
8847 // are not all in mediumNodeIDs
8849 // get remaining medium nodes
8850 TIDSortedNodeSet mediumNodes;
8851 set<int>::iterator nIdsIt = mediumNodeIDs.begin();
8852 for ( ; nIdsIt != mediumNodeIDs.end(); ++nIdsIt )
8853 if ( const SMDS_MeshNode* n = GetMeshDS()->FindNode( *nIdsIt ))
8854 mediumNodes.insert( mediumNodes.end(), n );
8856 // find more quadratic elements to convert
8857 TIDSortedElemSet moreElemsToConvert;
8858 TIDSortedNodeSet::iterator nIt = mediumNodes.begin();
8859 for ( ; nIt != mediumNodes.end(); ++nIt )
8861 SMDS_ElemIteratorPtr invIt = (*nIt)->GetInverseElementIterator();
8862 while ( invIt->more() )
8864 const SMDS_MeshElement* e = invIt->next();
8865 if ( e->IsQuadratic() && allMediumNodesIn( e, mediumNodes ))
8867 // find a more complex element including e and
8868 // whose medium nodes are not in mediumNodes
8869 bool complexFound = false;
8870 for ( int type = e->GetType() + 1; type < SMDSAbs_0DElement; ++type )
8872 SMDS_ElemIteratorPtr invIt2 =
8873 (*nIt)->GetInverseElementIterator( SMDSAbs_ElementType( type ));
8874 while ( invIt2->more() )
8876 const SMDS_MeshElement* eComplex = invIt2->next();
8877 if ( eComplex->IsQuadratic() && !allMediumNodesIn( eComplex, mediumNodes))
8879 int nbCommonNodes = SMESH_MeshAlgos::GetCommonNodes( e, eComplex ).size();
8880 if ( nbCommonNodes == e->NbNodes())
8882 complexFound = true;
8883 type = SMDSAbs_NbElementTypes; // to quit from the outer loop
8889 if ( !complexFound )
8890 moreElemsToConvert.insert( e );
8894 elemIt = elemSetIterator( moreElemsToConvert );
8895 removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 );
8898 //=======================================================================
8899 //function : SewSideElements
8901 //=======================================================================
8903 SMESH_MeshEditor::Sew_Error
8904 SMESH_MeshEditor::SewSideElements (TIDSortedElemSet& theSide1,
8905 TIDSortedElemSet& theSide2,
8906 const SMDS_MeshNode* theFirstNode1,
8907 const SMDS_MeshNode* theFirstNode2,
8908 const SMDS_MeshNode* theSecondNode1,
8909 const SMDS_MeshNode* theSecondNode2)
8911 myLastCreatedElems.Clear();
8912 myLastCreatedNodes.Clear();
8914 MESSAGE ("::::SewSideElements()");
8915 if ( theSide1.size() != theSide2.size() )
8916 return SEW_DIFF_NB_OF_ELEMENTS;
8918 Sew_Error aResult = SEW_OK;
8920 // 1. Build set of faces representing each side
8921 // 2. Find which nodes of the side 1 to merge with ones on the side 2
8922 // 3. Replace nodes in elements of the side 1 and remove replaced nodes
8924 // =======================================================================
8925 // 1. Build set of faces representing each side:
8926 // =======================================================================
8927 // a. build set of nodes belonging to faces
8928 // b. complete set of faces: find missing faces whose nodes are in set of nodes
8929 // c. create temporary faces representing side of volumes if correspondent
8930 // face does not exist
8932 SMESHDS_Mesh* aMesh = GetMeshDS();
8933 // TODO algoritm not OK with vtkUnstructuredGrid: 2 meshes can't share nodes
8934 //SMDS_Mesh aTmpFacesMesh; // try to use the same mesh
8935 TIDSortedElemSet faceSet1, faceSet2;
8936 set<const SMDS_MeshElement*> volSet1, volSet2;
8937 set<const SMDS_MeshNode*> nodeSet1, nodeSet2;
8938 TIDSortedElemSet * faceSetPtr[] = { &faceSet1, &faceSet2 };
8939 set<const SMDS_MeshElement*> * volSetPtr[] = { &volSet1, &volSet2 };
8940 set<const SMDS_MeshNode*> * nodeSetPtr[] = { &nodeSet1, &nodeSet2 };
8941 TIDSortedElemSet * elemSetPtr[] = { &theSide1, &theSide2 };
8942 int iSide, iFace, iNode;
8944 list<const SMDS_MeshElement* > tempFaceList;
8945 for ( iSide = 0; iSide < 2; iSide++ ) {
8946 set<const SMDS_MeshNode*> * nodeSet = nodeSetPtr[ iSide ];
8947 TIDSortedElemSet * elemSet = elemSetPtr[ iSide ];
8948 TIDSortedElemSet * faceSet = faceSetPtr[ iSide ];
8949 set<const SMDS_MeshElement*> * volSet = volSetPtr [ iSide ];
8950 set<const SMDS_MeshElement*>::iterator vIt;
8951 TIDSortedElemSet::iterator eIt;
8952 set<const SMDS_MeshNode*>::iterator nIt;
8954 // check that given nodes belong to given elements
8955 const SMDS_MeshNode* n1 = ( iSide == 0 ) ? theFirstNode1 : theFirstNode2;
8956 const SMDS_MeshNode* n2 = ( iSide == 0 ) ? theSecondNode1 : theSecondNode2;
8957 int firstIndex = -1, secondIndex = -1;
8958 for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
8959 const SMDS_MeshElement* elem = *eIt;
8960 if ( firstIndex < 0 ) firstIndex = elem->GetNodeIndex( n1 );
8961 if ( secondIndex < 0 ) secondIndex = elem->GetNodeIndex( n2 );
8962 if ( firstIndex > -1 && secondIndex > -1 ) break;
8964 if ( firstIndex < 0 || secondIndex < 0 ) {
8965 // we can simply return until temporary faces created
8966 return (iSide == 0 ) ? SEW_BAD_SIDE1_NODES : SEW_BAD_SIDE2_NODES;
8969 // -----------------------------------------------------------
8970 // 1a. Collect nodes of existing faces
8971 // and build set of face nodes in order to detect missing
8972 // faces corresponding to sides of volumes
8973 // -----------------------------------------------------------
8975 set< set <const SMDS_MeshNode*> > setOfFaceNodeSet;
8977 // loop on the given element of a side
8978 for (eIt = elemSet->begin(); eIt != elemSet->end(); eIt++ ) {
8979 //const SMDS_MeshElement* elem = *eIt;
8980 const SMDS_MeshElement* elem = *eIt;
8981 if ( elem->GetType() == SMDSAbs_Face ) {
8982 faceSet->insert( elem );
8983 set <const SMDS_MeshNode*> faceNodeSet;
8984 SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator();
8985 while ( nodeIt->more() ) {
8986 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
8987 nodeSet->insert( n );
8988 faceNodeSet.insert( n );
8990 setOfFaceNodeSet.insert( faceNodeSet );
8992 else if ( elem->GetType() == SMDSAbs_Volume )
8993 volSet->insert( elem );
8995 // ------------------------------------------------------------------------------
8996 // 1b. Complete set of faces: find missing faces whose nodes are in set of nodes
8997 // ------------------------------------------------------------------------------
8999 for ( nIt = nodeSet->begin(); nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
9000 SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
9001 while ( fIt->more() ) { // loop on faces sharing a node
9002 const SMDS_MeshElement* f = fIt->next();
9003 if ( faceSet->find( f ) == faceSet->end() ) {
9004 // check if all nodes are in nodeSet and
9005 // complete setOfFaceNodeSet if they are
9006 set <const SMDS_MeshNode*> faceNodeSet;
9007 SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
9008 bool allInSet = true;
9009 while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
9010 const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9011 if ( nodeSet->find( n ) == nodeSet->end() )
9014 faceNodeSet.insert( n );
9017 faceSet->insert( f );
9018 setOfFaceNodeSet.insert( faceNodeSet );
9024 // -------------------------------------------------------------------------
9025 // 1c. Create temporary faces representing sides of volumes if correspondent
9026 // face does not exist
9027 // -------------------------------------------------------------------------
9029 if ( !volSet->empty() ) {
9030 //int nodeSetSize = nodeSet->size();
9032 // loop on given volumes
9033 for ( vIt = volSet->begin(); vIt != volSet->end(); vIt++ ) {
9034 SMDS_VolumeTool vol (*vIt);
9035 // loop on volume faces: find free faces
9036 // --------------------------------------
9037 list<const SMDS_MeshElement* > freeFaceList;
9038 for ( iFace = 0; iFace < vol.NbFaces(); iFace++ ) {
9039 if ( !vol.IsFreeFace( iFace ))
9041 // check if there is already a face with same nodes in a face set
9042 const SMDS_MeshElement* aFreeFace = 0;
9043 const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iFace );
9044 int nbNodes = vol.NbFaceNodes( iFace );
9045 set <const SMDS_MeshNode*> faceNodeSet;
9046 vol.GetFaceNodes( iFace, faceNodeSet );
9047 bool isNewFace = setOfFaceNodeSet.insert( faceNodeSet ).second;
9049 // no such a face is given but it still can exist, check it
9050 vector<const SMDS_MeshNode *> nodes ( fNodes, fNodes + nbNodes);
9051 aFreeFace = aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false );
9054 // create a temporary face
9055 if ( nbNodes == 3 ) {
9056 //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2] );
9057 aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2] );
9059 else if ( nbNodes == 4 ) {
9060 //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
9061 aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] );
9064 vector<const SMDS_MeshNode *> poly_nodes ( fNodes, & fNodes[nbNodes]);
9065 //aFreeFace = aTmpFacesMesh.AddPolygonalFace(poly_nodes);
9066 aFreeFace = aMesh->AddPolygonalFace(poly_nodes);
9069 tempFaceList.push_back( aFreeFace );
9073 freeFaceList.push_back( aFreeFace );
9075 } // loop on faces of a volume
9077 // choose one of several free faces of a volume
9078 // --------------------------------------------
9079 if ( freeFaceList.size() > 1 ) {
9080 // choose a face having max nb of nodes shared by other elems of a side
9081 int maxNbNodes = -1;
9082 list<const SMDS_MeshElement* >::iterator fIt = freeFaceList.begin();
9083 while ( fIt != freeFaceList.end() ) { // loop on free faces
9084 int nbSharedNodes = 0;
9085 SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
9086 while ( nodeIt->more() ) { // loop on free face nodes
9087 const SMDS_MeshNode* n =
9088 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9089 SMDS_ElemIteratorPtr invElemIt = n->GetInverseElementIterator();
9090 while ( invElemIt->more() ) {
9091 const SMDS_MeshElement* e = invElemIt->next();
9092 nbSharedNodes += faceSet->count( e );
9093 nbSharedNodes += elemSet->count( e );
9096 if ( nbSharedNodes > maxNbNodes ) {
9097 maxNbNodes = nbSharedNodes;
9098 freeFaceList.erase( freeFaceList.begin(), fIt++ );
9100 else if ( nbSharedNodes == maxNbNodes ) {
9104 freeFaceList.erase( fIt++ ); // here fIt++ occurs before erase
9107 if ( freeFaceList.size() > 1 )
9109 // could not choose one face, use another way
9110 // choose a face most close to the bary center of the opposite side
9111 gp_XYZ aBC( 0., 0., 0. );
9112 set <const SMDS_MeshNode*> addedNodes;
9113 TIDSortedElemSet * elemSet2 = elemSetPtr[ 1 - iSide ];
9114 eIt = elemSet2->begin();
9115 for ( eIt = elemSet2->begin(); eIt != elemSet2->end(); eIt++ ) {
9116 SMDS_ElemIteratorPtr nodeIt = (*eIt)->nodesIterator();
9117 while ( nodeIt->more() ) { // loop on free face nodes
9118 const SMDS_MeshNode* n =
9119 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9120 if ( addedNodes.insert( n ).second )
9121 aBC += gp_XYZ( n->X(),n->Y(),n->Z() );
9124 aBC /= addedNodes.size();
9125 double minDist = DBL_MAX;
9126 fIt = freeFaceList.begin();
9127 while ( fIt != freeFaceList.end() ) { // loop on free faces
9129 SMDS_ElemIteratorPtr nodeIt = (*fIt)->nodesIterator();
9130 while ( nodeIt->more() ) { // loop on free face nodes
9131 const SMDS_MeshNode* n =
9132 static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9133 gp_XYZ p( n->X(),n->Y(),n->Z() );
9134 dist += ( aBC - p ).SquareModulus();
9136 if ( dist < minDist ) {
9138 freeFaceList.erase( freeFaceList.begin(), fIt++ );
9141 fIt = freeFaceList.erase( fIt++ );
9144 } // choose one of several free faces of a volume
9146 if ( freeFaceList.size() == 1 ) {
9147 const SMDS_MeshElement* aFreeFace = freeFaceList.front();
9148 faceSet->insert( aFreeFace );
9149 // complete a node set with nodes of a found free face
9150 // for ( iNode = 0; iNode < ; iNode++ )
9151 // nodeSet->insert( fNodes[ iNode ] );
9154 } // loop on volumes of a side
9156 // // complete a set of faces if new nodes in a nodeSet appeared
9157 // // ----------------------------------------------------------
9158 // if ( nodeSetSize != nodeSet->size() ) {
9159 // for ( ; nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide
9160 // SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face);
9161 // while ( fIt->more() ) { // loop on faces sharing a node
9162 // const SMDS_MeshElement* f = fIt->next();
9163 // if ( faceSet->find( f ) == faceSet->end() ) {
9164 // // check if all nodes are in nodeSet and
9165 // // complete setOfFaceNodeSet if they are
9166 // set <const SMDS_MeshNode*> faceNodeSet;
9167 // SMDS_ElemIteratorPtr nodeIt = f->nodesIterator();
9168 // bool allInSet = true;
9169 // while ( nodeIt->more() && allInSet ) { // loop on nodes of a face
9170 // const SMDS_MeshNode* n = static_cast<const SMDS_MeshNode*>( nodeIt->next() );
9171 // if ( nodeSet->find( n ) == nodeSet->end() )
9172 // allInSet = false;
9174 // faceNodeSet.insert( n );
9176 // if ( allInSet ) {
9177 // faceSet->insert( f );
9178 // setOfFaceNodeSet.insert( faceNodeSet );
9184 } // Create temporary faces, if there are volumes given
9187 if ( faceSet1.size() != faceSet2.size() ) {
9188 // delete temporary faces: they are in reverseElements of actual nodes
9189 // SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
9190 // while ( tmpFaceIt->more() )
9191 // aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
9192 // list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
9193 // for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
9194 // aMesh->RemoveElement(*tmpFaceIt);
9195 MESSAGE("Diff nb of faces");
9196 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9199 // ============================================================
9200 // 2. Find nodes to merge:
9201 // bind a node to remove to a node to put instead
9202 // ============================================================
9204 TNodeNodeMap nReplaceMap; // bind a node to remove to a node to put instead
9205 if ( theFirstNode1 != theFirstNode2 )
9206 nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
9207 if ( theSecondNode1 != theSecondNode2 )
9208 nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
9210 LinkID_Gen aLinkID_Gen( GetMeshDS() );
9211 set< long > linkIdSet; // links to process
9212 linkIdSet.insert( aLinkID_Gen.GetLinkID( theFirstNode1, theSecondNode1 ));
9214 typedef pair< const SMDS_MeshNode*, const SMDS_MeshNode* > NLink;
9215 list< NLink > linkList[2];
9216 linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
9217 linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
9218 // loop on links in linkList; find faces by links and append links
9219 // of the found faces to linkList
9220 list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
9221 for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ )
9223 NLink link[] = { *linkIt[0], *linkIt[1] };
9224 long linkID = aLinkID_Gen.GetLinkID( link[0].first, link[0].second );
9225 if ( !linkIdSet.count( linkID ) )
9228 // by links, find faces in the face sets,
9229 // and find indices of link nodes in the found faces;
9230 // in a face set, there is only one or no face sharing a link
9231 // ---------------------------------------------------------------
9233 const SMDS_MeshElement* face[] = { 0, 0 };
9234 vector<const SMDS_MeshNode*> fnodes[2];
9235 int iLinkNode[2][2];
9236 TIDSortedElemSet avoidSet;
9237 for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
9238 const SMDS_MeshNode* n1 = link[iSide].first;
9239 const SMDS_MeshNode* n2 = link[iSide].second;
9240 //cout << "Side " << iSide << " ";
9241 //cout << "L( " << n1->GetID() << ", " << n2->GetID() << " ) " << endl;
9242 // find a face by two link nodes
9243 face[ iSide ] = SMESH_MeshAlgos::FindFaceInSet( n1, n2,
9244 *faceSetPtr[ iSide ], avoidSet,
9245 &iLinkNode[iSide][0],
9246 &iLinkNode[iSide][1] );
9249 //cout << " F " << face[ iSide]->GetID() <<endl;
9250 faceSetPtr[ iSide ]->erase( face[ iSide ]);
9251 // put face nodes to fnodes
9252 if ( face[ iSide ]->IsQuadratic() )
9254 // use interlaced nodes iterator
9255 const SMDS_VtkFace* F = dynamic_cast<const SMDS_VtkFace*>( face[ iSide ]);
9256 if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace"));
9257 SMDS_ElemIteratorPtr nIter = F->interlacedNodesElemIterator();
9258 while ( nIter->more() )
9259 fnodes[ iSide ].push_back( cast2Node( nIter->next() ));
9263 fnodes[ iSide ].assign( face[ iSide ]->begin_nodes(),
9264 face[ iSide ]->end_nodes() );
9266 fnodes[ iSide ].push_back( fnodes[ iSide ].front());
9270 // check similarity of elements of the sides
9271 if (aResult == SEW_OK && (( face[0] && !face[1] ) || ( !face[0] && face[1] ))) {
9272 MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
9273 if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
9274 aResult = ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
9277 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9279 break; // do not return because it's necessary to remove tmp faces
9282 // set nodes to merge
9283 // -------------------
9285 if ( face[0] && face[1] ) {
9286 const int nbNodes = face[0]->NbNodes();
9287 if ( nbNodes != face[1]->NbNodes() ) {
9288 MESSAGE("Diff nb of face nodes");
9289 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9290 break; // do not return because it s necessary to remove tmp faces
9292 bool reverse[] = { false, false }; // order of nodes in the link
9293 for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides
9294 // analyse link orientation in faces
9295 int i1 = iLinkNode[ iSide ][ 0 ];
9296 int i2 = iLinkNode[ iSide ][ 1 ];
9297 reverse[ iSide ] = Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1;
9299 int di1 = reverse[0] ? -1 : +1, i1 = iLinkNode[0][1] + di1;
9300 int di2 = reverse[1] ? -1 : +1, i2 = iLinkNode[1][1] + di2;
9301 for ( int i = nbNodes - 2; i > 0; --i, i1 += di1, i2 += di2 )
9303 nReplaceMap.insert ( make_pair ( fnodes[0][ ( i1 + nbNodes ) % nbNodes ],
9304 fnodes[1][ ( i2 + nbNodes ) % nbNodes ]));
9307 // add other links of the faces to linkList
9308 // -----------------------------------------
9310 for ( iNode = 0; iNode < nbNodes; iNode++ ) {
9311 linkID = aLinkID_Gen.GetLinkID( fnodes[0][iNode], fnodes[0][iNode+1] );
9312 pair< set<long>::iterator, bool > iter_isnew = linkIdSet.insert( linkID );
9313 if ( !iter_isnew.second ) { // already in a set: no need to process
9314 linkIdSet.erase( iter_isnew.first );
9316 else // new in set == encountered for the first time: add
9318 const SMDS_MeshNode* n1 = fnodes[0][ iNode ];
9319 const SMDS_MeshNode* n2 = fnodes[0][ iNode + 1];
9320 linkList[0].push_back ( NLink( n1, n2 ));
9321 linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
9326 if ( faceSetPtr[0]->empty() || faceSetPtr[1]->empty() )
9329 } // loop on link lists
9331 if ( aResult == SEW_OK &&
9332 ( //linkIt[0] != linkList[0].end() ||
9333 !faceSetPtr[0]->empty() || !faceSetPtr[1]->empty() )) {
9334 MESSAGE( (linkIt[0] != linkList[0].end()) <<" "<< (faceSetPtr[0]->empty()) <<
9335 " " << (faceSetPtr[1]->empty()));
9336 aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9339 // ====================================================================
9340 // 3. Replace nodes in elements of the side 1 and remove replaced nodes
9341 // ====================================================================
9343 // delete temporary faces
9344 // SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator();
9345 // while ( tmpFaceIt->more() )
9346 // aTmpFacesMesh.RemoveElement( tmpFaceIt->next() );
9347 list<const SMDS_MeshElement* >::iterator tmpFaceIt = tempFaceList.begin();
9348 for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt)
9349 aMesh->RemoveElement(*tmpFaceIt);
9351 if ( aResult != SEW_OK)
9354 list< int > nodeIDsToRemove/*, elemIDsToRemove*/;
9355 // loop on nodes replacement map
9356 TNodeNodeMap::iterator nReplaceMapIt = nReplaceMap.begin(), nnIt;
9357 for ( ; nReplaceMapIt != nReplaceMap.end(); nReplaceMapIt++ )
9358 if ( (*nReplaceMapIt).first != (*nReplaceMapIt).second ) {
9359 const SMDS_MeshNode* nToRemove = (*nReplaceMapIt).first;
9360 nodeIDsToRemove.push_back( nToRemove->GetID() );
9361 // loop on elements sharing nToRemove
9362 SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator();
9363 while ( invElemIt->more() ) {
9364 const SMDS_MeshElement* e = invElemIt->next();
9365 // get a new suite of nodes: make replacement
9366 int nbReplaced = 0, i = 0, nbNodes = e->NbNodes();
9367 vector< const SMDS_MeshNode*> nodes( nbNodes );
9368 SMDS_ElemIteratorPtr nIt = e->nodesIterator();
9369 while ( nIt->more() ) {
9370 const SMDS_MeshNode* n =
9371 static_cast<const SMDS_MeshNode*>( nIt->next() );
9372 nnIt = nReplaceMap.find( n );
9373 if ( nnIt != nReplaceMap.end() ) {
9379 // if ( nbReplaced == nbNodes && e->GetType() == SMDSAbs_Face )
9380 // elemIDsToRemove.push_back( e->GetID() );
9384 SMDSAbs_ElementType etyp = e->GetType();
9385 SMDS_MeshElement* newElem = this->AddElement(nodes, etyp, false);
9388 myLastCreatedElems.Append(newElem);
9389 AddToSameGroups(newElem, e, aMesh);
9390 int aShapeId = e->getshapeId();
9393 aMesh->SetMeshElementOnShape( newElem, aShapeId );
9396 aMesh->RemoveElement(e);
9401 Remove( nodeIDsToRemove, true );
9406 //================================================================================
9408 * \brief Find corresponding nodes in two sets of faces
9409 * \param theSide1 - first face set
9410 * \param theSide2 - second first face
9411 * \param theFirstNode1 - a boundary node of set 1
9412 * \param theFirstNode2 - a node of set 2 corresponding to theFirstNode1
9413 * \param theSecondNode1 - a boundary node of set 1 linked with theFirstNode1
9414 * \param theSecondNode2 - a node of set 2 corresponding to theSecondNode1
9415 * \param nReplaceMap - output map of corresponding nodes
9416 * \return bool - is a success or not
9418 //================================================================================
9421 //#define DEBUG_MATCHING_NODES
9424 SMESH_MeshEditor::Sew_Error
9425 SMESH_MeshEditor::FindMatchingNodes(set<const SMDS_MeshElement*>& theSide1,
9426 set<const SMDS_MeshElement*>& theSide2,
9427 const SMDS_MeshNode* theFirstNode1,
9428 const SMDS_MeshNode* theFirstNode2,
9429 const SMDS_MeshNode* theSecondNode1,
9430 const SMDS_MeshNode* theSecondNode2,
9431 TNodeNodeMap & nReplaceMap)
9433 set<const SMDS_MeshElement*> * faceSetPtr[] = { &theSide1, &theSide2 };
9435 nReplaceMap.clear();
9436 if ( theFirstNode1 != theFirstNode2 )
9437 nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 ));
9438 if ( theSecondNode1 != theSecondNode2 )
9439 nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 ));
9441 set< SMESH_TLink > linkSet; // set of nodes where order of nodes is ignored
9442 linkSet.insert( SMESH_TLink( theFirstNode1, theSecondNode1 ));
9444 list< NLink > linkList[2];
9445 linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 ));
9446 linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 ));
9448 // loop on links in linkList; find faces by links and append links
9449 // of the found faces to linkList
9450 list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ;
9451 for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ ) {
9452 NLink link[] = { *linkIt[0], *linkIt[1] };
9453 if ( linkSet.find( link[0] ) == linkSet.end() )
9456 // by links, find faces in the face sets,
9457 // and find indices of link nodes in the found faces;
9458 // in a face set, there is only one or no face sharing a link
9459 // ---------------------------------------------------------------
9461 const SMDS_MeshElement* face[] = { 0, 0 };
9462 list<const SMDS_MeshNode*> notLinkNodes[2];
9463 //bool reverse[] = { false, false }; // order of notLinkNodes
9465 for ( int iSide = 0; iSide < 2; iSide++ ) // loop on 2 sides
9467 const SMDS_MeshNode* n1 = link[iSide].first;
9468 const SMDS_MeshNode* n2 = link[iSide].second;
9469 set<const SMDS_MeshElement*> * faceSet = faceSetPtr[ iSide ];
9470 set< const SMDS_MeshElement* > facesOfNode1;
9471 for ( int iNode = 0; iNode < 2; iNode++ ) // loop on 2 nodes of a link
9473 // during a loop of the first node, we find all faces around n1,
9474 // during a loop of the second node, we find one face sharing both n1 and n2
9475 const SMDS_MeshNode* n = iNode ? n1 : n2; // a node of a link
9476 SMDS_ElemIteratorPtr fIt = n->GetInverseElementIterator(SMDSAbs_Face);
9477 while ( fIt->more() ) { // loop on faces sharing a node
9478 const SMDS_MeshElement* f = fIt->next();
9479 if (faceSet->find( f ) != faceSet->end() && // f is in face set
9480 ! facesOfNode1.insert( f ).second ) // f encounters twice
9482 if ( face[ iSide ] ) {
9483 MESSAGE( "2 faces per link " );
9484 return ( iSide ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
9487 faceSet->erase( f );
9489 // get not link nodes
9490 int nbN = f->NbNodes();
9491 if ( f->IsQuadratic() )
9493 nbNodes[ iSide ] = nbN;
9494 list< const SMDS_MeshNode* > & nodes = notLinkNodes[ iSide ];
9495 int i1 = f->GetNodeIndex( n1 );
9496 int i2 = f->GetNodeIndex( n2 );
9497 int iEnd = nbN, iBeg = -1, iDelta = 1;
9498 bool reverse = ( Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1 );
9500 std::swap( iEnd, iBeg ); iDelta = -1;
9505 if ( i == iEnd ) i = iBeg + iDelta;
9506 if ( i == i1 ) break;
9507 nodes.push_back ( f->GetNode( i ) );
9513 // check similarity of elements of the sides
9514 if (( face[0] && !face[1] ) || ( !face[0] && face[1] )) {
9515 MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 ));
9516 if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found
9517 return ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES );
9520 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9524 // set nodes to merge
9525 // -------------------
9527 if ( face[0] && face[1] ) {
9528 if ( nbNodes[0] != nbNodes[1] ) {
9529 MESSAGE("Diff nb of face nodes");
9530 return SEW_TOPO_DIFF_SETS_OF_ELEMENTS;
9532 #ifdef DEBUG_MATCHING_NODES
9533 MESSAGE ( " Link 1: " << link[0].first->GetID() <<" "<< link[0].second->GetID()
9534 << " F 1: " << face[0] << "| Link 2: " << link[1].first->GetID() <<" "
9535 << link[1].second->GetID() << " F 2: " << face[1] << " | Bind: " ) ;
9537 int nbN = nbNodes[0];
9539 list<const SMDS_MeshNode*>::iterator n1 = notLinkNodes[0].begin();
9540 list<const SMDS_MeshNode*>::iterator n2 = notLinkNodes[1].begin();
9541 for ( int i = 0 ; i < nbN - 2; ++i ) {
9542 #ifdef DEBUG_MATCHING_NODES
9543 MESSAGE ( (*n1)->GetID() << " to " << (*n2)->GetID() );
9545 nReplaceMap.insert( make_pair( *(n1++), *(n2++) ));
9549 // add other links of the face 1 to linkList
9550 // -----------------------------------------
9552 const SMDS_MeshElement* f0 = face[0];
9553 const SMDS_MeshNode* n1 = f0->GetNode( nbN - 1 );
9554 for ( int i = 0; i < nbN; i++ )
9556 const SMDS_MeshNode* n2 = f0->GetNode( i );
9557 pair< set< SMESH_TLink >::iterator, bool > iter_isnew =
9558 linkSet.insert( SMESH_TLink( n1, n2 ));
9559 if ( !iter_isnew.second ) { // already in a set: no need to process
9560 linkSet.erase( iter_isnew.first );
9562 else // new in set == encountered for the first time: add
9564 #ifdef DEBUG_MATCHING_NODES
9565 MESSAGE ( "Add link 1: " << n1->GetID() << " " << n2->GetID() << " "
9566 << " | link 2: " << nReplaceMap[n1]->GetID() << " " << nReplaceMap[n2]->GetID() << " " );
9568 linkList[0].push_back ( NLink( n1, n2 ));
9569 linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] ));
9574 } // loop on link lists
9579 //================================================================================
9581 * \brief Create elements equal (on same nodes) to given ones
9582 * \param [in] theElements - a set of elems to duplicate. If it is empty, all
9583 * elements of the uppest dimension are duplicated.
9585 //================================================================================
9587 void SMESH_MeshEditor::DoubleElements( const TIDSortedElemSet& theElements )
9590 SMESHDS_Mesh* mesh = GetMeshDS();
9592 // get an element type and an iterator over elements
9594 SMDSAbs_ElementType type;
9595 SMDS_ElemIteratorPtr elemIt;
9596 vector< const SMDS_MeshElement* > allElems;
9597 if ( theElements.empty() )
9599 if ( mesh->NbNodes() == 0 )
9601 // get most complex type
9602 SMDSAbs_ElementType types[SMDSAbs_NbElementTypes] = {
9603 SMDSAbs_Volume, SMDSAbs_Face, SMDSAbs_Edge,
9604 SMDSAbs_0DElement, SMDSAbs_Ball, SMDSAbs_Node
9606 for ( int i = 0; i < SMDSAbs_NbElementTypes; ++i )
9607 if ( mesh->GetMeshInfo().NbElements( types[i] ))
9612 // put all elements in the vector <allElems>
9613 allElems.reserve( mesh->GetMeshInfo().NbElements( type ));
9614 elemIt = mesh->elementsIterator( type );
9615 while ( elemIt->more() )
9616 allElems.push_back( elemIt->next());
9617 elemIt = elemSetIterator( allElems );
9621 type = (*theElements.begin())->GetType();
9622 elemIt = elemSetIterator( theElements );
9625 // duplicate elements
9627 if ( type == SMDSAbs_Ball )
9629 SMDS_UnstructuredGrid* vtkGrid = mesh->getGrid();
9630 while ( elemIt->more() )
9632 const SMDS_MeshElement* elem = elemIt->next();
9633 if ( elem->GetType() != SMDSAbs_Ball )
9635 if (( elem = mesh->AddBall( elem->GetNode(0),
9636 vtkGrid->GetBallDiameter( elem->getVtkId() ))))
9637 myLastCreatedElems.Append( elem );
9642 vector< const SMDS_MeshNode* > nodes;
9643 while ( elemIt->more() )
9645 const SMDS_MeshElement* elem = elemIt->next();
9646 if ( elem->GetType() != type )
9649 nodes.assign( elem->begin_nodes(), elem->end_nodes() );
9651 if ( type == SMDSAbs_Volume && elem->GetVtkType() == VTK_POLYHEDRON )
9653 std::vector<int> quantities =
9654 static_cast< const SMDS_VtkVolume* >( elem )->GetQuantities();
9655 elem = mesh->AddPolyhedralVolume( nodes, quantities );
9659 AddElement( nodes, type, elem->IsPoly() );
9660 elem = 0; // myLastCreatedElems is already filled
9663 myLastCreatedElems.Append( elem );
9668 //================================================================================
9670 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
9671 \param theElems - the list of elements (edges or faces) to be replicated
9672 The nodes for duplication could be found from these elements
9673 \param theNodesNot - list of nodes to NOT replicate
9674 \param theAffectedElems - the list of elements (cells and edges) to which the
9675 replicated nodes should be associated to.
9676 \return TRUE if operation has been completed successfully, FALSE otherwise
9678 //================================================================================
9680 bool SMESH_MeshEditor::DoubleNodes( const TIDSortedElemSet& theElems,
9681 const TIDSortedElemSet& theNodesNot,
9682 const TIDSortedElemSet& theAffectedElems )
9684 myLastCreatedElems.Clear();
9685 myLastCreatedNodes.Clear();
9687 if ( theElems.size() == 0 )
9690 SMESHDS_Mesh* aMeshDS = GetMeshDS();
9695 std::map< const SMDS_MeshNode*, const SMDS_MeshNode* > anOldNodeToNewNode;
9696 // duplicate elements and nodes
9697 res = doubleNodes( aMeshDS, theElems, theNodesNot, anOldNodeToNewNode, true );
9698 // replce nodes by duplications
9699 res = doubleNodes( aMeshDS, theAffectedElems, theNodesNot, anOldNodeToNewNode, false );
9703 //================================================================================
9705 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
9706 \param theMeshDS - mesh instance
9707 \param theElems - the elements replicated or modified (nodes should be changed)
9708 \param theNodesNot - nodes to NOT replicate
9709 \param theNodeNodeMap - relation of old node to new created node
9710 \param theIsDoubleElem - flag os to replicate element or modify
9711 \return TRUE if operation has been completed successfully, FALSE otherwise
9713 //================================================================================
9715 bool SMESH_MeshEditor::doubleNodes( SMESHDS_Mesh* theMeshDS,
9716 const TIDSortedElemSet& theElems,
9717 const TIDSortedElemSet& theNodesNot,
9718 std::map< const SMDS_MeshNode*,
9719 const SMDS_MeshNode* >& theNodeNodeMap,
9720 const bool theIsDoubleElem )
9722 MESSAGE("doubleNodes");
9723 // iterate on through element and duplicate them (by nodes duplication)
9725 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
9726 for ( ; elemItr != theElems.end(); ++elemItr )
9728 const SMDS_MeshElement* anElem = *elemItr;
9732 bool isDuplicate = false;
9733 // duplicate nodes to duplicate element
9734 std::vector<const SMDS_MeshNode*> newNodes( anElem->NbNodes() );
9735 SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
9737 while ( anIter->more() )
9740 SMDS_MeshNode* aCurrNode = (SMDS_MeshNode*)anIter->next();
9741 SMDS_MeshNode* aNewNode = aCurrNode;
9742 if ( theNodeNodeMap.find( aCurrNode ) != theNodeNodeMap.end() )
9743 aNewNode = (SMDS_MeshNode*)theNodeNodeMap[ aCurrNode ];
9744 else if ( theIsDoubleElem && theNodesNot.find( aCurrNode ) == theNodesNot.end() )
9747 aNewNode = theMeshDS->AddNode( aCurrNode->X(), aCurrNode->Y(), aCurrNode->Z() );
9748 theNodeNodeMap[ aCurrNode ] = aNewNode;
9749 myLastCreatedNodes.Append( aNewNode );
9751 isDuplicate |= (aCurrNode != aNewNode);
9752 newNodes[ ind++ ] = aNewNode;
9757 if ( theIsDoubleElem )
9758 AddElement(newNodes, anElem->GetType(), anElem->IsPoly());
9761 MESSAGE("ChangeElementNodes");
9762 theMeshDS->ChangeElementNodes( anElem, &newNodes[ 0 ], anElem->NbNodes() );
9769 //================================================================================
9771 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
9772 \param theNodes - identifiers of nodes to be doubled
9773 \param theModifiedElems - identifiers of elements to be updated by the new (doubled)
9774 nodes. If list of element identifiers is empty then nodes are doubled but
9775 they not assigned to elements
9776 \return TRUE if operation has been completed successfully, FALSE otherwise
9778 //================================================================================
9780 bool SMESH_MeshEditor::DoubleNodes( const std::list< int >& theListOfNodes,
9781 const std::list< int >& theListOfModifiedElems )
9783 MESSAGE("DoubleNodes");
9784 myLastCreatedElems.Clear();
9785 myLastCreatedNodes.Clear();
9787 if ( theListOfNodes.size() == 0 )
9790 SMESHDS_Mesh* aMeshDS = GetMeshDS();
9794 // iterate through nodes and duplicate them
9796 std::map< const SMDS_MeshNode*, const SMDS_MeshNode* > anOldNodeToNewNode;
9798 std::list< int >::const_iterator aNodeIter;
9799 for ( aNodeIter = theListOfNodes.begin(); aNodeIter != theListOfNodes.end(); ++aNodeIter )
9801 int aCurr = *aNodeIter;
9802 SMDS_MeshNode* aNode = (SMDS_MeshNode*)aMeshDS->FindNode( aCurr );
9808 const SMDS_MeshNode* aNewNode = aMeshDS->AddNode( aNode->X(), aNode->Y(), aNode->Z() );
9811 anOldNodeToNewNode[ aNode ] = aNewNode;
9812 myLastCreatedNodes.Append( aNewNode );
9816 // Create map of new nodes for modified elements
9818 std::map< SMDS_MeshElement*, vector<const SMDS_MeshNode*> > anElemToNodes;
9820 std::list< int >::const_iterator anElemIter;
9821 for ( anElemIter = theListOfModifiedElems.begin();
9822 anElemIter != theListOfModifiedElems.end(); ++anElemIter )
9824 int aCurr = *anElemIter;
9825 SMDS_MeshElement* anElem = (SMDS_MeshElement*)aMeshDS->FindElement( aCurr );
9829 vector<const SMDS_MeshNode*> aNodeArr( anElem->NbNodes() );
9831 SMDS_ElemIteratorPtr anIter = anElem->nodesIterator();
9833 while ( anIter->more() )
9835 SMDS_MeshNode* aCurrNode = (SMDS_MeshNode*)anIter->next();
9836 if ( aCurr && anOldNodeToNewNode.find( aCurrNode ) != anOldNodeToNewNode.end() )
9838 const SMDS_MeshNode* aNewNode = anOldNodeToNewNode[ aCurrNode ];
9839 aNodeArr[ ind++ ] = aNewNode;
9842 aNodeArr[ ind++ ] = aCurrNode;
9844 anElemToNodes[ anElem ] = aNodeArr;
9847 // Change nodes of elements
9849 std::map< SMDS_MeshElement*, vector<const SMDS_MeshNode*> >::iterator
9850 anElemToNodesIter = anElemToNodes.begin();
9851 for ( ; anElemToNodesIter != anElemToNodes.end(); ++anElemToNodesIter )
9853 const SMDS_MeshElement* anElem = anElemToNodesIter->first;
9854 vector<const SMDS_MeshNode*> aNodeArr = anElemToNodesIter->second;
9857 MESSAGE("ChangeElementNodes");
9858 aMeshDS->ChangeElementNodes( anElem, &aNodeArr[ 0 ], anElem->NbNodes() );
9867 //================================================================================
9869 \brief Check if element located inside shape
9870 \return TRUE if IN or ON shape, FALSE otherwise
9872 //================================================================================
9874 template<class Classifier>
9875 bool isInside(const SMDS_MeshElement* theElem,
9876 Classifier& theClassifier,
9877 const double theTol)
9879 gp_XYZ centerXYZ (0, 0, 0);
9880 SMDS_ElemIteratorPtr aNodeItr = theElem->nodesIterator();
9881 while (aNodeItr->more())
9882 centerXYZ += SMESH_TNodeXYZ(cast2Node( aNodeItr->next()));
9884 gp_Pnt aPnt = centerXYZ / theElem->NbNodes();
9885 theClassifier.Perform(aPnt, theTol);
9886 TopAbs_State aState = theClassifier.State();
9887 return (aState == TopAbs_IN || aState == TopAbs_ON );
9890 //================================================================================
9892 * \brief Classifier of the 3D point on the TopoDS_Face
9893 * with interaface suitable for isInside()
9895 //================================================================================
9897 struct _FaceClassifier
9899 Extrema_ExtPS _extremum;
9900 BRepAdaptor_Surface _surface;
9901 TopAbs_State _state;
9903 _FaceClassifier(const TopoDS_Face& face):_extremum(),_surface(face),_state(TopAbs_OUT)
9905 _extremum.Initialize( _surface,
9906 _surface.FirstUParameter(), _surface.LastUParameter(),
9907 _surface.FirstVParameter(), _surface.LastVParameter(),
9908 _surface.Tolerance(), _surface.Tolerance() );
9910 void Perform(const gp_Pnt& aPnt, double theTol)
9912 _state = TopAbs_OUT;
9913 _extremum.Perform(aPnt);
9914 if ( _extremum.IsDone() )
9915 for ( int iSol = 1; iSol <= _extremum.NbExt() && _state == TopAbs_OUT; ++iSol)
9916 #if OCC_VERSION_LARGE > 0x06040000 // Porting to OCCT6.5.1
9917 _state = ( _extremum.SquareDistance(iSol) <= theTol ? TopAbs_IN : TopAbs_OUT );
9919 _state = ( _extremum.Value(iSol) <= theTol ? TopAbs_IN : TopAbs_OUT );
9922 TopAbs_State State() const
9929 //================================================================================
9931 \brief Identify the elements that will be affected by node duplication (actual duplication is not performed.
9932 This method is the first step of DoubleNodeElemGroupsInRegion.
9933 \param theElems - list of groups of elements (edges or faces) to be replicated
9934 \param theNodesNot - list of groups of nodes not to replicated
9935 \param theShape - shape to detect affected elements (element which geometric center
9936 located on or inside shape).
9937 The replicated nodes should be associated to affected elements.
9938 \return groups of affected elements
9939 \sa DoubleNodeElemGroupsInRegion()
9941 //================================================================================
9943 bool SMESH_MeshEditor::AffectedElemGroupsInRegion( const TIDSortedElemSet& theElems,
9944 const TIDSortedElemSet& theNodesNot,
9945 const TopoDS_Shape& theShape,
9946 TIDSortedElemSet& theAffectedElems)
9948 if ( theShape.IsNull() )
9951 const double aTol = Precision::Confusion();
9952 auto_ptr< BRepClass3d_SolidClassifier> bsc3d;
9953 auto_ptr<_FaceClassifier> aFaceClassifier;
9954 if ( theShape.ShapeType() == TopAbs_SOLID )
9956 bsc3d.reset( new BRepClass3d_SolidClassifier(theShape));;
9957 bsc3d->PerformInfinitePoint(aTol);
9959 else if (theShape.ShapeType() == TopAbs_FACE )
9961 aFaceClassifier.reset( new _FaceClassifier(TopoDS::Face(theShape)));
9964 // iterates on indicated elements and get elements by back references from their nodes
9965 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
9966 for ( ; elemItr != theElems.end(); ++elemItr )
9968 SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
9972 SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
9973 while ( nodeItr->more() )
9975 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
9976 if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
9978 SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
9979 while ( backElemItr->more() )
9981 const SMDS_MeshElement* curElem = backElemItr->next();
9982 if ( curElem && theElems.find(curElem) == theElems.end() &&
9984 isInside( curElem, *bsc3d, aTol ) :
9985 isInside( curElem, *aFaceClassifier, aTol )))
9986 theAffectedElems.insert( curElem );
9993 //================================================================================
9995 \brief Creates a hole in a mesh by doubling the nodes of some particular elements
9996 \param theElems - group of of elements (edges or faces) to be replicated
9997 \param theNodesNot - group of nodes not to replicate
9998 \param theShape - shape to detect affected elements (element which geometric center
9999 located on or inside shape).
10000 The replicated nodes should be associated to affected elements.
10001 \return TRUE if operation has been completed successfully, FALSE otherwise
10003 //================================================================================
10005 bool SMESH_MeshEditor::DoubleNodesInRegion( const TIDSortedElemSet& theElems,
10006 const TIDSortedElemSet& theNodesNot,
10007 const TopoDS_Shape& theShape )
10009 if ( theShape.IsNull() )
10012 const double aTol = Precision::Confusion();
10013 auto_ptr< BRepClass3d_SolidClassifier> bsc3d;
10014 auto_ptr<_FaceClassifier> aFaceClassifier;
10015 if ( theShape.ShapeType() == TopAbs_SOLID )
10017 bsc3d.reset( new BRepClass3d_SolidClassifier(theShape));;
10018 bsc3d->PerformInfinitePoint(aTol);
10020 else if (theShape.ShapeType() == TopAbs_FACE )
10022 aFaceClassifier.reset( new _FaceClassifier(TopoDS::Face(theShape)));
10025 // iterates on indicated elements and get elements by back references from their nodes
10026 TIDSortedElemSet anAffected;
10027 TIDSortedElemSet::const_iterator elemItr = theElems.begin();
10028 for ( ; elemItr != theElems.end(); ++elemItr )
10030 SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr;
10034 SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator();
10035 while ( nodeItr->more() )
10037 const SMDS_MeshNode* aNode = cast2Node(nodeItr->next());
10038 if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() )
10040 SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator();
10041 while ( backElemItr->more() )
10043 const SMDS_MeshElement* curElem = backElemItr->next();
10044 if ( curElem && theElems.find(curElem) == theElems.end() &&
10046 isInside( curElem, *bsc3d, aTol ) :
10047 isInside( curElem, *aFaceClassifier, aTol )))
10048 anAffected.insert( curElem );
10052 return DoubleNodes( theElems, theNodesNot, anAffected );
10056 * \brief compute an oriented angle between two planes defined by four points.
10057 * The vector (p0,p1) defines the intersection of the 2 planes (p0,p1,g1) and (p0,p1,g2)
10058 * @param p0 base of the rotation axe
10059 * @param p1 extremity of the rotation axe
10060 * @param g1 belongs to the first plane
10061 * @param g2 belongs to the second plane
10063 double SMESH_MeshEditor::OrientedAngle(const gp_Pnt& p0, const gp_Pnt& p1, const gp_Pnt& g1, const gp_Pnt& g2)
10065 // MESSAGE(" p0: " << p0.X() << " " << p0.Y() << " " << p0.Z());
10066 // MESSAGE(" p1: " << p1.X() << " " << p1.Y() << " " << p1.Z());
10067 // MESSAGE(" g1: " << g1.X() << " " << g1.Y() << " " << g1.Z());
10068 // MESSAGE(" g2: " << g2.X() << " " << g2.Y() << " " << g2.Z());
10069 gp_Vec vref(p0, p1);
10072 gp_Vec n1 = vref.Crossed(v1);
10073 gp_Vec n2 = vref.Crossed(v2);
10074 return n2.AngleWithRef(n1, vref);
10078 * \brief Double nodes on shared faces between groups of volumes and create flat elements on demand.
10079 * The list of groups must contain at least two groups. The groups have to be disjoint: no common element into two different groups.
10080 * The nodes of the internal faces at the boundaries of the groups are doubled. Optionally, the internal faces are replaced by flat elements.
10081 * Triangles are transformed into prisms, and quadrangles into hexahedrons.
10082 * The flat elements are stored in groups of volumes. These groups are named according to the position of the group in the list:
10083 * 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.
10084 * 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.
10085 * All the flat elements are gathered into the group named "joints3D" (or "joints2D" in 2D situation).
10086 * The flat element of the multiple junctions between the simple junction are stored in a group named "jointsMultiples".
10087 * @param theElems - list of groups of volumes, where a group of volume is a set of
10088 * SMDS_MeshElements sorted by Id.
10089 * @param createJointElems - if TRUE, create the elements
10090 * @return TRUE if operation has been completed successfully, FALSE otherwise
10092 bool SMESH_MeshEditor::DoubleNodesOnGroupBoundaries( const std::vector<TIDSortedElemSet>& theElems,
10093 bool createJointElems)
10095 MESSAGE("----------------------------------------------");
10096 MESSAGE("SMESH_MeshEditor::doubleNodesOnGroupBoundaries");
10097 MESSAGE("----------------------------------------------");
10099 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
10100 meshDS->BuildDownWardConnectivity(true);
10102 SMDS_UnstructuredGrid *grid = meshDS->getGrid();
10104 // --- build the list of faces shared by 2 domains (group of elements), with their domain and volume indexes
10105 // build the list of cells with only a node or an edge on the border, with their domain and volume indexes
10106 // build the list of nodes shared by 2 or more domains, with their domain indexes
10108 std::map<DownIdType, std::map<int,int>, DownIdCompare> faceDomains; // face --> (id domain --> id volume)
10109 std::map<int,int>celldom; // cell vtkId --> domain
10110 std::map<DownIdType, std::map<int,int>, DownIdCompare> cellDomains; // oldNode --> (id domain --> id cell)
10111 std::map<int, std::map<int,int> > nodeDomains; // oldId --> (domainId --> newId)
10112 faceDomains.clear();
10114 cellDomains.clear();
10115 nodeDomains.clear();
10116 std::map<int,int> emptyMap;
10117 std::set<int> emptySet;
10120 MESSAGE(".. Number of domains :"<<theElems.size());
10122 // Check if the domains do not share an element
10123 for (int idom = 0; idom < theElems.size()-1; idom++)
10125 // MESSAGE("... Check of domain #" << idom);
10126 const TIDSortedElemSet& domain = theElems[idom];
10127 TIDSortedElemSet::const_iterator elemItr = domain.begin();
10128 for (; elemItr != domain.end(); ++elemItr)
10130 SMDS_MeshElement* anElem = (SMDS_MeshElement*) *elemItr;
10131 int idombisdeb = idom + 1 ;
10132 for (int idombis = idombisdeb; idombis < theElems.size(); idombis++) // check if the element belongs to a domain further in the list
10134 const TIDSortedElemSet& domainbis = theElems[idombis];
10135 if ( domainbis.count(anElem) )
10137 MESSAGE(".... Domain #" << idom);
10138 MESSAGE(".... Domain #" << idombis);
10139 throw SALOME_Exception("The domains are not disjoint.");
10146 for (int idom = 0; idom < theElems.size(); idom++)
10149 // --- build a map (face to duplicate --> volume to modify)
10150 // with all the faces shared by 2 domains (group of elements)
10151 // and corresponding volume of this domain, for each shared face.
10152 // a volume has a face shared by 2 domains if it has a neighbor which is not in his domain.
10154 MESSAGE("... Neighbors of domain #" << idom);
10155 const TIDSortedElemSet& domain = theElems[idom];
10156 TIDSortedElemSet::const_iterator elemItr = domain.begin();
10157 for (; elemItr != domain.end(); ++elemItr)
10159 SMDS_MeshElement* anElem = (SMDS_MeshElement*) *elemItr;
10162 int vtkId = anElem->getVtkId();
10163 //MESSAGE(" vtkId " << vtkId << " smdsId " << anElem->GetID());
10164 int neighborsVtkIds[NBMAXNEIGHBORS];
10165 int downIds[NBMAXNEIGHBORS];
10166 unsigned char downTypes[NBMAXNEIGHBORS];
10167 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
10168 for (int n = 0; n < nbNeighbors; n++)
10170 int smdsId = meshDS->fromVtkToSmds(neighborsVtkIds[n]);
10171 const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
10172 if (! domain.count(elem)) // neighbor is in another domain : face is shared
10175 for (int idombis = 0; idombis < theElems.size(); idombis++) // check if the neighbor belongs to another domain of the list
10177 // MESSAGE("Domain " << idombis);
10178 const TIDSortedElemSet& domainbis = theElems[idombis];
10179 if ( domainbis.count(elem)) ok = true ; // neighbor is in a correct domain : face is kept
10181 if ( ok ) // the characteristics of the face is stored
10183 DownIdType face(downIds[n], downTypes[n]);
10184 if (!faceDomains.count(face))
10185 faceDomains[face] = emptyMap; // create an empty entry for face
10186 if (!faceDomains[face].count(idom))
10188 faceDomains[face][idom] = vtkId; // volume associated to face in this domain
10189 celldom[vtkId] = idom;
10190 //MESSAGE(" cell with a border " << vtkId << " domain " << idom);
10198 //MESSAGE("Number of shared faces " << faceDomains.size());
10199 std::map<DownIdType, std::map<int, int>, DownIdCompare>::iterator itface;
10201 // --- explore the shared faces domain by domain,
10202 // explore the nodes of the face and see if they belong to a cell in the domain,
10203 // which has only a node or an edge on the border (not a shared face)
10205 for (int idomain = 0; idomain < theElems.size(); idomain++)
10207 //MESSAGE("Domain " << idomain);
10208 const TIDSortedElemSet& domain = theElems[idomain];
10209 itface = faceDomains.begin();
10210 for (; itface != faceDomains.end(); ++itface)
10212 std::map<int, int> domvol = itface->second;
10213 if (!domvol.count(idomain))
10215 DownIdType face = itface->first;
10216 //MESSAGE(" --- face " << face.cellId);
10217 std::set<int> oldNodes;
10219 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
10220 std::set<int>::iterator itn = oldNodes.begin();
10221 for (; itn != oldNodes.end(); ++itn)
10224 //MESSAGE(" node " << oldId);
10225 vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
10226 for (int i=0; i<l.ncells; i++)
10228 int vtkId = l.cells[i];
10229 const SMDS_MeshElement* anElem = GetMeshDS()->FindElement(GetMeshDS()->fromVtkToSmds(vtkId));
10230 if (!domain.count(anElem))
10232 int vtkType = grid->GetCellType(vtkId);
10233 int downId = grid->CellIdToDownId(vtkId);
10236 MESSAGE("doubleNodesOnGroupBoundaries: internal algorithm problem");
10237 continue; // not OK at this stage of the algorithm:
10238 //no cells created after BuildDownWardConnectivity
10240 DownIdType aCell(downId, vtkType);
10241 if (!cellDomains.count(aCell))
10242 cellDomains[aCell] = emptyMap; // create an empty entry for cell
10243 cellDomains[aCell][idomain] = vtkId;
10244 celldom[vtkId] = idomain;
10245 //MESSAGE(" cell " << vtkId << " domain " << idomain);
10251 // --- explore the shared faces domain by domain, to duplicate the nodes in a coherent way
10252 // for each shared face, get the nodes
10253 // for each node, for each domain of the face, create a clone of the node
10255 // --- edges at the intersection of 3 or 4 domains, with the order of domains to build
10256 // junction elements of type prism or hexa. the key is the pair of nodesId (lower first)
10257 // the value is the ordered domain ids. (more than 4 domains not taken into account)
10259 std::map<std::vector<int>, std::vector<int> > edgesMultiDomains; // nodes of edge --> ordered domains
10260 std::map<int, std::vector<int> > mutipleNodes; // nodes multi domains with domain order
10261 std::map<int, std::vector<int> > mutipleNodesToFace; // nodes multi domains with domain order to transform in Face (junction between 3 or more 2D domains)
10263 MESSAGE(".. Duplication of the nodes");
10264 for (int idomain = 0; idomain < theElems.size(); idomain++)
10266 itface = faceDomains.begin();
10267 for (; itface != faceDomains.end(); ++itface)
10269 std::map<int, int> domvol = itface->second;
10270 if (!domvol.count(idomain))
10272 DownIdType face = itface->first;
10273 //MESSAGE(" --- face " << face.cellId);
10274 std::set<int> oldNodes;
10276 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
10277 std::set<int>::iterator itn = oldNodes.begin();
10278 for (; itn != oldNodes.end(); ++itn)
10281 //MESSAGE("-+-+-a node " << oldId);
10282 if (!nodeDomains.count(oldId))
10283 nodeDomains[oldId] = emptyMap; // create an empty entry for node
10284 if (nodeDomains[oldId].empty())
10286 nodeDomains[oldId][idomain] = oldId; // keep the old node in the first domain
10287 //MESSAGE("-+-+-b oldNode " << oldId << " domain " << idomain);
10289 std::map<int, int>::iterator itdom = domvol.begin();
10290 for (; itdom != domvol.end(); ++itdom)
10292 int idom = itdom->first;
10293 //MESSAGE(" domain " << idom);
10294 if (!nodeDomains[oldId].count(idom)) // --- node to clone
10296 if (nodeDomains[oldId].size() >= 2) // a multiple node
10298 vector<int> orderedDoms;
10299 //MESSAGE("multiple node " << oldId);
10300 if (mutipleNodes.count(oldId))
10301 orderedDoms = mutipleNodes[oldId];
10304 map<int,int>::iterator it = nodeDomains[oldId].begin();
10305 for (; it != nodeDomains[oldId].end(); ++it)
10306 orderedDoms.push_back(it->first);
10308 orderedDoms.push_back(idom); // TODO order ==> push_front or back
10309 //stringstream txt;
10310 //for (int i=0; i<orderedDoms.size(); i++)
10311 // txt << orderedDoms[i] << " ";
10312 //MESSAGE("orderedDoms " << txt.str());
10313 mutipleNodes[oldId] = orderedDoms;
10315 double *coords = grid->GetPoint(oldId);
10316 SMDS_MeshNode *newNode = meshDS->AddNode(coords[0], coords[1], coords[2]);
10317 int newId = newNode->getVtkId();
10318 nodeDomains[oldId][idom] = newId; // cloned node for other domains
10319 //MESSAGE("-+-+-c oldNode " << oldId << " domain " << idomain << " newNode " << newId << " domain " << idom << " size=" <<nodeDomains[oldId].size());
10326 MESSAGE(".. Creation of elements");
10327 for (int idomain = 0; idomain < theElems.size(); idomain++)
10329 itface = faceDomains.begin();
10330 for (; itface != faceDomains.end(); ++itface)
10332 std::map<int, int> domvol = itface->second;
10333 if (!domvol.count(idomain))
10335 DownIdType face = itface->first;
10336 //MESSAGE(" --- face " << face.cellId);
10337 std::set<int> oldNodes;
10339 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
10340 int nbMultipleNodes = 0;
10341 std::set<int>::iterator itn = oldNodes.begin();
10342 for (; itn != oldNodes.end(); ++itn)
10345 if (mutipleNodes.count(oldId))
10348 if (nbMultipleNodes > 1) // check if an edge of the face is shared between 3 or more domains
10350 //MESSAGE("multiple Nodes detected on a shared face");
10351 int downId = itface->first.cellId;
10352 unsigned char cellType = itface->first.cellType;
10353 // --- shared edge or shared face ?
10354 if ((cellType == VTK_LINE) || (cellType == VTK_QUADRATIC_EDGE)) // shared edge (between two faces)
10357 int nbNodes = grid->getDownArray(cellType)->getNodes(downId, nodes);
10358 for (int i=0; i< nbNodes; i=i+nbNodes-1) // i=0 , i=nbNodes-1
10359 if (mutipleNodes.count(nodes[i]))
10360 if (!mutipleNodesToFace.count(nodes[i]))
10361 mutipleNodesToFace[nodes[i]] = mutipleNodes[nodes[i]];
10363 else // shared face (between two volumes)
10365 int nbEdges = grid->getDownArray(cellType)->getNumberOfDownCells(downId);
10366 const int* downEdgeIds = grid->getDownArray(cellType)->getDownCells(downId);
10367 const unsigned char* edgeType = grid->getDownArray(cellType)->getDownTypes(downId);
10368 for (int ie =0; ie < nbEdges; ie++)
10371 int nbNodes = grid->getDownArray(edgeType[ie])->getNodes(downEdgeIds[ie], nodes);
10372 if (mutipleNodes.count(nodes[0]) && mutipleNodes.count(nodes[nbNodes-1]))
10374 vector<int> vn0 = mutipleNodes[nodes[0]];
10375 vector<int> vn1 = mutipleNodes[nodes[nbNodes - 1]];
10377 for (int i0 = 0; i0 < vn0.size(); i0++)
10378 for (int i1 = 0; i1 < vn1.size(); i1++)
10379 if (vn0[i0] == vn1[i1])
10380 doms.push_back(vn0[i0]);
10381 if (doms.size() >2)
10383 //MESSAGE(" detect edgesMultiDomains " << nodes[0] << " " << nodes[nbNodes - 1]);
10384 double *coords = grid->GetPoint(nodes[0]);
10385 gp_Pnt p0(coords[0], coords[1], coords[2]);
10386 coords = grid->GetPoint(nodes[nbNodes - 1]);
10387 gp_Pnt p1(coords[0], coords[1], coords[2]);
10389 int vtkVolIds[1000]; // an edge can belong to a lot of volumes
10390 map<int, SMDS_VtkVolume*> domvol; // domain --> a volume with the edge
10391 map<int, double> angleDom; // oriented angles between planes defined by edge and volume centers
10392 int nbvol = grid->GetParentVolumes(vtkVolIds, downEdgeIds[ie], edgeType[ie]);
10393 for (int id=0; id < doms.size(); id++)
10395 int idom = doms[id];
10396 for (int ivol=0; ivol<nbvol; ivol++)
10398 int smdsId = meshDS->fromVtkToSmds(vtkVolIds[ivol]);
10399 SMDS_MeshElement* elem = (SMDS_MeshElement*)meshDS->FindElement(smdsId);
10400 if (theElems[idom].count(elem))
10402 SMDS_VtkVolume* svol = dynamic_cast<SMDS_VtkVolume*>(elem);
10403 domvol[idom] = svol;
10404 //MESSAGE(" domain " << idom << " volume " << elem->GetID());
10406 vtkIdType npts = 0;
10407 vtkIdType* pts = 0;
10408 grid->GetCellPoints(vtkVolIds[ivol], npts, pts);
10409 SMDS_VtkVolume::gravityCenter(grid, pts, npts, values);
10412 gref.SetXYZ(gp_XYZ(values[0], values[1], values[2]));
10413 angleDom[idom] = 0;
10417 gp_Pnt g(values[0], values[1], values[2]);
10418 angleDom[idom] = OrientedAngle(p0, p1, gref, g); // -pi<angle<+pi
10419 //MESSAGE(" angle=" << angleDom[idom]);
10425 map<double, int> sortedDom; // sort domains by angle
10426 for (map<int, double>::iterator ia = angleDom.begin(); ia != angleDom.end(); ++ia)
10427 sortedDom[ia->second] = ia->first;
10428 vector<int> vnodes;
10430 for (map<double, int>::iterator ib = sortedDom.begin(); ib != sortedDom.end(); ++ib)
10432 vdom.push_back(ib->second);
10433 //MESSAGE(" ordered domain " << ib->second << " angle " << ib->first);
10435 for (int ino = 0; ino < nbNodes; ino++)
10436 vnodes.push_back(nodes[ino]);
10437 edgesMultiDomains[vnodes] = vdom; // nodes vector --> ordered domains
10446 // --- iterate on shared faces (volumes to modify, face to extrude)
10447 // get node id's of the face (id SMDS = id VTK)
10448 // create flat element with old and new nodes if requested
10450 // --- new quad nodes on flat quad elements: oldId --> ((domain1 X domain2) --> newId)
10451 // (domain1 X domain2) = domain1 + MAXINT*domain2
10453 std::map<int, std::map<long,int> > nodeQuadDomains;
10454 std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
10456 MESSAGE(".. Creation of elements: simple junction");
10457 if (createJointElems)
10460 string joints2DName = "joints2D";
10461 mapOfJunctionGroups[joints2DName] = this->myMesh->AddGroup(SMDSAbs_Face, joints2DName.c_str(), idg);
10462 SMESHDS_Group *joints2DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints2DName]->GetGroupDS());
10463 string joints3DName = "joints3D";
10464 mapOfJunctionGroups[joints3DName] = this->myMesh->AddGroup(SMDSAbs_Volume, joints3DName.c_str(), idg);
10465 SMESHDS_Group *joints3DGrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[joints3DName]->GetGroupDS());
10467 itface = faceDomains.begin();
10468 for (; itface != faceDomains.end(); ++itface)
10470 DownIdType face = itface->first;
10471 std::set<int> oldNodes;
10472 std::set<int>::iterator itn;
10474 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
10476 std::map<int, int> domvol = itface->second;
10477 std::map<int, int>::iterator itdom = domvol.begin();
10478 int dom1 = itdom->first;
10479 int vtkVolId = itdom->second;
10481 int dom2 = itdom->first;
10482 SMDS_MeshCell *vol = grid->extrudeVolumeFromFace(vtkVolId, dom1, dom2, oldNodes, nodeDomains,
10484 stringstream grpname;
10487 grpname << dom1 << "_" << dom2;
10489 grpname << dom2 << "_" << dom1;
10490 string namegrp = grpname.str();
10491 if (!mapOfJunctionGroups.count(namegrp))
10492 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(vol->GetType(), namegrp.c_str(), idg);
10493 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
10495 sgrp->Add(vol->GetID());
10496 if (vol->GetType() == SMDSAbs_Volume)
10497 joints3DGrp->Add(vol->GetID());
10498 else if (vol->GetType() == SMDSAbs_Face)
10499 joints2DGrp->Add(vol->GetID());
10503 // --- create volumes on multiple domain intersection if requested
10504 // iterate on mutipleNodesToFace
10505 // iterate on edgesMultiDomains
10507 MESSAGE(".. Creation of elements: multiple junction");
10508 if (createJointElems)
10510 // --- iterate on mutipleNodesToFace
10512 std::map<int, std::vector<int> >::iterator itn = mutipleNodesToFace.begin();
10513 for (; itn != mutipleNodesToFace.end(); ++itn)
10515 int node = itn->first;
10516 vector<int> orderDom = itn->second;
10517 vector<vtkIdType> orderedNodes;
10518 for (int idom = 0; idom <orderDom.size(); idom++)
10519 orderedNodes.push_back( nodeDomains[node][orderDom[idom]] );
10520 SMDS_MeshFace* face = this->GetMeshDS()->AddFaceFromVtkIds(orderedNodes);
10522 stringstream grpname;
10524 grpname << 0 << "_" << 0;
10526 string namegrp = grpname.str();
10527 if (!mapOfJunctionGroups.count(namegrp))
10528 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Face, namegrp.c_str(), idg);
10529 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
10531 sgrp->Add(face->GetID());
10534 // --- iterate on edgesMultiDomains
10536 std::map<std::vector<int>, std::vector<int> >::iterator ite = edgesMultiDomains.begin();
10537 for (; ite != edgesMultiDomains.end(); ++ite)
10539 vector<int> nodes = ite->first;
10540 vector<int> orderDom = ite->second;
10541 vector<vtkIdType> orderedNodes;
10542 if (nodes.size() == 2)
10544 //MESSAGE(" use edgesMultiDomains " << nodes[0] << " " << nodes[1]);
10545 for (int ino=0; ino < nodes.size(); ino++)
10546 if (orderDom.size() == 3)
10547 for (int idom = 0; idom <orderDom.size(); idom++)
10548 orderedNodes.push_back( nodeDomains[nodes[ino]][orderDom[idom]] );
10550 for (int idom = orderDom.size()-1; idom >=0; idom--)
10551 orderedNodes.push_back( nodeDomains[nodes[ino]][orderDom[idom]] );
10552 SMDS_MeshVolume* vol = this->GetMeshDS()->AddVolumeFromVtkIds(orderedNodes);
10555 string namegrp = "jointsMultiples";
10556 if (!mapOfJunctionGroups.count(namegrp))
10557 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
10558 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
10560 sgrp->Add(vol->GetID());
10564 INFOS("Quadratic multiple joints not implemented");
10565 // TODO quadratic nodes
10570 // --- list the explicit faces and edges of the mesh that need to be modified,
10571 // i.e. faces and edges built with one or more duplicated nodes.
10572 // associate these faces or edges to their corresponding domain.
10573 // only the first domain found is kept when a face or edge is shared
10575 std::map<DownIdType, std::map<int,int>, DownIdCompare> faceOrEdgeDom; // cellToModify --> (id domain --> id cell)
10576 std::map<int,int> feDom; // vtk id of cell to modify --> id domain
10577 faceOrEdgeDom.clear();
10580 MESSAGE(".. Modification of elements");
10581 for (int idomain = 0; idomain < theElems.size(); idomain++)
10583 std::map<int, std::map<int, int> >::const_iterator itnod = nodeDomains.begin();
10584 for (; itnod != nodeDomains.end(); ++itnod)
10586 int oldId = itnod->first;
10587 //MESSAGE(" node " << oldId);
10588 vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId);
10589 for (int i = 0; i < l.ncells; i++)
10591 int vtkId = l.cells[i];
10592 int vtkType = grid->GetCellType(vtkId);
10593 int downId = grid->CellIdToDownId(vtkId);
10595 continue; // new cells: not to be modified
10596 DownIdType aCell(downId, vtkType);
10597 int volParents[1000];
10598 int nbvol = grid->GetParentVolumes(volParents, vtkId);
10599 for (int j = 0; j < nbvol; j++)
10600 if (celldom.count(volParents[j]) && (celldom[volParents[j]] == idomain))
10601 if (!feDom.count(vtkId))
10603 feDom[vtkId] = idomain;
10604 faceOrEdgeDom[aCell] = emptyMap;
10605 faceOrEdgeDom[aCell][idomain] = vtkId; // affect face or edge to the first domain only
10606 //MESSAGE("affect cell " << this->GetMeshDS()->fromVtkToSmds(vtkId) << " domain " << idomain
10607 // << " type " << vtkType << " downId " << downId);
10613 // --- iterate on shared faces (volumes to modify, face to extrude)
10614 // get node id's of the face
10615 // replace old nodes by new nodes in volumes, and update inverse connectivity
10617 std::map<DownIdType, std::map<int,int>, DownIdCompare>* maps[3] = {&faceDomains, &cellDomains, &faceOrEdgeDom};
10618 for (int m=0; m<3; m++)
10620 std::map<DownIdType, std::map<int,int>, DownIdCompare>* amap = maps[m];
10621 itface = (*amap).begin();
10622 for (; itface != (*amap).end(); ++itface)
10624 DownIdType face = itface->first;
10625 std::set<int> oldNodes;
10626 std::set<int>::iterator itn;
10628 grid->GetNodeIds(oldNodes, face.cellId, face.cellType);
10629 //MESSAGE("examine cell, downId " << face.cellId << " type " << int(face.cellType));
10630 std::map<int, int> localClonedNodeIds;
10632 std::map<int, int> domvol = itface->second;
10633 std::map<int, int>::iterator itdom = domvol.begin();
10634 for (; itdom != domvol.end(); ++itdom)
10636 int idom = itdom->first;
10637 int vtkVolId = itdom->second;
10638 //MESSAGE("modify nodes of cell " << this->GetMeshDS()->fromVtkToSmds(vtkVolId) << " domain " << idom);
10639 localClonedNodeIds.clear();
10640 for (itn = oldNodes.begin(); itn != oldNodes.end(); ++itn)
10643 if (nodeDomains[oldId].count(idom))
10645 localClonedNodeIds[oldId] = nodeDomains[oldId][idom];
10646 //MESSAGE(" node " << oldId << " --> " << localClonedNodeIds[oldId]);
10649 meshDS->ModifyCellNodes(vtkVolId, localClonedNodeIds);
10654 meshDS->CleanDownWardConnectivity(); // Mesh has been modified, downward connectivity is no more usable, free memory
10655 grid->BuildLinks();
10663 * \brief Double nodes on some external faces and create flat elements.
10664 * Flat elements are mainly used by some types of mechanic calculations.
10666 * Each group of the list must be constituted of faces.
10667 * Triangles are transformed in prisms, and quadrangles in hexahedrons.
10668 * @param theElems - list of groups of faces, where a group of faces is a set of
10669 * SMDS_MeshElements sorted by Id.
10670 * @return TRUE if operation has been completed successfully, FALSE otherwise
10672 bool SMESH_MeshEditor::CreateFlatElementsOnFacesGroups(const std::vector<TIDSortedElemSet>& theElems)
10674 MESSAGE("-------------------------------------------------");
10675 MESSAGE("SMESH_MeshEditor::CreateFlatElementsOnFacesGroups");
10676 MESSAGE("-------------------------------------------------");
10678 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
10680 // --- For each group of faces
10681 // duplicate the nodes, create a flat element based on the face
10682 // replace the nodes of the faces by their clones
10684 std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> clonedNodes;
10685 std::map<const SMDS_MeshNode*, const SMDS_MeshNode*> intermediateNodes;
10686 clonedNodes.clear();
10687 intermediateNodes.clear();
10688 std::map<std::string, SMESH_Group*> mapOfJunctionGroups;
10689 mapOfJunctionGroups.clear();
10691 for (int idom = 0; idom < theElems.size(); idom++)
10693 const TIDSortedElemSet& domain = theElems[idom];
10694 TIDSortedElemSet::const_iterator elemItr = domain.begin();
10695 for (; elemItr != domain.end(); ++elemItr)
10697 SMDS_MeshElement* anElem = (SMDS_MeshElement*) *elemItr;
10698 SMDS_MeshFace* aFace = dynamic_cast<SMDS_MeshFace*> (anElem);
10701 // MESSAGE("aFace=" << aFace->GetID());
10702 bool isQuad = aFace->IsQuadratic();
10703 vector<const SMDS_MeshNode*> ln0, ln1, ln2, ln3, ln4;
10705 // --- clone the nodes, create intermediate nodes for non medium nodes of a quad face
10707 SMDS_ElemIteratorPtr nodeIt = aFace->nodesIterator();
10708 while (nodeIt->more())
10710 const SMDS_MeshNode* node = static_cast<const SMDS_MeshNode*> (nodeIt->next());
10711 bool isMedium = isQuad && (aFace->IsMediumNode(node));
10713 ln2.push_back(node);
10715 ln0.push_back(node);
10717 const SMDS_MeshNode* clone = 0;
10718 if (!clonedNodes.count(node))
10720 clone = meshDS->AddNode(node->X(), node->Y(), node->Z());
10721 clonedNodes[node] = clone;
10724 clone = clonedNodes[node];
10727 ln3.push_back(clone);
10729 ln1.push_back(clone);
10731 const SMDS_MeshNode* inter = 0;
10732 if (isQuad && (!isMedium))
10734 if (!intermediateNodes.count(node))
10736 inter = meshDS->AddNode(node->X(), node->Y(), node->Z());
10737 intermediateNodes[node] = inter;
10740 inter = intermediateNodes[node];
10741 ln4.push_back(inter);
10745 // --- extrude the face
10747 vector<const SMDS_MeshNode*> ln;
10748 SMDS_MeshVolume* vol = 0;
10749 vtkIdType aType = aFace->GetVtkType();
10753 vol = meshDS->AddVolume(ln0[2], ln0[1], ln0[0], ln1[2], ln1[1], ln1[0]);
10754 // MESSAGE("vol prism " << vol->GetID());
10755 ln.push_back(ln1[0]);
10756 ln.push_back(ln1[1]);
10757 ln.push_back(ln1[2]);
10760 vol = meshDS->AddVolume(ln0[3], ln0[2], ln0[1], ln0[0], ln1[3], ln1[2], ln1[1], ln1[0]);
10761 // MESSAGE("vol hexa " << vol->GetID());
10762 ln.push_back(ln1[0]);
10763 ln.push_back(ln1[1]);
10764 ln.push_back(ln1[2]);
10765 ln.push_back(ln1[3]);
10767 case VTK_QUADRATIC_TRIANGLE:
10768 vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln0[0], ln0[1], ln0[2], ln3[0], ln3[1], ln3[2],
10769 ln2[0], ln2[1], ln2[2], ln4[0], ln4[1], ln4[2]);
10770 // MESSAGE("vol quad prism " << vol->GetID());
10771 ln.push_back(ln1[0]);
10772 ln.push_back(ln1[1]);
10773 ln.push_back(ln1[2]);
10774 ln.push_back(ln3[0]);
10775 ln.push_back(ln3[1]);
10776 ln.push_back(ln3[2]);
10778 case VTK_QUADRATIC_QUAD:
10779 // vol = meshDS->AddVolume(ln0[0], ln0[1], ln0[2], ln0[3], ln1[0], ln1[1], ln1[2], ln1[3],
10780 // ln2[0], ln2[1], ln2[2], ln2[3], ln3[0], ln3[1], ln3[2], ln3[3],
10781 // ln4[0], ln4[1], ln4[2], ln4[3]);
10782 vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln1[3], ln0[0], ln0[1], ln0[2], ln0[3],
10783 ln3[0], ln3[1], ln3[2], ln3[3], ln2[0], ln2[1], ln2[2], ln2[3],
10784 ln4[0], ln4[1], ln4[2], ln4[3]);
10785 // MESSAGE("vol quad hexa " << vol->GetID());
10786 ln.push_back(ln1[0]);
10787 ln.push_back(ln1[1]);
10788 ln.push_back(ln1[2]);
10789 ln.push_back(ln1[3]);
10790 ln.push_back(ln3[0]);
10791 ln.push_back(ln3[1]);
10792 ln.push_back(ln3[2]);
10793 ln.push_back(ln3[3]);
10803 stringstream grpname;
10807 string namegrp = grpname.str();
10808 if (!mapOfJunctionGroups.count(namegrp))
10809 mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg);
10810 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(mapOfJunctionGroups[namegrp]->GetGroupDS());
10812 sgrp->Add(vol->GetID());
10815 // --- modify the face
10817 aFace->ChangeNodes(&ln[0], ln.size());
10824 * \brief identify all the elements around a geom shape, get the faces delimiting the hole
10825 * Build groups of volume to remove, groups of faces to replace on the skin of the object,
10826 * groups of faces to remove inside the object, (idem edges).
10827 * Build ordered list of nodes at the border of each group of faces to replace (to be used to build a geom subshape)
10829 void SMESH_MeshEditor::CreateHoleSkin(double radius,
10830 const TopoDS_Shape& theShape,
10831 SMESH_NodeSearcher* theNodeSearcher,
10832 const char* groupName,
10833 std::vector<double>& nodesCoords,
10834 std::vector<std::vector<int> >& listOfListOfNodes)
10836 MESSAGE("--------------------------------");
10837 MESSAGE("SMESH_MeshEditor::CreateHoleSkin");
10838 MESSAGE("--------------------------------");
10840 // --- zone of volumes to remove is given :
10841 // 1 either by a geom shape (one or more vertices) and a radius,
10842 // 2 either by a group of nodes (representative of the shape)to use with the radius,
10843 // 3 either by a group of nodes where all the elements build on one of this nodes are to remove,
10844 // In the case 2, the group of nodes is an external group of nodes from another mesh,
10845 // In the case 3, the group of nodes is an internal group of the mesh (obtained for instance by a filter),
10846 // defined by it's name.
10848 SMESHDS_GroupBase* groupDS = 0;
10849 SMESH_Mesh::GroupIteratorPtr groupIt = this->myMesh->GetGroups();
10850 while ( groupIt->more() )
10853 SMESH_Group * group = groupIt->next();
10854 if ( !group ) continue;
10855 groupDS = group->GetGroupDS();
10856 if ( !groupDS || groupDS->IsEmpty() ) continue;
10857 std::string grpName = group->GetName();
10858 //MESSAGE("grpName=" << grpName);
10859 if (grpName == groupName)
10865 bool isNodeGroup = false;
10866 bool isNodeCoords = false;
10869 if (groupDS->GetType() != SMDSAbs_Node)
10871 isNodeGroup = true; // a group of nodes exists and it is in this mesh
10874 if (nodesCoords.size() > 0)
10875 isNodeCoords = true; // a list o nodes given by their coordinates
10876 //MESSAGE("---" << isNodeGroup << " " << isNodeCoords);
10878 // --- define groups to build
10880 int idg; // --- group of SMDS volumes
10881 string grpvName = groupName;
10882 grpvName += "_vol";
10883 SMESH_Group *grp = this->myMesh->AddGroup(SMDSAbs_Volume, grpvName.c_str(), idg);
10886 MESSAGE("group not created " << grpvName);
10889 SMESHDS_Group *sgrp = dynamic_cast<SMESHDS_Group*>(grp->GetGroupDS());
10891 int idgs; // --- group of SMDS faces on the skin
10892 string grpsName = groupName;
10893 grpsName += "_skin";
10894 SMESH_Group *grps = this->myMesh->AddGroup(SMDSAbs_Face, grpsName.c_str(), idgs);
10897 MESSAGE("group not created " << grpsName);
10900 SMESHDS_Group *sgrps = dynamic_cast<SMESHDS_Group*>(grps->GetGroupDS());
10902 int idgi; // --- group of SMDS faces internal (several shapes)
10903 string grpiName = groupName;
10904 grpiName += "_internalFaces";
10905 SMESH_Group *grpi = this->myMesh->AddGroup(SMDSAbs_Face, grpiName.c_str(), idgi);
10908 MESSAGE("group not created " << grpiName);
10911 SMESHDS_Group *sgrpi = dynamic_cast<SMESHDS_Group*>(grpi->GetGroupDS());
10913 int idgei; // --- group of SMDS faces internal (several shapes)
10914 string grpeiName = groupName;
10915 grpeiName += "_internalEdges";
10916 SMESH_Group *grpei = this->myMesh->AddGroup(SMDSAbs_Edge, grpeiName.c_str(), idgei);
10919 MESSAGE("group not created " << grpeiName);
10922 SMESHDS_Group *sgrpei = dynamic_cast<SMESHDS_Group*>(grpei->GetGroupDS());
10924 // --- build downward connectivity
10926 SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS();
10927 meshDS->BuildDownWardConnectivity(true);
10928 SMDS_UnstructuredGrid* grid = meshDS->getGrid();
10930 // --- set of volumes detected inside
10932 std::set<int> setOfInsideVol;
10933 std::set<int> setOfVolToCheck;
10935 std::vector<gp_Pnt> gpnts;
10938 if (isNodeGroup) // --- a group of nodes is provided : find all the volumes using one or more of this nodes
10940 MESSAGE("group of nodes provided");
10941 SMDS_ElemIteratorPtr elemIt = groupDS->GetElements();
10942 while ( elemIt->more() )
10944 const SMDS_MeshElement* elem = elemIt->next();
10947 const SMDS_MeshNode* node = dynamic_cast<const SMDS_MeshNode*>(elem);
10950 SMDS_MeshElement* vol = 0;
10951 SMDS_ElemIteratorPtr volItr = node->GetInverseElementIterator(SMDSAbs_Volume);
10952 while (volItr->more())
10954 vol = (SMDS_MeshElement*)volItr->next();
10955 setOfInsideVol.insert(vol->getVtkId());
10956 sgrp->Add(vol->GetID());
10960 else if (isNodeCoords)
10962 MESSAGE("list of nodes coordinates provided");
10965 while (i < nodesCoords.size()-2)
10967 double x = nodesCoords[i++];
10968 double y = nodesCoords[i++];
10969 double z = nodesCoords[i++];
10970 gp_Pnt p = gp_Pnt(x, y ,z);
10971 gpnts.push_back(p);
10972 MESSAGE("TopoDS_Vertex " << k << " " << p.X() << " " << p.Y() << " " << p.Z());
10976 else // --- no group, no coordinates : use the vertices of the geom shape provided, and radius
10978 MESSAGE("no group of nodes provided, using vertices from geom shape, and radius");
10979 TopTools_IndexedMapOfShape vertexMap;
10980 TopExp::MapShapes( theShape, TopAbs_VERTEX, vertexMap );
10981 gp_Pnt p = gp_Pnt(0,0,0);
10982 if (vertexMap.Extent() < 1)
10985 for ( int i = 1; i <= vertexMap.Extent(); ++i )
10987 const TopoDS_Vertex& vertex = TopoDS::Vertex( vertexMap( i ));
10988 p = BRep_Tool::Pnt(vertex);
10989 gpnts.push_back(p);
10990 MESSAGE("TopoDS_Vertex " << i << " " << p.X() << " " << p.Y() << " " << p.Z());
10994 if (gpnts.size() > 0)
10997 const SMDS_MeshNode* startNode = theNodeSearcher->FindClosestTo(gpnts[0]);
10999 nodeId = startNode->GetID();
11000 MESSAGE("nodeId " << nodeId);
11002 double radius2 = radius*radius;
11003 MESSAGE("radius2 " << radius2);
11005 // --- volumes on start node
11007 setOfVolToCheck.clear();
11008 SMDS_MeshElement* startVol = 0;
11009 SMDS_ElemIteratorPtr volItr = startNode->GetInverseElementIterator(SMDSAbs_Volume);
11010 while (volItr->more())
11012 startVol = (SMDS_MeshElement*)volItr->next();
11013 setOfVolToCheck.insert(startVol->getVtkId());
11015 if (setOfVolToCheck.empty())
11017 MESSAGE("No volumes found");
11021 // --- starting with central volumes then their neighbors, check if they are inside
11022 // or outside the domain, until no more new neighbor volume is inside.
11023 // Fill the group of inside volumes
11025 std::map<int, double> mapOfNodeDistance2;
11026 mapOfNodeDistance2.clear();
11027 std::set<int> setOfOutsideVol;
11028 while (!setOfVolToCheck.empty())
11030 std::set<int>::iterator it = setOfVolToCheck.begin();
11032 MESSAGE("volume to check, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
11033 bool volInside = false;
11034 vtkIdType npts = 0;
11035 vtkIdType* pts = 0;
11036 grid->GetCellPoints(vtkId, npts, pts);
11037 for (int i=0; i<npts; i++)
11039 double distance2 = 0;
11040 if (mapOfNodeDistance2.count(pts[i]))
11042 distance2 = mapOfNodeDistance2[pts[i]];
11043 MESSAGE("point " << pts[i] << " distance2 " << distance2);
11047 double *coords = grid->GetPoint(pts[i]);
11048 gp_Pnt aPoint = gp_Pnt(coords[0], coords[1], coords[2]);
11050 for (int j=0; j<gpnts.size(); j++)
11052 double d2 = aPoint.SquareDistance(gpnts[j]);
11053 if (d2 < distance2)
11056 if (distance2 < radius2)
11060 mapOfNodeDistance2[pts[i]] = distance2;
11061 MESSAGE(" point " << pts[i] << " distance2 " << distance2 << " coords " << coords[0] << " " << coords[1] << " " << coords[2]);
11063 if (distance2 < radius2)
11065 volInside = true; // one or more nodes inside the domain
11066 sgrp->Add(meshDS->fromVtkToSmds(vtkId));
11072 setOfInsideVol.insert(vtkId);
11073 MESSAGE(" volume inside, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
11074 int neighborsVtkIds[NBMAXNEIGHBORS];
11075 int downIds[NBMAXNEIGHBORS];
11076 unsigned char downTypes[NBMAXNEIGHBORS];
11077 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
11078 for (int n = 0; n < nbNeighbors; n++)
11079 if (!setOfInsideVol.count(neighborsVtkIds[n]) ||setOfOutsideVol.count(neighborsVtkIds[n]))
11080 setOfVolToCheck.insert(neighborsVtkIds[n]);
11084 setOfOutsideVol.insert(vtkId);
11085 MESSAGE(" volume outside, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
11087 setOfVolToCheck.erase(vtkId);
11091 // --- for outside hexahedrons, check if they have more than one neighbor volume inside
11092 // If yes, add the volume to the inside set
11094 bool addedInside = true;
11095 std::set<int> setOfVolToReCheck;
11096 while (addedInside)
11098 MESSAGE(" --------------------------- re check");
11099 addedInside = false;
11100 std::set<int>::iterator itv = setOfInsideVol.begin();
11101 for (; itv != setOfInsideVol.end(); ++itv)
11104 int neighborsVtkIds[NBMAXNEIGHBORS];
11105 int downIds[NBMAXNEIGHBORS];
11106 unsigned char downTypes[NBMAXNEIGHBORS];
11107 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
11108 for (int n = 0; n < nbNeighbors; n++)
11109 if (!setOfInsideVol.count(neighborsVtkIds[n]))
11110 setOfVolToReCheck.insert(neighborsVtkIds[n]);
11112 setOfVolToCheck = setOfVolToReCheck;
11113 setOfVolToReCheck.clear();
11114 while (!setOfVolToCheck.empty())
11116 std::set<int>::iterator it = setOfVolToCheck.begin();
11118 if (grid->GetCellType(vtkId) == VTK_HEXAHEDRON)
11120 MESSAGE("volume to recheck, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
11121 int countInside = 0;
11122 int neighborsVtkIds[NBMAXNEIGHBORS];
11123 int downIds[NBMAXNEIGHBORS];
11124 unsigned char downTypes[NBMAXNEIGHBORS];
11125 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
11126 for (int n = 0; n < nbNeighbors; n++)
11127 if (setOfInsideVol.count(neighborsVtkIds[n]))
11129 MESSAGE("countInside " << countInside);
11130 if (countInside > 1)
11132 MESSAGE(" volume inside, vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
11133 setOfInsideVol.insert(vtkId);
11134 sgrp->Add(meshDS->fromVtkToSmds(vtkId));
11135 addedInside = true;
11138 setOfVolToReCheck.insert(vtkId);
11140 setOfVolToCheck.erase(vtkId);
11144 // --- map of Downward faces at the boundary, inside the global volume
11145 // map of Downward faces on the skin of the global volume (equivalent to SMDS faces on the skin)
11146 // fill group of SMDS faces inside the volume (when several volume shapes)
11147 // fill group of SMDS faces on the skin of the global volume (if skin)
11149 std::map<DownIdType, int, DownIdCompare> boundaryFaces; // boundary faces inside the volume --> corresponding cell
11150 std::map<DownIdType, int, DownIdCompare> skinFaces; // faces on the skin of the global volume --> corresponding cell
11151 std::set<int>::iterator it = setOfInsideVol.begin();
11152 for (; it != setOfInsideVol.end(); ++it)
11155 //MESSAGE(" vtkId " << vtkId << " smdsId " << meshDS->fromVtkToSmds(vtkId));
11156 int neighborsVtkIds[NBMAXNEIGHBORS];
11157 int downIds[NBMAXNEIGHBORS];
11158 unsigned char downTypes[NBMAXNEIGHBORS];
11159 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId, true);
11160 for (int n = 0; n < nbNeighbors; n++)
11162 int neighborDim = SMDS_Downward::getCellDimension(grid->GetCellType(neighborsVtkIds[n]));
11163 if (neighborDim == 3)
11165 if (! setOfInsideVol.count(neighborsVtkIds[n])) // neighbor volume is not inside : face is boundary
11167 DownIdType face(downIds[n], downTypes[n]);
11168 boundaryFaces[face] = vtkId;
11170 // if the face between to volumes is in the mesh, get it (internal face between shapes)
11171 int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
11172 if (vtkFaceId >= 0)
11174 sgrpi->Add(meshDS->fromVtkToSmds(vtkFaceId));
11175 // find also the smds edges on this face
11176 int nbEdges = grid->getDownArray(downTypes[n])->getNumberOfDownCells(downIds[n]);
11177 const int* dEdges = grid->getDownArray(downTypes[n])->getDownCells(downIds[n]);
11178 const unsigned char* dTypes = grid->getDownArray(downTypes[n])->getDownTypes(downIds[n]);
11179 for (int i = 0; i < nbEdges; i++)
11181 int vtkEdgeId = grid->getDownArray(dTypes[i])->getVtkCellId(dEdges[i]);
11182 if (vtkEdgeId >= 0)
11183 sgrpei->Add(meshDS->fromVtkToSmds(vtkEdgeId));
11187 else if (neighborDim == 2) // skin of the volume
11189 DownIdType face(downIds[n], downTypes[n]);
11190 skinFaces[face] = vtkId;
11191 int vtkFaceId = grid->getDownArray(downTypes[n])->getVtkCellId(downIds[n]);
11192 if (vtkFaceId >= 0)
11193 sgrps->Add(meshDS->fromVtkToSmds(vtkFaceId));
11198 // --- identify the edges constituting the wire of each subshape on the skin
11199 // define polylines with the nodes of edges, equivalent to wires
11200 // project polylines on subshapes, and partition, to get geom faces
11202 std::map<int, std::set<int> > shapeIdToVtkIdSet; // shapeId --> set of vtkId on skin
11203 std::set<int> emptySet;
11205 std::set<int> shapeIds;
11207 SMDS_ElemIteratorPtr itelem = sgrps->GetElements();
11208 while (itelem->more())
11210 const SMDS_MeshElement *elem = itelem->next();
11211 int shapeId = elem->getshapeId();
11212 int vtkId = elem->getVtkId();
11213 if (!shapeIdToVtkIdSet.count(shapeId))
11215 shapeIdToVtkIdSet[shapeId] = emptySet;
11216 shapeIds.insert(shapeId);
11218 shapeIdToVtkIdSet[shapeId].insert(vtkId);
11221 std::map<int, std::set<DownIdType, DownIdCompare> > shapeIdToEdges; // shapeId --> set of downward edges
11222 std::set<DownIdType, DownIdCompare> emptyEdges;
11223 emptyEdges.clear();
11225 std::map<int, std::set<int> >::iterator itShape = shapeIdToVtkIdSet.begin();
11226 for (; itShape != shapeIdToVtkIdSet.end(); ++itShape)
11228 int shapeId = itShape->first;
11229 MESSAGE(" --- Shape ID --- "<< shapeId);
11230 shapeIdToEdges[shapeId] = emptyEdges;
11232 std::vector<int> nodesEdges;
11234 std::set<int>::iterator its = itShape->second.begin();
11235 for (; its != itShape->second.end(); ++its)
11238 MESSAGE(" " << vtkId);
11239 int neighborsVtkIds[NBMAXNEIGHBORS];
11240 int downIds[NBMAXNEIGHBORS];
11241 unsigned char downTypes[NBMAXNEIGHBORS];
11242 int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId);
11243 for (int n = 0; n < nbNeighbors; n++)
11245 if (neighborsVtkIds[n]<0) // only smds faces are considered as neighbors here
11247 int smdsId = meshDS->fromVtkToSmds(neighborsVtkIds[n]);
11248 const SMDS_MeshElement* elem = meshDS->FindElement(smdsId);
11249 if ( shapeIds.count(elem->getshapeId()) && !sgrps->Contains(elem)) // edge : neighbor in the set of shape, not in the group
11251 DownIdType edge(downIds[n], downTypes[n]);
11252 if (!shapeIdToEdges[shapeId].count(edge))
11254 shapeIdToEdges[shapeId].insert(edge);
11256 int nbNodes = grid->getDownArray(downTypes[n])->getNodes(downIds[n],vtkNodeId);
11257 nodesEdges.push_back(vtkNodeId[0]);
11258 nodesEdges.push_back(vtkNodeId[nbNodes-1]);
11259 MESSAGE(" --- nodes " << vtkNodeId[0]+1 << " " << vtkNodeId[nbNodes-1]+1);
11265 std::list<int> order;
11267 if (nodesEdges.size() > 0)
11269 order.push_back(nodesEdges[0]); MESSAGE(" --- back " << order.back()+1); // SMDS id = VTK id + 1;
11270 nodesEdges[0] = -1;
11271 order.push_back(nodesEdges[1]); MESSAGE(" --- back " << order.back()+1);
11272 nodesEdges[1] = -1; // do not reuse this edge
11276 int nodeTofind = order.back(); // try first to push back
11278 for (i = 0; i<nodesEdges.size(); i++)
11279 if (nodesEdges[i] == nodeTofind)
11281 if (i == nodesEdges.size())
11282 found = false; // no follower found on back
11285 if (i%2) // odd ==> use the previous one
11286 if (nodesEdges[i-1] < 0)
11290 order.push_back(nodesEdges[i-1]); MESSAGE(" --- back " << order.back()+1);
11291 nodesEdges[i-1] = -1;
11293 else // even ==> use the next one
11294 if (nodesEdges[i+1] < 0)
11298 order.push_back(nodesEdges[i+1]); MESSAGE(" --- back " << order.back()+1);
11299 nodesEdges[i+1] = -1;
11304 // try to push front
11306 nodeTofind = order.front(); // try to push front
11307 for (i = 0; i<nodesEdges.size(); i++)
11308 if (nodesEdges[i] == nodeTofind)
11310 if (i == nodesEdges.size())
11312 found = false; // no predecessor found on front
11315 if (i%2) // odd ==> use the previous one
11316 if (nodesEdges[i-1] < 0)
11320 order.push_front(nodesEdges[i-1]); MESSAGE(" --- front " << order.front()+1);
11321 nodesEdges[i-1] = -1;
11323 else // even ==> use the next one
11324 if (nodesEdges[i+1] < 0)
11328 order.push_front(nodesEdges[i+1]); MESSAGE(" --- front " << order.front()+1);
11329 nodesEdges[i+1] = -1;
11335 std::vector<int> nodes;
11336 nodes.push_back(shapeId);
11337 std::list<int>::iterator itl = order.begin();
11338 for (; itl != order.end(); itl++)
11340 nodes.push_back((*itl) + 1); // SMDS id = VTK id + 1;
11341 MESSAGE(" ordered node " << nodes[nodes.size()-1]);
11343 listOfListOfNodes.push_back(nodes);
11346 // partition geom faces with blocFissure
11347 // mesh blocFissure and geom faces of the skin (external wires given, triangle algo to choose)
11348 // mesh volume around blocFissure (skin triangles and quadrangle given, tetra algo to choose)
11354 //================================================================================
11356 * \brief Generates skin mesh (containing 2D cells) from 3D mesh
11357 * The created 2D mesh elements based on nodes of free faces of boundary volumes
11358 * \return TRUE if operation has been completed successfully, FALSE otherwise
11360 //================================================================================
11362 bool SMESH_MeshEditor::Make2DMeshFrom3D()
11364 // iterates on volume elements and detect all free faces on them
11365 SMESHDS_Mesh* aMesh = GetMeshDS();
11368 //bool res = false;
11369 int nbFree = 0, nbExisted = 0, nbCreated = 0;
11370 SMDS_VolumeIteratorPtr vIt = aMesh->volumesIterator();
11373 const SMDS_MeshVolume* volume = vIt->next();
11374 SMDS_VolumeTool vTool( volume, /*ignoreCentralNodes=*/false );
11375 vTool.SetExternalNormal();
11376 //const bool isPoly = volume->IsPoly();
11377 const int iQuad = volume->IsQuadratic();
11378 for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
11380 if (!vTool.IsFreeFace(iface))
11383 vector<const SMDS_MeshNode *> nodes;
11384 int nbFaceNodes = vTool.NbFaceNodes(iface);
11385 const SMDS_MeshNode** faceNodes = vTool.GetFaceNodes(iface);
11387 for ( ; inode < nbFaceNodes; inode += iQuad+1)
11388 nodes.push_back(faceNodes[inode]);
11389 if (iQuad) { // add medium nodes
11390 for ( inode = 1; inode < nbFaceNodes; inode += 2)
11391 nodes.push_back(faceNodes[inode]);
11392 if ( nbFaceNodes == 9 ) // bi-quadratic quad
11393 nodes.push_back(faceNodes[8]);
11395 // add new face based on volume nodes
11396 if (aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false) ) {
11398 continue; // face already exsist
11400 AddElement(nodes, SMDSAbs_Face, ( !iQuad && nbFaceNodes/(iQuad+1) > 4 ));
11404 return ( nbFree==(nbExisted+nbCreated) );
11409 inline const SMDS_MeshNode* getNodeWithSameID(SMESHDS_Mesh* mesh, const SMDS_MeshNode* node)
11411 if ( const SMDS_MeshNode* n = mesh->FindNode( node->GetID() ))
11413 return mesh->AddNodeWithID( node->X(),node->Y(),node->Z(), node->GetID() );
11416 //================================================================================
11418 * \brief Creates missing boundary elements
11419 * \param elements - elements whose boundary is to be checked
11420 * \param dimension - defines type of boundary elements to create
11421 * \param group - a group to store created boundary elements in
11422 * \param targetMesh - a mesh to store created boundary elements in
11423 * \param toCopyElements - if true, the checked elements will be copied into the targetMesh
11424 * \param toCopyExistingBoundary - if true, not only new but also pre-existing
11425 * boundary elements will be copied into the targetMesh
11426 * \param toAddExistingBondary - if true, not only new but also pre-existing
11427 * boundary elements will be added into the new group
11428 * \param aroundElements - if true, elements will be created on boundary of given
11429 * elements else, on boundary of the whole mesh.
11430 * \return nb of added boundary elements
11432 //================================================================================
11434 int SMESH_MeshEditor::MakeBoundaryMesh(const TIDSortedElemSet& elements,
11435 Bnd_Dimension dimension,
11436 SMESH_Group* group/*=0*/,
11437 SMESH_Mesh* targetMesh/*=0*/,
11438 bool toCopyElements/*=false*/,
11439 bool toCopyExistingBoundary/*=false*/,
11440 bool toAddExistingBondary/*= false*/,
11441 bool aroundElements/*= false*/)
11443 SMDSAbs_ElementType missType = (dimension == BND_2DFROM3D) ? SMDSAbs_Face : SMDSAbs_Edge;
11444 SMDSAbs_ElementType elemType = (dimension == BND_1DFROM2D) ? SMDSAbs_Face : SMDSAbs_Volume;
11445 // hope that all elements are of the same type, do not check them all
11446 if ( !elements.empty() && (*elements.begin())->GetType() != elemType )
11447 throw SALOME_Exception(LOCALIZED("wrong element type"));
11450 toCopyElements = toCopyExistingBoundary = false;
11452 SMESH_MeshEditor tgtEditor( targetMesh ? targetMesh : myMesh );
11453 SMESHDS_Mesh* aMesh = GetMeshDS(), *tgtMeshDS = tgtEditor.GetMeshDS();
11454 int nbAddedBnd = 0;
11456 // editor adding present bnd elements and optionally holding elements to add to the group
11457 SMESH_MeshEditor* presentEditor;
11458 SMESH_MeshEditor tgtEditor2( tgtEditor.GetMesh() );
11459 presentEditor = toAddExistingBondary ? &tgtEditor : &tgtEditor2;
11461 SMESH_MesherHelper helper( *myMesh );
11462 const TopAbs_ShapeEnum missShapeType = ( missType==SMDSAbs_Face ? TopAbs_FACE : TopAbs_EDGE );
11463 SMDS_VolumeTool vTool;
11464 TIDSortedElemSet avoidSet;
11465 const TIDSortedElemSet emptySet, *elemSet = aroundElements ? &elements : &emptySet;
11468 typedef vector<const SMDS_MeshNode*> TConnectivity;
11470 SMDS_ElemIteratorPtr eIt;
11471 if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
11472 else eIt = elemSetIterator( elements );
11474 while (eIt->more())
11476 const SMDS_MeshElement* elem = eIt->next();
11477 const int iQuad = elem->IsQuadratic();
11479 // ------------------------------------------------------------------------------------
11480 // 1. For an elem, get present bnd elements and connectivities of missing bnd elements
11481 // ------------------------------------------------------------------------------------
11482 vector<const SMDS_MeshElement*> presentBndElems;
11483 vector<TConnectivity> missingBndElems;
11484 TConnectivity nodes, elemNodes;
11485 if ( vTool.Set(elem, /*ignoreCentralNodes=*/true) ) // elem is a volume --------------
11487 vTool.SetExternalNormal();
11488 const SMDS_MeshElement* otherVol = 0;
11489 for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ )
11491 if ( !vTool.IsFreeFace(iface, &otherVol) &&
11492 ( !aroundElements || elements.count( otherVol )))
11494 const SMDS_MeshNode** nn = vTool.GetFaceNodes(iface);
11495 const int nbFaceNodes = vTool.NbFaceNodes (iface);
11496 if ( missType == SMDSAbs_Edge ) // boundary edges
11498 nodes.resize( 2+iQuad );
11499 for ( int i = 0; i < nbFaceNodes; i += 1+iQuad)
11501 for ( int j = 0; j < nodes.size(); ++j )
11503 if ( const SMDS_MeshElement* edge =
11504 aMesh->FindElement(nodes,SMDSAbs_Edge,/*noMedium=*/false))
11505 presentBndElems.push_back( edge );
11507 missingBndElems.push_back( nodes );
11510 else // boundary face
11513 for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
11514 nodes.push_back( nn[inode] ); // add corner nodes
11516 for ( inode = 1; inode < nbFaceNodes; inode += 2)
11517 nodes.push_back( nn[inode] ); // add medium nodes
11518 int iCenter = vTool.GetCenterNodeIndex(iface); // for HEX27
11520 nodes.push_back( vTool.GetNodes()[ iCenter ] );
11522 if (const SMDS_MeshElement * f = aMesh->FindElement( nodes,
11523 SMDSAbs_Face, /*noMedium=*/false ))
11524 presentBndElems.push_back( f );
11526 missingBndElems.push_back( nodes );
11528 if ( targetMesh != myMesh )
11530 // add 1D elements on face boundary to be added to a new mesh
11531 const SMDS_MeshElement* edge;
11532 for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad)
11535 edge = aMesh->FindEdge( nn[inode], nn[inode+1], nn[inode+2]);
11537 edge = aMesh->FindEdge( nn[inode], nn[inode+1]);
11538 if ( edge && avoidSet.insert( edge ).second )
11539 presentBndElems.push_back( edge );
11545 else if ( elem->GetType() == SMDSAbs_Face ) // elem is a face ------------------------
11547 avoidSet.clear(), avoidSet.insert( elem );
11548 elemNodes.assign( SMDS_MeshElement::iterator( elem->interlacedNodesElemIterator() ),
11549 SMDS_MeshElement::iterator() );
11550 elemNodes.push_back( elemNodes[0] );
11551 nodes.resize( 2 + iQuad );
11552 const int nbLinks = elem->NbCornerNodes();
11553 for ( int i = 0, iN = 0; i < nbLinks; i++, iN += 1+iQuad )
11555 nodes[0] = elemNodes[iN];
11556 nodes[1] = elemNodes[iN+1+iQuad];
11557 if ( SMESH_MeshAlgos::FindFaceInSet( nodes[0], nodes[1], *elemSet, avoidSet))
11558 continue; // not free link
11560 if ( iQuad ) nodes[2] = elemNodes[iN+1];
11561 if ( const SMDS_MeshElement* edge =
11562 aMesh->FindElement(nodes,SMDSAbs_Edge,/*noMedium=*/false))
11563 presentBndElems.push_back( edge );
11565 missingBndElems.push_back( nodes );
11569 // ---------------------------------
11570 // 2. Add missing boundary elements
11571 // ---------------------------------
11572 if ( targetMesh != myMesh )
11573 // instead of making a map of nodes in this mesh and targetMesh,
11574 // we create nodes with same IDs.
11575 for ( int i = 0; i < missingBndElems.size(); ++i )
11577 TConnectivity& srcNodes = missingBndElems[i];
11578 TConnectivity nodes( srcNodes.size() );
11579 for ( inode = 0; inode < nodes.size(); ++inode )
11580 nodes[inode] = getNodeWithSameID( tgtMeshDS, srcNodes[inode] );
11581 if ( aroundElements && tgtEditor.GetMeshDS()->FindElement( nodes,
11583 /*noMedium=*/false))
11585 tgtEditor.AddElement(nodes, missType, !iQuad && nodes.size()/(iQuad+1)>4);
11589 for ( int i = 0; i < missingBndElems.size(); ++i )
11591 TConnectivity& nodes = missingBndElems[i];
11592 if ( aroundElements && tgtEditor.GetMeshDS()->FindElement( nodes,
11594 /*noMedium=*/false))
11596 SMDS_MeshElement* elem =
11597 tgtEditor.AddElement(nodes, missType, !iQuad && nodes.size()/(iQuad+1)>4);
11600 // try to set a new element to a shape
11601 if ( myMesh->HasShapeToMesh() )
11604 set< pair<TopAbs_ShapeEnum, int > > mediumShapes;
11605 const int nbN = nodes.size() / (iQuad+1 );
11606 for ( inode = 0; inode < nbN && ok; ++inode )
11608 pair<int, TopAbs_ShapeEnum> i_stype =
11609 helper.GetMediumPos( nodes[inode], nodes[(inode+1)%nbN]);
11610 if (( ok = ( i_stype.first > 0 && i_stype.second >= TopAbs_FACE )))
11611 mediumShapes.insert( make_pair ( i_stype.second, i_stype.first ));
11613 if ( ok && mediumShapes.size() > 1 )
11615 set< pair<TopAbs_ShapeEnum, int > >::iterator stype_i = mediumShapes.begin();
11616 pair<TopAbs_ShapeEnum, int> stype_i_0 = *stype_i;
11617 for ( ++stype_i; stype_i != mediumShapes.end() && ok; ++stype_i )
11619 if (( ok = ( stype_i->first != stype_i_0.first )))
11620 ok = helper.IsSubShape( aMesh->IndexToShape( stype_i->second ),
11621 aMesh->IndexToShape( stype_i_0.second ));
11624 if ( ok && mediumShapes.begin()->first == missShapeType )
11625 aMesh->SetMeshElementOnShape( elem, mediumShapes.begin()->second );
11629 // ----------------------------------
11630 // 3. Copy present boundary elements
11631 // ----------------------------------
11632 if ( toCopyExistingBoundary )
11633 for ( int i = 0 ; i < presentBndElems.size(); ++i )
11635 const SMDS_MeshElement* e = presentBndElems[i];
11636 TConnectivity nodes( e->NbNodes() );
11637 for ( inode = 0; inode < nodes.size(); ++inode )
11638 nodes[inode] = getNodeWithSameID( tgtMeshDS, e->GetNode(inode) );
11639 presentEditor->AddElement(nodes, e->GetType(), e->IsPoly());
11641 else // store present elements to add them to a group
11642 for ( int i = 0 ; i < presentBndElems.size(); ++i )
11644 presentEditor->myLastCreatedElems.Append(presentBndElems[i]);
11647 } // loop on given elements
11649 // ---------------------------------------------
11650 // 4. Fill group with boundary elements
11651 // ---------------------------------------------
11654 if ( SMESHDS_Group* g = dynamic_cast<SMESHDS_Group*>( group->GetGroupDS() ))
11655 for ( int i = 0; i < tgtEditor.myLastCreatedElems.Size(); ++i )
11656 g->SMDSGroup().Add( tgtEditor.myLastCreatedElems( i+1 ));
11658 tgtEditor.myLastCreatedElems.Clear();
11659 tgtEditor2.myLastCreatedElems.Clear();
11661 // -----------------------
11662 // 5. Copy given elements
11663 // -----------------------
11664 if ( toCopyElements && targetMesh != myMesh )
11666 if (elements.empty()) eIt = aMesh->elementsIterator(elemType);
11667 else eIt = elemSetIterator( elements );
11668 while (eIt->more())
11670 const SMDS_MeshElement* elem = eIt->next();
11671 TConnectivity nodes( elem->NbNodes() );
11672 for ( inode = 0; inode < nodes.size(); ++inode )
11673 nodes[inode] = getNodeWithSameID( tgtMeshDS, elem->GetNode(inode) );
11674 tgtEditor.AddElement(nodes, elemType, elem->IsPoly());
11676 tgtEditor.myLastCreatedElems.Clear();